/* * __ .__ .__ ._____. * _/ |_ _______ __|__| ____ | | |__\_ |__ ______ * \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/ * | | ( <_> > <| \ \___| |_| || \_\ \\___ \ * |__| \____/__/\_ \__|\___ >____/__||___ /____ > * \/ \/ \/ \/ * * 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.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import toxi.geom.AABB; import toxi.geom.Line3D; import toxi.geom.Vec3D; import toxi.geom.mesh.Mesh3D; import toxi.geom.mesh.WETriangleMesh; import toxi.math.ScaleMap; import toxi.util.datatypes.FloatRange; import toxi.util.datatypes.IntegerRange; /** * * @author tux */ public class MeshLatticeBuilder { /** * */ protected static final Logger logger = Logger .getLogger(MeshLatticeBuilder.class.getName()); /** * * @param mesh * @param res * @param stroke * @return */ public static WETriangleMesh build(WETriangleMesh mesh, int res, float stroke) { return build(mesh, res, new FloatRange(stroke, stroke)); } /** * * @param mesh * @param res * @param stroke * @return */ public static WETriangleMesh build(WETriangleMesh mesh, int res, FloatRange stroke) { VolumetricSpace volume = buildVolume(mesh, res, stroke); IsoSurface surface = new HashIsoSurface(volume); mesh = (WETriangleMesh) surface.computeSurfaceMesh(new WETriangleMesh( "iso", 300000, 900000), 0.2f); logger.log(Level.INFO, "created lattice mesh: {0}", mesh); return mesh; } /** * * @param mesh * @param res * @param stroke * @return */ public static VolumetricSpace buildVolume(WETriangleMesh mesh, int res, float stroke) { return buildVolume(mesh, res, new FloatRange(stroke, stroke)); } /** * * @param mesh * @param res * @param stroke * @return */ public static VolumetricSpace buildVolume(WETriangleMesh mesh, int res, FloatRange stroke) { MeshLatticeBuilder builder = new MeshLatticeBuilder(mesh .getBoundingBox().getExtent().scale(2), res, res, res, stroke); return builder.buildVolume(mesh); } protected IntegerRange voxRangeX, /** * */ /** * */ voxRangeY, /** * */ /** * */ voxRangeZ; /** * */ protected FloatRange stroke; private VolumetricSpace volume; private float drawStep = 0.5f; private ScaleMap bboxToVoxelX; private ScaleMap bboxToVoxelY; private ScaleMap bboxToVoxelZ; /** * * @param scale * @param res * @param stroke */ public MeshLatticeBuilder(Vec3D scale, int res, float stroke) { this(scale, res, res, res, new FloatRange(stroke, stroke)); } /** * * @param scale * @param resX * @param resY * @param resZ * @param stroke */ public MeshLatticeBuilder(Vec3D scale, int resX, int resY, int resZ, FloatRange stroke) { this.stroke = stroke; this.voxRangeX = new IntegerRange(1, resX - 2); this.voxRangeY = new IntegerRange(1, resY - 2); this.voxRangeZ = new IntegerRange(1, resZ - 2); volume = new VolumetricHashMap(scale, resX, resY, resZ, 0.1f); } /** * * @param mesh * @param targetMesh * @param isoValue * @return */ public WETriangleMesh buildLattice(WETriangleMesh mesh, Mesh3D targetMesh, float isoValue) { if (volume == null) { volume = buildVolume(mesh); } if (targetMesh == null) { targetMesh = new WETriangleMesh(); } IsoSurface surface = new HashIsoSurface(volume); mesh = (WETriangleMesh) surface .computeSurfaceMesh(targetMesh, isoValue); return mesh; } /** * * @param mesh * @return */ public VolumetricSpace buildVolume(WETriangleMesh mesh) { VolumetricBrush brush = new RoundBrush(volume, 1); brush.setMode(VolumetricBrush.MODE_PEAK); return buildVolume(mesh, brush); } /** * * @param mesh * @param brush * @return */ public VolumetricSpace buildVolume(final WETriangleMesh mesh, final VolumetricBrush brush) { logger.info("creating lattice..."); setMesh(mesh); List edgeLengths = new ArrayList<>(mesh.edges.size()); mesh.edges.values().stream().forEach((e) -> { edgeLengths.add(e.getLength()); }); FloatRange range = FloatRange.fromSamples(edgeLengths); ScaleMap brushSize = new ScaleMap(range.min, range.max, stroke.min, stroke.max); mesh.edges.values().stream().map((e) -> { brush.setSize((float) brushSize.getClippedValueFor(e.getLength())); return e; }).forEach((e) -> { createLattice(brush, e, drawStep); }); volume.closeSides(); return volume; } /** * * @param brush * @param l * @param drawStep */ public void createLattice(VolumetricBrush brush, Line3D l, float drawStep) { List points = l.splitIntoSegments(null, drawStep, true); points.stream().forEach((p) -> { float x = (float) bboxToVoxelX.getClippedValueFor(p.x); float y = (float) bboxToVoxelY.getClippedValueFor(p.y); float z = (float) bboxToVoxelZ.getClippedValueFor(p.z); brush.drawAtGridPos(x, y, z, 1); }); } /** * @return the drawStep */ public float getDrawStep() { return drawStep; } /** * @return the volume */ public VolumetricSpace getVolume() { return volume; } /** * Sets the distance between {@link VolumetricBrush} positions when tracing * mesh edges. * * @param drawStep * the drawStep to set */ public void setDrawStepLength(float drawStep) { this.drawStep = drawStep; } /** * * @param box */ public void setInputBounds(AABB box) { Vec3D bmin = box.getMin(); Vec3D bmax = box.getMax(); bboxToVoxelX = new ScaleMap(bmin.x, bmax.x, voxRangeX.min, voxRangeX.max); bboxToVoxelY = new ScaleMap(bmin.y, bmax.y, voxRangeY.min, voxRangeY.max); bboxToVoxelZ = new ScaleMap(bmin.z, bmax.z, voxRangeZ.min, voxRangeZ.max); } /** * * @param mesh */ public void setMesh(WETriangleMesh mesh) { setInputBounds(mesh.getBoundingBox()); } /** * * @param range * @param min * @param max * @param maxRes */ protected void setRangeMinMax(IntegerRange range, int min, int max, int maxRes) { // swap if necessary... if (min > max) { max ^= min; min ^= max; max ^= min; } if (min < 0 || min >= maxRes || max < 0 || max >= maxRes) { throw new IllegalArgumentException( "voxel range min/max is out of bounds: " + min + "->" + max); } range.min = min; range.max = max; } /** * @param volume * the volume to set */ public void setVolume(VolumetricSpace volume) { this.volume = volume; } /** * * @param min * @param max * @return */ public MeshLatticeBuilder setVoxelRangeX(int min, int max) { setRangeMinMax(voxRangeX, min, max, volume.resX); return this; } /** * * @param min * @param max * @return */ public MeshLatticeBuilder setVoxelRangeY(int min, int max) { setRangeMinMax(voxRangeY, min, max, volume.resY); return this; } /** * * @param min * @param max * @return */ public MeshLatticeBuilder setVoxelRangeZ(int min, int max) { setRangeMinMax(voxRangeZ, min, max, volume.resZ); return this; } }