Package toxi.volume

Source Code of toxi.volume.HashIsoSurface

/*
*   __               .__       .__  ._____.          
* _/  |_  _______  __|__| ____ |  | |__\_ |__   ______
* \   __\/  _ \  \/  /  |/ ___\|  | |  || __ \ /  ___/
*  |  | (  <_> >    <|  \  \___|  |_|  || \_\ \\___ \
*  |__|  \____/__/\_ \__|\___  >____/__||___  /____  >
*                   \/       \/             \/     \/
*
* Copyright (c) 2006-2011 Karsten Schmidt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* http://creativecommons.org/licenses/LGPL/2.1/
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/

package toxi.volume;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import toxi.geom.Vec3D;
import toxi.geom.mesh.Mesh3D;
import toxi.geom.mesh.TriangleMesh;

/**
* HashMap based implementation of the IsoSurface interface. More memory
* efficient than {@link ArrayIsoSurface} and so better suited for very
* high-resolution volumes.
*/
public class HashIsoSurface implements IsoSurface {

    protected static final Logger logger = Logger
            .getLogger(HashIsoSurface.class.getName());

    protected Vec3D cellSize;
    protected Vec3D centreOffset;
    protected VolumetricSpace volume;

    public float isoValue;

    protected int resX, resY, resZ;
    protected int resX1, resY1, resZ1;

    protected int sliceRes;
    protected int nextXY;

    protected HashMap<Integer, Vec3D> edgeVertices;
    protected float density;

    protected short[] cellIndexCache, prevCellIndexCache;

    /**
     * Creates a new instance using the expected default vertex density of 50%.
     *
     * @see #setExpectedDensity(float)
     *
     * @param volume
     */
    public HashIsoSurface(VolumetricSpace volume) {
        this(volume, 0.5f);
    }

    /**
     * Creates a new instance using the given expected vertex density.
     *
     * @see #setExpectedDensity(float)
     *
     * @param volume
     */
    public HashIsoSurface(VolumetricSpace volume, float density) {
        this.volume = volume;
        this.density = density;
        cellSize = new Vec3D(volume.scale.x / volume.resX1, volume.scale.y
                / volume.resY1, volume.scale.z / volume.resZ1);

        resX = volume.resX;
        resY = volume.resY;
        resZ = volume.resZ;
        resX1 = volume.resX1;
        resY1 = volume.resY1;
        resZ1 = volume.resZ1;

        sliceRes = volume.sliceRes;
        nextXY = resX + sliceRes;

        cellIndexCache = new short[sliceRes];
        prevCellIndexCache = new short[sliceRes];

        centreOffset = volume.halfScale.getInverted();
        reset();
    }

    public Mesh3D computeSurfaceMesh(Mesh3D mesh, final float iso) {
        if (mesh == null) {
            mesh = new TriangleMesh("isosurface-" + iso);
        } else {
            mesh.clear();
        }
        isoValue = iso;
        float offsetZ = centreOffset.z;
        for (int z = 0; z < resZ1; z++) {
            int sliceOffset = sliceRes * z;
            float offsetY = centreOffset.y;
            for (int y = 0; y < resY1; y++) {
                float offsetX = centreOffset.x;
                int sliceIndex = resX * y;
                int offset = sliceIndex + sliceOffset;
                for (int x = 0; x < resX1; x++) {
                    final int cellIndex = getCellIndex(x, y, z);
                    cellIndexCache[sliceIndex + x] = (short) cellIndex;
                    if (cellIndex > 0 && cellIndex < 255) {
                        final int edgeFlags = MarchingCubesIndex.edgesToCompute[cellIndex];
                        if (edgeFlags > 0 && edgeFlags < 255) {
                            int edgeOffsetIndex = offset * 3;
                            float offsetData = volume.getVoxelAt(offset);
                            float isoDiff = isoValue - offsetData;
                            if ((edgeFlags & 1) > 0) {
                                float t = isoDiff
                                        / (volume.getVoxelAt(offset + 1) - offsetData);
                                edgeVertices.put(edgeOffsetIndex, new Vec3D(
                                        offsetX + t * cellSize.x, y
                                                * cellSize.y + centreOffset.y,
                                        z * cellSize.z + centreOffset.z));
                            }
                            if ((edgeFlags & 2) > 0) {
                                float t = isoDiff
                                        / (volume.getVoxelAt(offset + resX) - offsetData);
                                edgeVertices.put(edgeOffsetIndex + 1,
                                        new Vec3D(x * cellSize.x
                                                + centreOffset.x, offsetY + t
                                                * cellSize.y, z * cellSize.z
                                                + centreOffset.z));
                            }
                            if ((edgeFlags & 4) > 0) {
                                float t = isoDiff
                                        / (volume.getVoxelAt(offset + sliceRes) - offsetData);
                                edgeVertices.put(edgeOffsetIndex + 2,
                                        new Vec3D(x * cellSize.x
                                                + centreOffset.x, y
                                                * cellSize.y + centreOffset.y,
                                                offsetZ + t * cellSize.z));
                            }
                        }
                    }
                    offsetX += cellSize.x;
                    offset++;
                }
                offsetY += cellSize.y;
            }
            if (z > 0) {
                createFacesForSlice(mesh, z - 1);
            }
            short[] tmp = prevCellIndexCache;
            prevCellIndexCache = cellIndexCache;
            cellIndexCache = tmp;
            offsetZ += cellSize.z;
        }
        createFacesForSlice(mesh, resZ1 - 1);
        return mesh;
    }

    private void createFacesForSlice(Mesh3D mesh, int z) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("creating polygons for slice: " + z);
        }
        int[] face = new int[16];
        int sliceOffset = sliceRes * z;
        for (int y = 0; y < resY1; y++) {
            int offset = resX * y;
            for (int x = 0; x < resX1; x++) {
                final int cellIndex = prevCellIndexCache[offset];
                if (cellIndex > 0 && cellIndex < 255) {
                    int n = 0;
                    int edgeIndex;
                    int[] cellTriangles = MarchingCubesIndex.cellTriangles[cellIndex];
                    while ((edgeIndex = cellTriangles[n]) != -1) {
                        int[] edgeOffsetInfo = MarchingCubesIndex.edgeOffsets[edgeIndex];
                        face[n] = ((x + edgeOffsetInfo[0]) + resX
                                * (y + edgeOffsetInfo[1]) + sliceRes
                                * (z + edgeOffsetInfo[2]))
                                * 3 + edgeOffsetInfo[3];
                        n++;
                    }
                    for (int i = 0; i < n; i += 3) {
                        final Vec3D va = edgeVertices.get(face[i + 1]);
                        if (va != null) {
                            final Vec3D vb = edgeVertices.get(face[i + 2]);
                            if (vb != null) {
                                final Vec3D vc = edgeVertices.get(face[i]);
                                if (vc != null) {
                                    mesh.addFace(va, vb, vc);
                                }
                            }
                        }
                    }
                }
                offset++;
            }
        }
        int minIndex = sliceOffset * 3;
        for (Iterator<Entry<Integer, Vec3D>> i = edgeVertices.entrySet()
                .iterator(); i.hasNext();) {
            if (i.next().getKey() < minIndex) {
                i.remove();
            }
        }
    }

    protected final int getCellIndex(int x, int y, int z) {
        int cellIndex = 0;
        int idx = x + y * resX + z * sliceRes;
        if (volume.getVoxelAt(idx) < isoValue) {
            cellIndex |= 0x01;
        }
        if (volume.getVoxelAt(idx + sliceRes) < isoValue) {
            cellIndex |= 0x08;
        }
        if (volume.getVoxelAt(idx + resX) < isoValue) {
            cellIndex |= 0x10;
        }
        if (volume.getVoxelAt(idx + resX + sliceRes) < isoValue) {
            cellIndex |= 0x80;
        }
        idx++;
        if (volume.getVoxelAt(idx) < isoValue) {
            cellIndex |= 0x02;
        }
        if (volume.getVoxelAt(idx + sliceRes) < isoValue) {
            cellIndex |= 0x04;
        }
        if (volume.getVoxelAt(idx + resX) < isoValue) {
            cellIndex |= 0x20;
        }
        if (volume.getVoxelAt(idx + resX + sliceRes) < isoValue) {
            cellIndex |= 0x40;
        }
        return cellIndex;
    }

    /**
     * Resets mesh vertices to default positions and clears face index. Needs to
     * be called inbetween successive calls to
     * {@link #computeSurfaceMesh(Mesh3D, float)}.
     */
    public void reset() {
        edgeVertices = new HashMap<Integer, Vec3D>(
                (int) (density * volume.numCells));
    }

    public void setExpectedDensity(float density) {
        this.density = density;
    }
}
TOP

Related Classes of toxi.volume.HashIsoSurface

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.