/*
* Copyright (c) 2010, Frederik Vanhoutte 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 wblut.hemesh;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastTable;
import wblut.WB_Epsilon;
import wblut.core.WB_HasData;
import wblut.geom.WB_AABB3D;
import wblut.geom.WB_ClassifyPointToPlane;
import wblut.geom.WB_Distance;
import wblut.geom.WB_ExplicitPolygon;
import wblut.geom.WB_ExplicitSegment;
import wblut.geom.WB_Frame;
import wblut.geom.WB_IndexedSegment;
import wblut.geom.WB_IndexedTriangle;
import wblut.geom.WB_IndexedTriangle2D;
import wblut.geom.WB_Intersection;
import wblut.geom.WB_IntersectionResult;
import wblut.geom.WB_KDNeighbor;
import wblut.geom.WB_KDTree3Dold;
import wblut.geom.WB_Normal3d;
import wblut.geom.WB_Plane;
import wblut.geom.WB_Point3d;
import wblut.geom.WB_PolygonType2D;
import wblut.geom.WB_Ray;
import wblut.geom.WB_Transform;
import wblut.geom.WB_Vector3d;
// TODO: Auto-generated Javadoc
/**
* Half-edge mesh data structure.
*
* @author Frederik Vanhoutte (W:Blut)
*
*/
public class HE_Mesh extends HE_MeshStructure implements WB_HasData {
/** Stored mesh center. */
private WB_Point3d _center;
/** Status of mesh center. */
private boolean _centerUpdated;
/** General purpose label. */
protected int label;
/** The _data. */
private HashMap<String, Object> _data;
/**
* Instantiates a new HE_Mesh.
*
*/
public HE_Mesh() {
super();
_center = new WB_Point3d();
_centerUpdated = false;
label = -1;
}
/**
* Sets the label.
*
* @param lab
* the new label
*/
public void setLabel(final int lab) {
label = lab;
}
/**
* Gets the label.
*
* @return the label
*/
public int getLabel() {
return label;
}
// CREATE
/**
* Constructor.
*
* @param creator
* HE_Creator that generates this mesh
*/
public HE_Mesh(final HEC_Creator creator) {
super();
setNoCopy(creator.create());
_centerUpdated = false;
label = -1;
}
// MODIFY
/**
* Modify the mesh.
*
* @param modifier
* HE_Modifier to apply
* @return self
*/
public HE_Mesh modify(final HEM_Modifier modifier) {
return modifier.apply(this);
}
/**
* Modify selection. Elements should be part of this mesh.
*
* @param modifier
* HE_Modifier to apply
* @param selection
* the selection
* @return self
*/
public HE_Mesh modifySelected(final HEM_Modifier modifier,
final HE_Selection selection) {
return modifier.apply(selection.get());
}
// SUBDIVIDE
/**
* Subdivide the mesh.
*
* @param subdividor
* HE_Subdividor to apply
* @return self
*/
public HE_Mesh subdivide(final HES_Subdividor subdividor) {
return subdividor.apply(this);
}
/**
* Subdivide selection of the mesh.
*
* @param subdividor
* HE_Subdividor to apply
* @param selection
* HE_Selection
* @return self
*/
public HE_Mesh subdivideSelected(final HES_Subdividor subdividor,
final HE_Selection selection) {
return subdividor.apply(selection);
}
/**
* Subdivide the mesh a number of times.
*
* @param subdividor
* HE_Subdividor to apply
* @param rep
* subdivision iterations. WARNING: higher values will lead to
* unmanageable number of faces.
* @return self
*/
public HE_Mesh subdivide(final HES_Subdividor subdividor, final int rep) {
for (int i = 0; i < rep; i++) {
subdivide(subdividor);
}
return this;
}
/**
* Subdivide a selection of the mesh a number of times.
*
* @param subdividor
* HE_Subdividor to apply
* @param selection
* HE_Selection initial selection
* @param rep
* subdivision iterations
* @return self
*/
public HE_Mesh subdivideSelected(final HES_Subdividor subdividor,
final HE_Selection selection, final int rep) {
for (int i = 0; i < rep; i++) {
subdivideSelected(subdividor, selection);
}
return this;
}
/**
* Simplify.
*
* @param simplifier
* the simplifier
* @return the h e_ mesh
*/
public HE_Mesh simplify(final HES_Simplifier simplifier) {
return simplifier.apply(this);
}
/**
* Simplify.
*
* @param simplifier
* the simplifier
* @param selection
* the selection
* @return the h e_ mesh
*/
public HE_Mesh simplify(final HES_Simplifier simplifier,
final HE_Selection selection) {
return simplifier.apply(selection);
}
/**
* Deep copy of mesh.
*
* @return copy as new HE_Mesh, includes selection
*/
public HE_Mesh get() {
final HE_Mesh result = new HE_Mesh();
final HashMap<Integer, Integer> vertexCorrelation = new HashMap<Integer, Integer>();
final HashMap<Integer, Integer> faceCorrelation = new HashMap<Integer, Integer>();
final HashMap<Integer, Integer> halfedgeCorrelation = new HashMap<Integer, Integer>();
final HashMap<Integer, Integer> edgeCorrelation = new HashMap<Integer, Integer>();
HE_Vertex rv;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
rv = new HE_Vertex(v);
result.add(rv);
vertexCorrelation.put(v.key(), rv.key());
}
HE_Face rf;
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
rf = new HE_Face();
result.add(rf);
rf.label = f.label;
faceCorrelation.put(f.key(), rf.key());
}
HE_Halfedge rhe;
HE_Halfedge he;
final Iterator<HE_Halfedge> heItr = heItr();
while (heItr.hasNext()) {
he = heItr.next();
rhe = new HE_Halfedge();
result.add(rhe);
halfedgeCorrelation.put(he.key(), rhe.key());
}
HE_Edge re;
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
while (eItr.hasNext()) {
e = eItr.next();
re = new HE_Edge();
result.add(re);
edgeCorrelation.put(e.key(), re.key());
}
HE_Vertex sv;
HE_Vertex tv;
final Iterator<HE_Vertex> svItr = vItr();
final Iterator<HE_Vertex> tvItr = result.vItr();
Integer key;
while (svItr.hasNext()) {
sv = svItr.next();
tv = tvItr.next();
tv.set(sv);
if (sv.getHalfedge() != null) {
key = halfedgeCorrelation.get(sv.getHalfedge().key());
tv.setHalfedge(result.getHalfedgeByKey(key));
}
}
HE_Face sf;
HE_Face tf;
final Iterator<HE_Face> sfItr = fItr();
final Iterator<HE_Face> tfItr = result.fItr();
while (sfItr.hasNext()) {
sf = sfItr.next();
tf = tfItr.next();
if (sf.getHalfedge() != null) {
key = halfedgeCorrelation.get(sf.getHalfedge().key());
tf.setHalfedge(result.getHalfedgeByKey(key));
}
}
final Iterator<HE_Edge> seItr = eItr();
final Iterator<HE_Edge> teItr = result.eItr();
HE_Edge se;
HE_Edge te;
while (seItr.hasNext()) {
se = seItr.next();
te = teItr.next();
if (se.getHalfedge() != null) {
key = halfedgeCorrelation.get(se.getHalfedge().key());
te.setHalfedge(result.getHalfedgeByKey(key));
}
}
HE_Halfedge she;
HE_Halfedge the;
final Iterator<HE_Halfedge> sheItr = heItr();
final Iterator<HE_Halfedge> theItr = result.heItr();
while (sheItr.hasNext()) {
she = sheItr.next();
the = theItr.next();
if (she.getPair() != null) {
key = halfedgeCorrelation.get(she.getPair().key());
the.setPair(result.getHalfedgeByKey(key));
}
if (she.getNextInFace() != null) {
key = halfedgeCorrelation.get(she.getNextInFace().key());
the.setNext(result.getHalfedgeByKey(key));
}
if (she.getVertex() != null) {
key = vertexCorrelation.get(she.getVertex().key());
the.setVertex(result.getVertexByKey(key));
}
if (she.getFace() != null) {
key = faceCorrelation.get(she.getFace().key());
the.setFace(result.getFaceByKey(key));
}
if (she.getEdge() != null) {
key = edgeCorrelation.get(she.getEdge().key());
the.setEdge(result.getEdgeByKey(key));
}
}
result._center.set(_center);
result._centerUpdated = _centerUpdated;
return result;
}
/**
* Add all mesh elements to this mesh. No copies are made.
*
* @param mesh
* mesh to add
*/
public void add(final HE_Mesh mesh) {
addVertices(mesh.getVerticesAsArray());
addFaces(mesh.getFacesAsArray());
addEdges(mesh.getEdgesAsArray());
addHalfedges(mesh.getHalfedgesAsArray());
}
/**
* Add all mesh elements to this mesh. No copies are made. Tries to join
* geometry.
*
* @param mesh
* mesh to add
*/
public void fuse(final HE_Mesh mesh) {
addVertices(mesh.getVerticesAsArray());
addFaces(mesh.getFacesAsArray());
addEdges(mesh.getEdgesAsArray());
addHalfedges(mesh.getHalfedgesAsArray());
set(new HE_Mesh(new HEC_FromPolygons().setPolygons(this
.getPolygonList())));
}
/**
* Replace mesh with deep copy of target.
*
* @param target
* HE_Mesh to be duplicated
*/
public void set(final HE_Mesh target) {
final HE_Mesh result = target.get();
replaceVertices(result.getVerticesAsArray());
replaceFaces(result.getFacesAsArray());
replaceHalfedges(result.getHalfedgesAsArray());
replaceEdges(result.getEdgesAsArray());
}
/**
* Replace mesh with shallow copy of target.
*
* @param target
* HE_Mesh to be duplicated
*/
private void setNoCopy(final HE_Mesh target) {
_hashedVertices = target._hashedVertices;
_hashedHalfedges = target._hashedHalfedges;
_hashedEdges = target._hashedEdges;
_hashedFaces = target._hashedFaces;
_center = target._center;
_centerUpdated = target._centerUpdated;
}
// CONVERT
/**
* Return all vertex positions as an array .
*
* @return 2D array of float. First index gives vertex. Second index gives
* x-,y- or z-coordinate.
*/
public float[][] getVerticesAsFloat() {
final float[][] result = new float[numberOfVertices()][3];
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
result[i][0] = (float) (v.x);
result[i][1] = (float) (v.y);
result[i][2] = (float) (v.z);
i++;
}
return result;
}
/**
* Return all vertex positions as an array .
*
* @return 2D array of double. First index gives vertex. Second index gives
* x-,y- or z-coordinate.
*/
public double[][] getVerticesAsDouble() {
final double[][] result = new double[numberOfVertices()][3];
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
result[i][0] = (float) (v.x);
result[i][1] = (float) (v.y);
result[i][2] = (float) (v.z);
i++;
}
return result;
}
/**
* Vertex key to index.
*
* @return the map
*/
public Map<Integer, Integer> vertexKeyToIndex() {
final Map<Integer, Integer> map = new FastMap<Integer, Integer>();
int i = 0;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
map.put(vItr.next().key(), i);
i++;
}
return map;
}
/**
* Return all vertex positions.
*
* @return array of WB_Point, values are copied.
*/
public WB_Point3d[] getVerticesAsNewPoint() {
final WB_Point3d[] result = new WB_Point3d[numberOfVertices()];
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
result[i] = new WB_Point3d(v);
i++;
}
return result;
}
/**
* Return all vertex positions.
*
* @return array of WB_Point, no copies are made.
*/
public WB_Point3d[] getVerticesAsPoint() {
final WB_Point3d[] result = new WB_Point3d[numberOfVertices()];
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
result[i] = v;
i++;
}
return result;
}
/**
* Return all vertex normal.
*
* @return array of WB_Normal.
*/
public WB_Normal3d[] getVertexNormals() {
final WB_Normal3d[] result = new WB_Normal3d[numberOfVertices()];
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
result[i] = v.getVertexNormal();
i++;
}
return result;
}
/**
* Return all vertex normal.
*
* @return FastMap of WB_Normal.
*/
public Map<Integer, WB_Normal3d> getKeyedVertexNormals() {
final Map<Integer, WB_Normal3d> result = new FastMap<Integer, WB_Normal3d>(
numberOfVertices());
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
result.put(v.key(), v.getVertexNormal());
}
return result;
}
/**
* Return the faces as array of vertex indices.
*
* @return 2D array of int. First index gives face. Second index gives
* vertices.
*/
public int[][] getFacesAsInt() {
final int[][] result = new int[numberOfFaces()][];
final FastMap<Integer, Integer> vertexKeys = new FastMap<Integer, Integer>();
final Iterator<HE_Vertex> vItr = vItr();
int i = 0;
while (vItr.hasNext()) {
vertexKeys.put(vItr.next().key(), i);
i++;
}
final Iterator<HE_Face> fItr = fItr();
HE_Halfedge he;
HE_Face f;
i = 0;
while (fItr.hasNext()) {
f = fItr.next();
result[i] = new int[f.getFaceOrder()];
he = f.getHalfedge();
int j = 0;
do {
result[i][j] = vertexKeys.get(he.getVertex().key());
he = he.getNextInFace();
j++;
} while (he != f.getHalfedge());
i++;
}
return result;
}
/**
* Return all face normals.
*
* @return array of WB_Normal.
*/
public WB_Normal3d[] getFaceNormals() {
final WB_Normal3d[] result = new WB_Normal3d[numberOfFaces()];
int i = 0;
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
result[i] = f.getFaceNormal();
i++;
}
return result;
}
/**
* Return all face normals.
*
* @return FastMap of WB_Normal.
*/
public Map<Integer, WB_Normal3d> getKeyedFaceNormals() {
final Map<Integer, WB_Normal3d> result = new FastMap<Integer, WB_Normal3d>(
numberOfFaces());
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
result.put(f.key(), f.getFaceNormal());
}
return result;
}
/**
* Return all face centers.
*
* @return array of WB_Point.
*/
public WB_Point3d[] getFaceCenters() {
final WB_Point3d[] result = new WB_Point3d[numberOfFaces()];
int i = 0;
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
result[i] = f.getFaceCenter();
i++;
}
return result;
}
/**
* Return all face centers.
*
* @return FastMap of WB_Point.
*/
public Map<Integer, WB_Point3d> getKeyedFaceCenters() {
final Map<Integer, WB_Point3d> result = new FastMap<Integer, WB_Point3d>(
numberOfFaces());
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
result.put(f.key(), f.getFaceCenter());
}
return result;
}
/**
* Return all edge normals.
*
* @return array of WB_Normal.
*/
public WB_Normal3d[] getEdgeNormals() {
final WB_Normal3d[] result = new WB_Normal3d[numberOfEdges()];
int i = 0;
HE_Edge e;
final Iterator<HE_Edge> eItr = eItr();
while (eItr.hasNext()) {
e = eItr.next();
result[i] = e.getEdgeNormal();
i++;
}
return result;
}
/**
* Return all edge normals.
*
* @return FastMap of WB_Normal.
*/
public Map<Integer, WB_Normal3d> getKeyedEdgeNormals() {
final Map<Integer, WB_Normal3d> result = new FastMap<Integer, WB_Normal3d>(
numberOfEdges());
HE_Edge e;
final Iterator<HE_Edge> eItr = eItr();
while (eItr.hasNext()) {
e = eItr.next();
result.put(e.key(), e.getEdgeNormal());
}
return result;
}
/**
* Return all edge centers.
*
* @return array of WB_Point.
*/
public WB_Point3d[] getEdgeCenters() {
final WB_Point3d[] result = new WB_Point3d[numberOfEdges()];
int i = 0;
HE_Edge e;
final Iterator<HE_Edge> eItr = eItr();
while (eItr.hasNext()) {
e = eItr.next();
result[i] = e.getEdgeCenter();
i++;
}
return result;
}
/**
* Return all edge centers.
*
* @return FastMap of WB_Point.
*/
public Map<Integer, WB_Point3d> getKeyedEdgeCenters() {
final Map<Integer, WB_Point3d> result = new FastMap<Integer, WB_Point3d>(
numberOfEdges());
HE_Edge e;
final Iterator<HE_Edge> eItr = eItr();
while (eItr.hasNext()) {
e = eItr.next();
result.put(e.key(), e.getEdgeCenter());
}
return result;
}
/**
* Set vertex positions to values in array.
*
* @param values
* 2D array of float. First index is number of vertices, second
* index is 3 (x-,y- and z-coordinate)
*/
public void setVerticesFromFloat(final float[][] values) {
int i = 0;
_center.set(0, 0, 0);
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
v.set(values[i][0], values[i][1], values[i][2]);
i++;
}
}
/**
* Set vertex positions to values in array.
*
* @param values
* array of WB_Point.
*/
public void setVerticesFromPoint(final WB_Point3d[] values) {
int i = 0;
_center.set(0, 0, 0);
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
v.set(values[i]);
i++;
}
;
}
/**
* Set vertex positions to values in array.
*
* @param values
* 2D array of double. First index is number of vertices, second
* index is 3 (x-,y- and z-coordinate)
*/
public void setVerticesFromDouble(final double[][] values) {
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
v.set(values[i][0], values[i][1], values[i][2]);
i++;
}
;
}
/**
* Set vertex positions to values in array.
*
* @param values
* 2D array of int. First index is number of vertices, second
* index is 3 (x-,y- and z-coordinate)
*/
public void setVerticesFromInt(final int[][] values) {
int i = 0;
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
v.set(values[i][0], values[i][1], values[i][2]);
i++;
}
;
}
/**
* Return the mesh as polygon soup.
*
* @return array of WB_polygon
*
*/
public WB_ExplicitPolygon[] getPolygons() {
final WB_ExplicitPolygon[] result = new WB_ExplicitPolygon[numberOfFaces()];
final Iterator<HE_Face> fItr = fItr();
HE_Face f;
int i = 0;
while (fItr.hasNext()) {
f = fItr.next();
result[i] = f.toPolygon();
i++;
}
return result;
}
/**
* Gets the polygon list.
*
* @return the polygon list
*/
public List<WB_ExplicitPolygon> getPolygonList() {
final List<WB_ExplicitPolygon> result = new FastList<WB_ExplicitPolygon>();
final Iterator<HE_Face> fItr = fItr();
HE_Face f;
while (fItr.hasNext()) {
f = fItr.next();
result.add(f.toPolygon());
}
return result;
}
/**
* Gets the segments.
*
* @return the segments
*/
public WB_ExplicitSegment[] getSegments() {
final WB_ExplicitSegment[] result = new WB_ExplicitSegment[numberOfEdges()];
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
int i = 0;
while (eItr.hasNext()) {
e = eItr.next();
result[i] = new WB_ExplicitSegment(e.getStartVertex(),
e.getEndVertex(), false);
i++;
}
return result;
}
/**
* Gets the indexed segments.
*
* @return the indexed segments
*/
public WB_IndexedSegment[] getIndexedSegments() {
final WB_IndexedSegment[] result = new WB_IndexedSegment[numberOfEdges()];
final WB_Point3d[] points = getVerticesAsPoint();
final FastMap<Integer, Integer> map = new FastMap<Integer, Integer>();
map.putAll(vertexKeyToIndex());
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
int i = 0;
while (eItr.hasNext()) {
e = eItr.next();
result[i] = new WB_IndexedSegment(
map.get(e.getStartVertex().key()), map.get(e.getEndVertex()
.key()), points);
i++;
}
return result;
}
/**
* Gets the frame.
*
* @return the frame
*/
public WB_Frame getFrame() {
final WB_Frame frame = new WB_Frame(getVerticesAsPoint());
final FastMap<Integer, Integer> map = new FastMap<Integer, Integer>();
map.putAll(vertexKeyToIndex());
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
while (eItr.hasNext()) {
e = eItr.next();
frame.addStrut(map.get(e.getStartVertex().key()),
map.get(e.getEndVertex().key()));
}
return frame;
}
// TRANSFORM
/**
* Apply transform to entire mesh.
*
* @param T
* WB_Transform to apply
*
* @return self
*/
public HE_Mesh transform(final WB_Transform T) {
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
T.applySelf(vItr.next());
}
return this;
}
/**
* Translate entire mesh.
*
* @param x
* the x
* @param y
* the y
* @param z
* the z
* @return self
*/
public HE_Mesh move(final double x, final double y, final double z) {
_center.add(x, y, z);
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
vItr.next().add(x, y, z);
}
return this;
}
/**
* Translate entire mesh.
*
* @param v
* the v
* @return self
*/
public HE_Mesh move(final WB_Point3d v) {
return move(v.x, v.y, v.z);
}
/**
* Translate entire mesh to given position.
*
* @param x
* the x
* @param y
* the y
* @param z
* the z
* @return self
*/
public HE_Mesh moveTo(final double x, final double y, final double z) {
if (!_centerUpdated) {
getCenter();
}
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
vItr.next().add(x - _center.x, y - _center.y, z - _center.z);
}
_center.set(x, y, z);
return this;
}
/**
* Translate entire mesh to given position.
*
* @param v
* the v
* @return self
*/
public HE_Mesh moveTo(final WB_Point3d v) {
return moveTo(v.x, v.y, v.z);
}
/**
* Rotate entire mesh around an arbitrary axis.
*
* @param angle
* angle
* @param p1x
* x-coordinate of first point on axis
* @param p1y
* y-coordinate of first point on axis
* @param p1z
* z-coordinate of first point on axis
* @param p2x
* x-coordinate of second point on axis
* @param p2y
* y-coordinate of second point on axis
* @param p2z
* z-coordinate of second point on axis
* @return self
*/
public HE_Mesh rotateAboutAxis(final double angle, final double p1x,
final double p1y, final double p1z, final double p2x,
final double p2y, final double p2z) {
if (!_centerUpdated) {
getCenter();
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
final WB_Transform raa = new WB_Transform();
raa.addRotateAboutAxis(angle, new WB_Point3d(p1x, p1y, p1z),
new WB_Vector3d(p2x - p1x, p2y - p1y, p2z - p1z));
while (vItr.hasNext()) {
v = vItr.next();
raa.applySelf(v);
}
raa.applySelf(_center);
return this;
}
/**
* Rotate entire mesh around an arbitrary axis.
*
* @param angle
* angle
* @param p1
* first point on axis
* @param p2
* second point on axis
* @return self
*/
public HE_Mesh rotateAboutAxis(final double angle, final WB_Point3d p1,
final WB_Point3d p2) {
if (!_centerUpdated) {
getCenter();
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
final WB_Transform raa = new WB_Transform();
raa.addRotateAboutAxis(angle, p1, p2.subToVector(p1));
while (vItr.hasNext()) {
v = vItr.next();
raa.applySelf(v);
}
raa.applySelf(_center);
;
return this;
}
/**
* Rotate entire mesh around an arbitrary axis.
*
* @param angle
* angle
* @param p
* rotation point
* @param a
* axis
* @return self
*/
public HE_Mesh rotateAboutAxis(final double angle, final WB_Point3d p,
final WB_Vector3d a) {
if (!_centerUpdated) {
getCenter();
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
final WB_Transform raa = new WB_Transform();
raa.addRotateAboutAxis(angle, p, a);
while (vItr.hasNext()) {
v = vItr.next();
raa.applySelf(v);
}
raa.applySelf(_center);
;
return this;
}
/**
* Rotate entire mesh around an arbitrary axis.
*
* @param angle
* angle
* @param p
* rotation point
* @param a
* axis
* @return self
*/
public HE_Mesh rotateAboutAxis(final double angle, final WB_Point3d p,
final WB_Normal3d a) {
if (!_centerUpdated) {
getCenter();
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
final WB_Transform raa = new WB_Transform();
raa.addRotateAboutAxis(angle, p, a);
while (vItr.hasNext()) {
v = vItr.next();
raa.applySelf(v);
}
raa.applySelf(_center);
;
return this;
}
/**
* Scale entire mesh around center point.
*
* @param scaleFactorx
* x-coordinate of scale factor
* @param scaleFactory
* y-coordinate of scale factor
* @param scaleFactorz
* z-coordinate of scale factor
* @param c
* center
* @return self
*/
public HE_Mesh scale(final double scaleFactorx, final double scaleFactory,
final double scaleFactorz, final WB_Point3d c) {
if (!_centerUpdated) {
getCenter();
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
v.set(c.x + scaleFactorx * (v.x - c.x), c.y + scaleFactory
* (v.y - c.y), c.z + scaleFactorz * (v.z - c.z));
}
_center.set(c.x + scaleFactorx * (-c.x + _center.x), c.y + scaleFactory
* (-c.y + _center.y), c.z + scaleFactorz * (-c.z + _center.z));
;
return this;
}
/**
* Scale entire mesh around center point.
*
* @param scaleFactor
* scale
* @param c
* center
* @return self
*/
public HE_Mesh scale(final double scaleFactor, final WB_Point3d c) {
return scale(scaleFactor, scaleFactor, scaleFactor, c);
}
/**
* Scale entire mesh around bodycenter.
*
* @param scaleFactorx
* x-coordinate of scale factor
* @param scaleFactory
* y-coordinate of scale factor
* @param scaleFactorz
* z-coordinate of scale factor
* @return self
*/
public HE_Mesh scale(final double scaleFactorx, final double scaleFactory,
final double scaleFactorz) {
if (!_centerUpdated) {
getCenter();
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
v.set(_center.x + scaleFactorx * (v.x - _center.x), _center.y
+ scaleFactory * (v.y - _center.y), _center.z
+ scaleFactorz * (v.z - _center.z));
}
;
return this;
}
/**
* Scale entire mesh around bodycenter.
*
* @param scaleFactor
* scale
* @return self
*/
public HE_Mesh scale(final double scaleFactor) {
return scale(scaleFactor, scaleFactor, scaleFactor);
}
// DERIVED ELEMENTS
/**
* Get the center (average of all vertex positions).
*
* @return the center
*/
public WB_Point3d getCenter() {
if (_centerUpdated) {
return _center;
} else {
resetCenter();
return _center;
}
}
/**
* Reset the center to the average of all vertex positions).
*
*/
public void resetCenter() {
_center.set(0, 0, 0);
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
_center.add(vItr.next());
}
_center.div(numberOfVertices());
_centerUpdated = true;
}
// HELPERS
/**
* Assign face to halfedge loop.
*
* @param face
* face
* @param halfedge
* halfedge loop
*/
protected static void assignFaceToLoop(final HE_Face face,
final HE_Halfedge halfedge) {
HE_Halfedge he = halfedge;
do {
he.setFace(face);
he = he.getNextInFace();
} while (he != halfedge);
}
/**
* Cycle halfedges.
*
* @param halfedges
* halfedges to cycle
*/
public static void cycleHalfedges(final List<HE_Halfedge> halfedges) {
HE_Halfedge he;
final int n = halfedges.size();
if (n > 0) {
for (int j = 0; j < n - 1; j++) {
he = halfedges.get(j);
he.setNext(halfedges.get(j + 1));
}
he = halfedges.get(n - 1);
he.setNext(halfedges.get(0));
}
}
/**
* Cycle halfedges.
*
* @param halfedges
* halfedges to cycle
*/
public static void cycleHalfedgesReverse(final List<HE_Halfedge> halfedges) {
HE_Halfedge he;
final int n = halfedges.size();
if (n > 0) {
he = halfedges.get(0);
he.setNext(halfedges.get(n - 1));
for (int j = 1; j < n; j++) {
he = halfedges.get(j);
he.setNext(halfedges.get(j - 1));
}
}
}
/**
* Collect all unpaired halfedges.
*
* @return the unpaired halfedges
*/
public List<HE_Halfedge> getUnpairedHalfedges() {
final List<HE_Halfedge> unpairedHalfedges = new FastList<HE_Halfedge>();
HE_Halfedge he;
final Iterator<HE_Halfedge> heItr = heItr();
while (heItr.hasNext()) {
he = heItr.next();
if (he.getPair() == null) {
unpairedHalfedges.add(he);
}
}
return unpairedHalfedges;
}
/**
* Collect all boundary halfedges.
*
* @return boundary halfedges
*/
public List<HE_Halfedge> getBoundaryHalfedges() {
final List<HE_Halfedge> boundaryHalfedges = new FastList<HE_Halfedge>();
HE_Halfedge he;
final Iterator<HE_Halfedge> heItr = heItr();
while (heItr.hasNext()) {
he = heItr.next();
if (he.getFace() == null) {
boundaryHalfedges.add(he);
}
}
return boundaryHalfedges;
}
/**
* Try to pair all unpaired halfedges.
*/
public void pairHalfedges() {
class VertexInfo {
FastList<HE_Halfedge> out;
FastList<HE_Halfedge> in;
VertexInfo() {
out = new FastList<HE_Halfedge>();
in = new FastList<HE_Halfedge>();
}
}
final FastMap<Integer, VertexInfo> vertexLists = new FastMap<Integer, VertexInfo>();
final List<HE_Halfedge> unpairedHalfedges = getUnpairedHalfedges();
HE_Vertex v;
VertexInfo vi;
// System.out.println("HE_Mesh : collating " + unpairedHalfedges.size()
// + " unpaired halfedges per vertex.");
for (final HE_Halfedge he : unpairedHalfedges) {
v = he.getVertex();
vi = vertexLists.get(v.key());
if (vi == null) {
vi = new VertexInfo();
vertexLists.put(v.key(), vi);
}
vi.out.add(he);
v = he.getNextInFace().getVertex();
vi = vertexLists.get(v.key());
if (vi == null) {
vi = new VertexInfo();
vertexLists.put(v.key(), vi);
}
vi.in.add(he);
}
HE_Halfedge he;
HE_Halfedge he2;
HE_Edge e;
// System.out.println("HE_Mesh : pairing unpaired halfedges per vertex.");
for (VertexInfo vInfo : vertexLists.values()) {
for (int i = 0; i < vInfo.out.size(); i++) {
he = vInfo.out.get(i);
if (he.getPair() == null) {
for (int j = 0; j < vInfo.in.size(); j++) {
he2 = vInfo.in.get(j);
if ((he2.getPair() == null)
&& (he.getVertex() == he2.getNextInFace()
.getVertex())
&& (he2.getVertex() == he.getNextInFace()
.getVertex())) {
he.setPair(he2);
e = new HE_Edge();
e.setHalfedge(he);
he.setEdge(e);
he2.setEdge(e);
add(e);
break;
}
}
}
}
}
}
/**
* Pair halfedges.
*
* @param unpairedHalfedges
* the unpaired halfedges
*/
public void pairHalfedges(final List<HE_Halfedge> unpairedHalfedges) {
class VertexInfo {
FastList<HE_Halfedge> out;
FastList<HE_Halfedge> in;
VertexInfo() {
out = new FastList<HE_Halfedge>();
in = new FastList<HE_Halfedge>();
}
}
final FastMap<Integer, VertexInfo> vertexLists = new FastMap<Integer, VertexInfo>();
HE_Vertex v;
VertexInfo vi;
System.out.println("HE_Mesh : collating " + unpairedHalfedges.size()
+ " unpaired halfedges per vertex.");
for (final HE_Halfedge he : unpairedHalfedges) {
v = he.getVertex();
vi = vertexLists.get(v.key());
if (vi == null) {
vi = new VertexInfo();
vertexLists.put(v.key(), vi);
}
vi.out.add(he);
v = he.getNextInFace().getVertex();
vi = vertexLists.get(v.key());
if (vi == null) {
vi = new VertexInfo();
vertexLists.put(v.key(), vi);
}
vi.in.add(he);
}
HE_Halfedge he;
HE_Halfedge he2;
HE_Edge e;
System.out.println("HE_Mesh : pairing unpaired halfedges per vertex.");
for (final VertexInfo vInfo : vertexLists.values()) {
for (int i = 0; i < vInfo.out.size(); i++) {
he = vInfo.out.get(i);
if (he.getPair() == null) {
for (int j = 0; j < vInfo.in.size(); j++) {
he2 = vInfo.in.get(j);
if ((he2.getPair() == null)
&& (he.getVertex() == he2.getNextInFace()
.getVertex())
&& (he2.getVertex() == he.getNextInFace()
.getVertex())) {
he.setPair(he2);
e = new HE_Edge();
e.setHalfedge(he);
he.setEdge(e);
he2.setEdge(e);
add(e);
break;
}
}
}
}
}
}
/**
* Cap all remaining unpaired halfedges. Only use after pairHalfedges();
*/
public void capHalfedges() {
final List<HE_Halfedge> unpairedHalfedges = getUnpairedHalfedges();
final int nuh = unpairedHalfedges.size();
final HE_Halfedge[] newHalfedges = new HE_Halfedge[nuh];
HE_Halfedge he1, he2;
HE_Edge e;
for (int i = 0; i < nuh; i++) {
he1 = unpairedHalfedges.get(i);
he2 = new HE_Halfedge();
he2.setVertex(he1.getNextInFace().getVertex());
he1.setPair(he2);
newHalfedges[i] = he2;
add(he2);
e = new HE_Edge();
add(e);
e.setHalfedge(he1);
he1.setEdge(e);
he2.setEdge(e);
}
for (int i = 0; i < nuh; i++) {
he1 = newHalfedges[i];
if (he1.getNextInFace() == null) {
for (int j = 0; j < nuh; j++) {
he2 = newHalfedges[j];
if (he2.getVertex() == he1.getPair().getVertex()) {
he1.setNext(he2);
break;
}
}
}
}
}
/**
* Uncap halfedges.
*/
public void uncapHalfedges() {
final Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
if (he.getFace() == null) {
he.getVertex().setHalfedge(he.getPair());
he.getEdge().setHalfedge(he.getPair());
he.getPair().clearPair();
heItr.remove();
}
}
}
/**
* Cap holes.
*
* @return all new faces as FastList<HE_Face>
*/
public List<HE_Face> capHoles() {
final List<HE_Face> caps = new FastList<HE_Face>();
final List<HE_Halfedge> unpairedEdges = getUnpairedHalfedges();
List<HE_Halfedge> loopedHalfedges;
HE_Halfedge start;
HE_Halfedge he;
HE_Halfedge hen;
HE_Face nf;
List<HE_Halfedge> newHalfedges;
HE_Halfedge phe;
HE_Halfedge nhe;
HE_Edge ne;
while (unpairedEdges.size() > 0) {
loopedHalfedges = new FastList<HE_Halfedge>();
start = unpairedEdges.get(0);
loopedHalfedges.add(start);
he = start;
hen = start;
boolean stuck = false;
do {
for (int i = 0; i < unpairedEdges.size(); i++) {
hen = unpairedEdges.get(i);
if (hen.getVertex() == he.getNextInFace().getVertex()) {
if (!loopedHalfedges.contains(hen)) {
loopedHalfedges.add(hen);
} else {
stuck = true;
}
break;
}
}
if (hen.getVertex() != he.getNextInFace().getVertex()) {
stuck = true;
}
he = hen;
} while ((hen.getNextInFace().getVertex() != start.getVertex())
&& (!stuck));
unpairedEdges.removeAll(loopedHalfedges);
nf = new HE_Face();
add(nf);
caps.add(nf);
newHalfedges = new FastList<HE_Halfedge>();
for (int i = 0; i < loopedHalfedges.size(); i++) {
phe = loopedHalfedges.get(i);
nhe = new HE_Halfedge();
add(nhe);
newHalfedges.add(nhe);
nhe.setVertex(phe.getNextInFace().getVertex());
nhe.setPair(phe);
nhe.setFace(nf);
if (nf.getHalfedge() == null) {
nf.setHalfedge(nhe);
}
ne = new HE_Edge();
add(ne);
ne.setHalfedge(nhe);
nhe.setEdge(ne);
phe.setEdge(ne);
}
cycleHalfedgesReverse(newHalfedges);
}
return caps;
}
/**
* Clean all mesh elements not used by any faces.
*
* @return self
*/
public HE_Mesh cleanUnusedElementsByFace() {
final List<HE_Vertex> cleanedVertices = new FastList<HE_Vertex>();
final List<HE_Halfedge> cleanedHalfedges = new FastList<HE_Halfedge>();
final List<HE_Edge> cleanedEdges = new FastList<HE_Edge>();
HE_Halfedge he;
HE_Edge e;
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
he = f.getHalfedge();
do {
if (!cleanedVertices.contains(he.getVertex())) {
cleanedVertices.add(he.getVertex());
he.getVertex().setHalfedge(he);
}
if (!cleanedHalfedges.contains(he)) {
cleanedHalfedges.add(he);
}
he.clearEdge();
he = he.getNextInFace();
} while (he != f.getHalfedge());
}
final int n = cleanedHalfedges.size();
for (int i = 0; i < n; i++) {
he = cleanedHalfedges.get(i);
if (!cleanedHalfedges.contains(he.getPair())) {
he.clearPair();
he.getVertex().setHalfedge(he);
} else {
if (he.getEdge() == null) {
e = new HE_Edge();
e.setHalfedge(he);
he.setEdge(e);
he.getPair().setEdge(e);
cleanedEdges.add(e);
}
}
}
replaceVertices(cleanedVertices);
replaceHalfedges(cleanedHalfedges);
replaceEdges(cleanedEdges);
return this;
}
// MESH OPERATIONS
/**
* Reverse all faces. Flips normals.
*
* @return the h e_ mesh
*/
public HE_Mesh flipAllFaces() {
HE_Edge edge;
HE_Halfedge he1;
HE_Halfedge he2;
HE_Vertex tmp;
HE_Halfedge[] prevHe;
HE_Halfedge he;
final Iterator<HE_Edge> eItr = eItr();
while (eItr.hasNext()) {
edge = eItr.next();
he1 = edge.getHalfedge();
he2 = he1.getPair();
tmp = he1.getVertex();
he1.setVertex(he2.getVertex());
he2.setVertex(tmp);
he1.getVertex().setHalfedge(he1);
he2.getVertex().setHalfedge(he2);
}
prevHe = new HE_Halfedge[numberOfHalfedges()];
int i = 0;
Iterator<HE_Halfedge> heItr = heItr();
while (heItr.hasNext()) {
he = heItr.next();
prevHe[i] = he.getPrevInFace();
i++;
}
i = 0;
heItr = heItr();
while (heItr.hasNext()) {
he = heItr.next();
he.setNext(prevHe[i]);
i++;
}
return this;
}
/**
* Collapse halfedge. Start vertex is removed. Degenerate faces are removed.
* This function can result in non-manifold meshes.
*
* @param he
* the he
* @return true, if successful
*/
public boolean collapseHalfedge(final HE_Halfedge he) {
if (contains(he)) {
final HE_Halfedge hePair = he.getPair();
HE_Face f = he.getFace();
HE_Face fp = hePair.getFace();
final HE_Vertex v = he.getVertex();
final HE_Vertex vp = hePair.getVertex();
final List<HE_Halfedge> tmp = v.getHalfedgeStar();
for (int i = 0; i < tmp.size(); i++) {
tmp.get(i).setVertex(vp);
}
vp.setHalfedge(hePair.getNextInVertex());
final HE_Halfedge hen = he.getNextInFace();
final HE_Halfedge hep = he.getPrevInFace();
final HE_Halfedge hePairn = hePair.getNextInFace();
final HE_Halfedge hePairp = hePair.getPrevInFace();
if (f != null) {
f.setHalfedge(hen);
}
if (fp != null) {
fp.setHalfedge(hePairn);
}
hep.setNext(hen);
hePairp.setNext(hePairn);
remove(he);
remove(hePair);
remove(he.getEdge());
remove(v);
deleteTwoEdgeFace(f);
deleteTwoEdgeFace(fp);
return true;
}
return false;
}
/**
* Collapse halfedge bp.
*
* @param he
* the he
* @return true, if successful
*/
public boolean collapseHalfedgeBP(final HE_Halfedge he) {
if (contains(he)) {
final HE_Halfedge hePair = he.getPair();
HE_Face f = he.getFace();
HE_Face fp = hePair.getFace();
final HE_Vertex v = he.getVertex();
final HE_Vertex vp = hePair.getVertex();
if (v.isBoundary()) {
return false;
}
final List<HE_Halfedge> tmp = v.getHalfedgeStar();
for (int i = 0; i < tmp.size(); i++) {
tmp.get(i).setVertex(vp);
}
vp.setHalfedge(hePair.getNextInVertex());
final HE_Halfedge hen = he.getNextInFace();
final HE_Halfedge hep = he.getPrevInFace();
final HE_Halfedge hePairn = hePair.getNextInFace();
final HE_Halfedge hePairp = hePair.getPrevInFace();
if (f != null) {
f.setHalfedge(hen);
}
if (fp != null) {
fp.setHalfedge(hePairn);
}
hep.setNext(hen);
hePairp.setNext(hePairn);
remove(he);
remove(hePair);
remove(he.getEdge());
remove(v);
deleteTwoEdgeFace(f);
deleteTwoEdgeFace(fp);
return true;
}
return false;
}
/**
* Collapse edge. End vertices are averaged. Degenerate faces are removed.
* This function can result in non-manifold meshes.
*
* @param e
* edge to collapse
* @return true, if successful
*/
public boolean collapseEdge(final HE_Edge e) {
if (contains(e)) {
final HE_Halfedge he = e.getHalfedge();
final HE_Halfedge hePair = e.getHalfedge().getPair();
HE_Face f = he.getFace();
HE_Face fp = hePair.getFace();
final HE_Vertex v = he.getVertex();
final HE_Vertex vp = hePair.getVertex();
vp.add(v).mult(0.5);
final List<HE_Halfedge> tmp = v.getHalfedgeStar();
for (int i = 0; i < tmp.size(); i++) {
tmp.get(i).setVertex(vp);
}
vp.setHalfedge(hePair.getNextInVertex());
final HE_Halfedge hen = he.getNextInFace();
final HE_Halfedge hep = he.getPrevInFace();
final HE_Halfedge hePairn = hePair.getNextInFace();
final HE_Halfedge hePairp = hePair.getPrevInFace();
if (f != null) {
f.setHalfedge(hen);
}
if (fp != null) {
fp.setHalfedge(hePairn);
}
hep.setNext(hen);
hePairp.setNext(hePairn);
remove(he);
remove(hePair);
remove(e);
remove(v);
deleteTwoEdgeFace(f);
deleteTwoEdgeFace(fp);
return true;
}
return false;
}
/**
* Collapse edge bp.
*
* @param e
* the e
* @param strict
* the strict
* @return true, if successful
*/
public boolean collapseEdgeBP(final HE_Edge e, boolean strict) {
if (contains(e)) {
final HE_Halfedge he = e.getHalfedge();
final HE_Halfedge hePair = e.getHalfedge().getPair();
HE_Face f = he.getFace();
HE_Face fp = hePair.getFace();
final HE_Vertex v = he.getVertex();
final HE_Vertex vp = hePair.getVertex();
if (v.isBoundary()) {
if (vp.isBoundary()) {
if ((!e.isBoundary()) || strict)
return false;
vp.add(v).mult(0.5);
} else {
vp.set(v);
}
} else {
if (!vp.isBoundary()) {
vp.add(v).mult(0.5);
}
}
final List<HE_Halfedge> tmp = v.getHalfedgeStar();
for (int i = 0; i < tmp.size(); i++) {
tmp.get(i).setVertex(vp);
}
vp.setHalfedge(hePair.getNextInVertex());
final HE_Halfedge hen = he.getNextInFace();
final HE_Halfedge hep = he.getPrevInFace();
final HE_Halfedge hePairn = hePair.getNextInFace();
final HE_Halfedge hePairp = hePair.getPrevInFace();
if (f != null) {
f.setHalfedge(hen);
}
if (fp != null) {
fp.setHalfedge(hePairn);
}
hep.setNext(hen);
hePairp.setNext(hePairn);
remove(he);
remove(hePair);
remove(e);
remove(v);
deleteTwoEdgeFace(f);
deleteTwoEdgeFace(fp);
return true;
}
return false;
}
/**
* Remove a face if it has only two vertices and stitch the mesh together.
*
* @param f
* face to check
*/
public void deleteTwoEdgeFace(HE_Face f) {
if (contains(f)) {
final HE_Halfedge he = f.getHalfedge();
final HE_Halfedge hen = he.getNextInFace();
if (he == hen.getNextInFace()) {
final HE_Halfedge hePair = he.getPair();
final HE_Halfedge henPair = hen.getPair();
remove(f);
remove(he.getEdge());
remove(he);
he.getVertex().setHalfedge(he.getNextInVertex());
remove(hen);
hen.getVertex().setHalfedge(hen.getNextInVertex());
hePair.setEdge(henPair.getEdge());
henPair.getEdge().setHalfedge(henPair);
hePair.setPair(henPair);
}
}
}
/**
* Fix halfedge vertex assignment.
*/
public void fixHalfedgeVertexAssignment() {
Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
he.getVertex().setHalfedge(he);
}
}
/**
* Collapse all zero-length edges.
*
*/
public void collapseDegenerateEdges() {
final FastList<HE_Edge> edgesToRemove = new FastList<HE_Edge>();
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
while (eItr.hasNext()) {
e = eItr.next();
if (WB_Epsilon.isZeroSq(WB_Distance.sqDistance(e.getStartVertex(),
e.getEndVertex()))) {
edgesToRemove.add(e);
}
}
for (int i = 0; i < edgesToRemove.size(); i++) {
collapseEdge(edgesToRemove.get(i));
}
}
/**
* Delete face and remove all references.
*
* @param f
* face to delete
*/
public void deleteFace(final HE_Face f) {
HE_Halfedge he = f.getHalfedge();
do {
he.clearFace();
he = he.getNextInFace();
} while (he != f.getHalfedge());
remove(f);
}
/**
* Delete edge. Adjacent faces are fused.
*
* @param e
* edge to delete
* @return fused face (or null)
*/
public HE_Face deleteEdge(final HE_Edge e) {
HE_Face f = null;
final HE_Halfedge he1 = e.getHalfedge();
final HE_Halfedge he2 = e.getHalfedge().getPair();
final HE_Halfedge he1n = e.getHalfedge().getNextInFace();
final HE_Halfedge he2n = e.getHalfedge().getPair().getNextInFace();
final HE_Halfedge he1p = e.getHalfedge().getPrevInFace();
final HE_Halfedge he2p = e.getHalfedge().getPair().getPrevInFace();
he1p.setNext(he2n);
he2p.setNext(he1n);
HE_Vertex v = he1.getVertex();
if (v.getHalfedge() == he1) {
v.setHalfedge(he1.getNextInVertex());
}
v = he2.getVertex();
if (v.getHalfedge() == he2) {
v.setHalfedge(he2.getNextInVertex());
}
if ((e.getFirstFace() != null) && (e.getSecondFace() != null)) {
f = new HE_Face();
add(f);
f.setHalfedge(he1p);
HE_Halfedge he = he1p;
do {
he.setFace(f);
he = he.getNextInFace();
} while (he != he1p);
}
if (e.getFirstFace() != null) {
remove(e.getFirstFace());
}
if (e.getSecondFace() != null) {
remove(e.getSecondFace());
}
remove(he1);
remove(he2);
remove(e);
return f;
}
/**
* Insert vertex in edge.
*
* @param edge
* edge to split
* @param v
* position of new vertex
* @return selection of new vertex and new edge
*/
public HE_Selection splitEdge(final HE_Edge edge, final WB_Point3d v) {
final HE_Selection out = new HE_Selection(this);
final HE_Halfedge he0 = edge.getHalfedge();
final HE_Halfedge he1 = he0.getPair();
final HE_Vertex vNew = new HE_Vertex(v);
final HE_Halfedge he0new = new HE_Halfedge();
final HE_Halfedge he1new = new HE_Halfedge();
he0new.setVertex(vNew);
he1new.setVertex(vNew);
vNew.setHalfedge(he0new);
he0new.setNext(he0.getNextInFace());
he1new.setNext(he1.getNextInFace());
he0.setNext(he0new);
he1.setNext(he1new);
he0.setPair(he1new);
he0new.setPair(he1);
final HE_Edge edgeNew = new HE_Edge();
edgeNew.label = edge.label;
edgeNew.setHalfedge(he0new);
he1new.setEdge(edge);
he1.setEdge(edgeNew);
he0new.setEdge(edgeNew);
if (he0.getFace() != null) {
he0new.setFace(he0.getFace());
}
if (he1.getFace() != null) {
he1new.setFace(he1.getFace());
}
vNew.setLabel(1);
add(vNew);
add(he0new);
add(he1new);
add(edgeNew);
out.add(vNew);
out.add(edgeNew);
return out;
}
/**
* Insert vertex in edge.
*
* @param key
* key of edge to split
* @param v
* position of new vertex
* @return selection of new vertex and new edge
*/
public HE_Selection splitEdge(final Integer key, final WB_Point3d v) {
final HE_Edge edge = getEdgeByKey(key);
return splitEdge(edge, v);
}
/**
* Insert vertex in edge.
*
* @param edge
* edge to split
* @param x
* x-coordinate of new vertex
* @param y
* y-coordinate of new vertex
* @param z
* z-coordinate of new vertex
*/
public void splitEdge(final HE_Edge edge, final double x, final double y,
final double z) {
splitEdge(edge, new WB_Point3d(x, y, z));
}
/**
* Insert vertex in edge.
*
* @param key
* key of edge to split
* @param x
* x-coordinate of new vertex
* @param y
* y-coordinate of new vertex
* @param z
* z-coordinate of new vertex
*/
public void splitEdge(final Integer key, final double x, final double y,
final double z) {
splitEdge(key, new WB_Point3d(x, y, z));
}
/**
* Split edge in half.
*
* @param edge
* edge to split.
* @return selection of new vertex and new edge
*/
public HE_Selection splitEdge(final HE_Edge edge) {
final WB_Point3d v = edge.getStartVertex().addAndCopy(
edge.getEndVertex());
v.mult(0.5);
return splitEdge(edge, v);
}
/**
* Split edge in half.
*
* @param key
* key of edge to split.
* @return selection of new vertex and new edge
*/
public HE_Selection splitEdge(final Integer key) {
final HE_Edge edge = getEdgeByKey(key);
final WB_Point3d v = edge.getStartVertex().addAndCopy(
edge.getEndVertex());
v.mult(0.5);
return splitEdge(edge, v);
}
/**
* Split edge in two parts.
*
* @param edge
* edge to split
* @param f
* fraction of first part (0..1)
* @return selection of new vertex and new edge
*/
public HE_Selection splitEdge(final HE_Edge edge, final double f) {
final WB_Point3d v = WB_Point3d.interpolate(edge.getStartVertex(),
edge.getEndVertex(), f);
return splitEdge(edge, v);
}
/**
* Split edge in two parts.
*
* @param key
* key of edge to split
* @param f
* fraction of first part (0..1)
* @return selection of new vertex and new edge
*/
public HE_Selection splitEdge(final Integer key, final double f) {
final HE_Edge edge = getEdgeByKey(key);
return splitEdge(edge, f);
}
/**
* Split all edges in half.
*
* @return selection of new vertices and new edges
*/
public HE_Selection splitEdges() {
final HE_Selection selectionOut = new HE_Selection(this);
final HE_Edge[] edges = getEdgesAsArray();
final int n = numberOfEdges();
for (int i = 0; i < n; i++) {
selectionOut.union(splitEdge(edges[i], 0.5));
}
return selectionOut;
}
/**
* Split all edges in half, offset the center by a given distance along the
* edge normal.
*
* @param offset
* the offset
* @return selection of new vertices and new edges
*/
public HE_Selection splitEdges(final double offset) {
final HE_Selection selectionOut = new HE_Selection(this);
final HE_Edge[] edges = getEdgesAsArray();
final int n = numberOfEdges();
for (int i = 0; i < n; i++) {
final WB_Point3d p = new WB_Point3d(edges[i].getEdgeNormal());
p.mult(offset).add(edges[i].getEdgeCenter());
selectionOut.union(splitEdge(edges[i], p));
}
return selectionOut;
}
/**
* Split edge in half.
*
* @param selection
* edges to split.
* @return selection of new vertices and new edges
*/
public HE_Selection splitEdges(final HE_Selection selection) {
final HE_Selection selectionOut = new HE_Selection(this);
selection.collectEdges();
final Iterator<HE_Edge> eItr = selection.eItr();
while (eItr.hasNext()) {
selectionOut.union(splitEdge(eItr.next(), 0.5));
}
selection.addEdges(selectionOut.getEdgesAsArray());
return selectionOut;
}
/**
* Split edge in half, offset the center by a given distance along the edge
* normal.
*
* @param selection
* edges to split.
* @param offset
* the offset
* @return selection of new vertices and new edges
*/
public HE_Selection splitEdges(final HE_Selection selection,
final double offset) {
final HE_Selection selectionOut = new HE_Selection(this);
selection.collectEdges();
final Iterator<HE_Edge> eItr = selection.eItr();
HE_Edge e;
while (eItr.hasNext()) {
e = eItr.next();
final WB_Point3d p = new WB_Point3d(e.getEdgeNormal());
p.mult(offset).add(e.getEdgeCenter());
selectionOut.union(splitEdge(e, p));
}
selection.addEdges(selectionOut.getEdgesAsArray());
return selectionOut;
}
/**
* Split edge in multiple parts.
*
* @param edge
* edge to split
* @param f
* array of fractions (0..1)
*/
public void splitEdge(final HE_Edge edge, final double[] f) {
final double[] fArray = Arrays.copyOf(f, f.length);
Arrays.sort(fArray);
HE_Edge e = edge;
final HE_Halfedge he0 = edge.getHalfedge();
final HE_Halfedge he1 = he0.getPair();
final HE_Vertex v0 = he0.getVertex();
final HE_Vertex v1 = he1.getVertex();
HE_Vertex v = new HE_Vertex();
for (int i = 0; i < f.length; i++) {
final double fi = fArray[i];
if ((fi > 0) && (fi < 1)) {
v = new HE_Vertex(WB_Point3d.interpolate(v0, v1, fi));
e = (splitEdge(e, v).eItr().next());
}
}
}
/**
* Split edge in multiple parts.
*
* @param key
* key of edge to split
* @param f
* array of fractions (0..1)
*/
public void splitEdge(final Integer key, final double[] f) {
final HE_Edge edge = getEdgeByKey(key);
splitEdge(edge, f);
}
/**
* Split edge in multiple parts.
*
* @param edge
* edge to split
* @param f
* array of fractions (0..1)
*/
public void splitEdge(final HE_Edge edge, final float[] f) {
final float[] fArray = Arrays.copyOf(f, f.length);
Arrays.sort(fArray);
HE_Edge e = edge;
final HE_Halfedge he0 = edge.getHalfedge();
final HE_Halfedge he1 = he0.getPair();
final HE_Vertex v0 = he0.getVertex();
final HE_Vertex v1 = he1.getVertex();
HE_Vertex v = new HE_Vertex();
for (int i = 0; i < f.length; i++) {
final double fi = fArray[i];
if ((fi > 0) && (fi < 1)) {
v = new HE_Vertex(WB_Point3d.interpolate(v0, v1, fi));
e = (splitEdge(e, v).eItr().next());
}
}
}
/**
* Split edge in multiple parts.
*
* @param key
* key of edge to split
* @param f
* array of fractions (0..1)
*/
public void splitEdge(final Integer key, final float[] f) {
final HE_Edge edge = getEdgeByKey(key);
splitEdge(edge, f);
}
/**
* Divide edge.
*
* @param origE
* edge to divide
* @param n
* number of parts
*/
public void divideEdge(final HE_Edge origE, final int n) {
if (n > 1) {
final double[] f = new double[n - 1];
final double in = 1.0 / n;
for (int i = 0; i < n - 1; i++) {
f[i] = (i + 1) * in;
}
splitEdge(origE, f);
}
}
/**
* Divide edge.
*
* @param key
* key of edge to divide
* @param n
* number of parts
*/
public void divideEdge(final Integer key, final int n) {
final HE_Edge edge = getEdgeByKey(key);
divideEdge(edge, n);
}
/**
* Find halfedge shared by vertex and face.
*
* @param f
* face
* @param v
* vertex
* @return halfedge
*/
private HE_Halfedge findHalfedge(final HE_Face f, final HE_Vertex v) {
HE_Halfedge he = f.getHalfedge();
do {
if (he.getVertex() == v) {
return he;
}
he = he.getNextInFace();
} while (he != f.getHalfedge());
return null;
}
/**
* Divide face along two vertices.
*
* @param face
* face to divide
* @param vi
* first vertex
* @param vj
* second vertex
* @return new face and edge
*/
public HE_Selection splitFace(final HE_Face face, final HE_Vertex vi,
final HE_Vertex vj) {
final HE_Selection out = new HE_Selection(this);
final HE_Halfedge hei = findHalfedge(face, vi);
final HE_Halfedge hej = findHalfedge(face, vj);
HE_Halfedge heiPrev;
HE_Halfedge hejPrev;
HE_Halfedge he0new;
HE_Halfedge he1new;
HE_Edge edgeNew;
HE_Face faceNew;
HE_Halfedge he;
if ((hei.getNextInFace() != hej) || (hei.getPrevInFace() != hej)) {
heiPrev = hei.getPrevInFace();
hejPrev = hej.getPrevInFace();
he0new = new HE_Halfedge();
he1new = new HE_Halfedge();
he0new.setVertex(vj);
he1new.setVertex(vi);
he0new.setNext(hei);
he1new.setNext(hej);
heiPrev.setNext(he1new);
hejPrev.setNext(he0new);
he0new.setPair(he1new);
edgeNew = new HE_Edge();
edgeNew.setHalfedge(he0new);
he0new.setEdge(edgeNew);
he1new.setEdge(edgeNew);
he0new.setFace(face);
faceNew = new HE_Face();
face.setHalfedge(hei);
faceNew.setHalfedge(hej);
faceNew.label = face.label;
assignFaceToLoop(faceNew, hej);
add(he0new);
add(he1new);
add(edgeNew);
add(faceNew);
out.add(edgeNew);
out.add(faceNew);
he = face.getHalfedge();
do {
he = he.getNextInFace();
} while (he != face.getHalfedge());
return out;
}
return null;
}
/**
* Divide face along two vertices.
*
* @param fkey
* key of face
* @param vkeyi
* key of first vertex
* @param vkeyj
* key of second vertex
* @return new face and edge
*/
public HE_Selection splitFace(final Integer fkey, final Integer vkeyi,
final Integer vkeyj) {
return splitFace(getFaceByKey(fkey), getVertexByKey(vkeyi),
getVertexByKey(vkeyj));
}
/**
* Tri split face.
*
* @param face
* face
* @param v
* new vertex
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFace(final HE_Face face, final WB_Point3d v) {
HE_Halfedge he = face.getHalfedge();
final HE_Vertex vi = new HE_Vertex(v);
vi.setLabel(2);
final HE_Selection out = new HE_Selection(this);
int c = 0;
boolean onEdge = false;
do {
c++;
final WB_Plane P = new WB_Plane(he.getHalfedgeCenter(),
he.getHalfedgeNormal());
final double d = WB_Distance.distance(v, P);
if (WB_Epsilon.isZero(d)) {
onEdge = true;
break;
}
he = he.getNextInFace();
} while (he != face.getHalfedge());
if (!onEdge) {
add(vi);
final HE_Halfedge[] he0 = new HE_Halfedge[c];
final HE_Halfedge[] he1 = new HE_Halfedge[c];
final HE_Halfedge[] he2 = new HE_Halfedge[c];
c = 0;
do {
HE_Face f;
if (c == 0) {
f = face;
} else {
f = new HE_Face();
f.label = face.label;
add(f);
out.add(f);
}
he0[c] = he;
he.setFace(f);
f.setHalfedge(he);
he1[c] = new HE_Halfedge();
he2[c] = new HE_Halfedge();
add(he1[c]);
add(he2[c]);
he1[c].setVertex(he.getNextInFace().getVertex());
he2[c].setVertex(vi);
he1[c].setNext(he2[c]);
he2[c].setNext(he);
he1[c].setFace(f);
he2[c].setFace(f);
c++;
he = he.getNextInFace();
} while (he != face.getHalfedge());
vi.setHalfedge(he2[0]);
for (int i = 0; i < c; i++) {
he0[i].setNext(he1[i]);
he1[i].setPair(he2[i == c - 1 ? 0 : i + 1]);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(he1[i]);
he1[i].setEdge(e);
he1[i].getPair().setEdge(e);
}
out.add(vi);
return out;
}
return null;
}
/**
* Tri split face.
*
* @param face
* face
* @param x
* x-coordinate of new vertex
* @param y
* y-coordinate of new vertex
* @param z
* z-coordinate of new vertex
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFace(final HE_Face face, final double x,
final double y, final double z) {
return triSplitFace(face, new WB_Point3d(x, y, z));
}
/**
* Tri split face.
*
* @param face
* face
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFace(final HE_Face face) {
return triSplitFace(face, face.getFaceCenter());
}
/**
* Tri split face with offset along face normal.
*
* @param face
* face
* @param d
* offset along face normal
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFace(final HE_Face face, final double d) {
return triSplitFace(face,
face.getFaceCenter().add(face.getFaceNormal(), d));
}
/**
* Tri split faces with offset along face normal.
*
* @param d
* offset along face normal
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFaces(final double d) {
final HE_Selection selectionOut = new HE_Selection(this);
final HE_Face[] faces = getFacesAsArray();
final int n = numberOfFaces();
for (int i = 0; i < n; i++) {
selectionOut.union(triSplitFace(faces[i], d));
}
return selectionOut;
}
/**
* Tri split faces.
*
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFaces() {
final HE_Selection selectionOut = new HE_Selection(this);
final HE_Face[] faces = getFacesAsArray();
final int n = numberOfFaces();
for (int i = 0; i < n; i++) {
selectionOut.union(triSplitFace(faces[i]));
}
return selectionOut;
}
/**
* Tri split faces.
*
* @param selection
* face selection to split
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFaces(final HE_Selection selection) {
final HE_Selection selectionOut = new HE_Selection(this);
final HE_Face[] faces = selection.getFacesAsArray();
final int n = selection.numberOfFaces();
for (int i = 0; i < n; i++) {
selectionOut.union(triSplitFace(faces[i]));
}
selection.union(selectionOut);
return selectionOut;
}
/**
* Tri split faces with offset along face normal.
*
* @param selection
* face selection to split
* @param d
* offset along face normal
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFaces(final HE_Selection selection,
final double d) {
final HE_Selection selectionOut = new HE_Selection(this);
final HE_Face[] faces = selection.getFacesAsArray();
final int n = selection.numberOfFaces();
for (int i = 0; i < n; i++) {
selectionOut.union(triSplitFace(faces[i], d));
}
selection.union(selectionOut);
return selectionOut;
}
/**
* Split face by connecting all face vertices with new vertex.
*
* @param key
* key of face
* @param v
* position of new vertex
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFace(final Integer key, final WB_Point3d v) {
return triSplitFace(getFaceByKey(key), v);
}
/**
* Split face by connecting all face vertices with new vertex.
*
* @param key
* key of face
* @param x
* x-coordinate of new vertex
* @param y
* y-coordinate of new vertex
* @param z
* z-coordinate of new vertex
* @return selection of new faces and new vertex
*/
public HE_Selection triSplitFace(final Integer key, final double x,
final double y, final double z) {
return triSplitFace(getFaceByKey(key), new WB_Point3d(x, y, z));
}
/**
* Quad split faces.
*
* @return selection of new faces and new vertices
*/
public HE_Selection quadSplitFaces() {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = numberOfFaces();
final WB_Point3d[] faceCenters = new WB_Point3d[n];
final int[] faceOrders = new int[n];
HE_Face f;
int i = 0;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
faceCenters[i] = f.getFaceCenter();
faceOrders[i] = f.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges().getVerticesAsArray());
final HE_Face[] faces = getFacesAsArray();
HE_Vertex vi = new HE_Vertex();
for (i = 0; i < n; i++) {
f = faces[i];
vi = new HE_Vertex(faceCenters[i]);
vi.setLabel(2);
add(vi);
selectionOut.add(vi);
HE_Halfedge startHE = f.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he3 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
HE_Face fc;
if (c == 0) {
fc = f;
} else {
fc = new HE_Face();
fc.label = f.label;
add(fc);
}
he0[c] = he;
he.setFace(fc);
fc.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
he3[c] = new HE_Halfedge();
add(he2[c]);
add(he3[c]);
he2[c].setVertex(he.getNextInFace().getNextInFace().getVertex());
he3[c].setVertex(vi);
he2[c].setNext(he3[c]);
he3[c].setNext(he);
he1[c].setFace(fc);
he2[c].setFace(fc);
he3[c].setFace(fc);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
vi.setHalfedge(he3[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
}
}
pairHalfedges();
return selectionOut;
}
/**
* Quad split selected faces.
*
* @param sel
* selection to split
* @return selection of new faces and new vertices
*/
public HE_Selection quadSplitFaces(final HE_Selection sel) {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = sel.numberOfFaces();
final WB_Point3d[] faceCenters = new WB_Point3d[n];
final int[] faceOrders = new int[n];
HE_Face face;
final Iterator<HE_Face> fItr = sel.fItr();
int i = 0;
while (fItr.hasNext()) {
face = fItr.next();
faceCenters[i] = face.getFaceCenter();
faceOrders[i] = face.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(sel.getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges(orig).getVerticesAsArray());
final HE_Face[] faces = sel.getFacesAsArray();
for (i = 0; i < n; i++) {
face = faces[i];
final HE_Vertex vi = new HE_Vertex(faceCenters[i]);
add(vi);
vi.setLabel(2);
selectionOut.add(vi);
HE_Halfedge startHE = face.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he3 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
HE_Face f;
if (c == 0) {
f = face;
} else {
f = new HE_Face();
add(f);
f.label = face.label;
sel.add(f);
}
he0[c] = he;
he.setFace(f);
f.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
he3[c] = new HE_Halfedge();
add(he2[c]);
add(he3[c]);
he2[c].setVertex(he.getNextInFace().getNextInFace().getVertex());
he3[c].setVertex(vi);
he2[c].setNext(he3[c]);
he3[c].setNext(he);
he1[c].setFace(f);
he2[c].setFace(f);
he3[c].setFace(f);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
vi.setHalfedge(he3[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
}
}
pairHalfedges();
return selectionOut;
}
/**
* Hybrid split faces: midsplit for triangles, quad split otherwise.
*
* @return selection of new faces and new vertices
*/
public HE_Selection hybridSplitFaces() {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = numberOfFaces();
final WB_Point3d[] faceCenters = new WB_Point3d[n];
final int[] faceOrders = new int[n];
HE_Face f;
int i = 0;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
faceCenters[i] = f.getFaceCenter();
faceOrders[i] = f.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges().getVerticesAsArray());
final HE_Face[] faces = getFacesAsArray();
HE_Vertex vi = new HE_Vertex();
for (i = 0; i < n; i++) {
f = faces[i];
if (f.getFaceOrder() == 3) {
HE_Halfedge startHE = f.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] hec = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
final HE_Face fn = new HE_Face();
fn.label = f.label;
add(fn);
he0[c] = he;
he.setFace(fn);
fn.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
hec[c] = new HE_Halfedge();
add(he2[c]);
add(hec[c]);
hec[c].setVertex(he.getVertex());
hec[c].setPair(he2[c]);
hec[c].setFace(f);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(hec[c]);
hec[c].setEdge(e);
he2[c].setEdge(e);
he2[c].setVertex(he.getNextInFace().getNextInFace()
.getVertex());
he2[c].setNext(he0[c]);
he1[c].setFace(fn);
he2[c].setFace(fn);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
f.setHalfedge(hec[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
hec[j].setNext(hec[(j + 1) % c]);
}
}
else if (f.getFaceOrder() > 3) {
vi = new HE_Vertex(faceCenters[i]);
vi.setLabel(2);
add(vi);
selectionOut.add(vi);
HE_Halfedge startHE = f.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he3 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
HE_Face fc;
if (c == 0) {
fc = f;
} else {
fc = new HE_Face();
fc.label = f.label;
add(fc);
}
he0[c] = he;
he.setFace(fc);
fc.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
he3[c] = new HE_Halfedge();
add(he2[c]);
add(he3[c]);
he2[c].setVertex(he.getNextInFace().getNextInFace()
.getVertex());
he3[c].setVertex(vi);
he2[c].setNext(he3[c]);
he3[c].setNext(he);
he1[c].setFace(fc);
he2[c].setFace(fc);
he3[c].setFace(fc);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
vi.setHalfedge(he3[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
}
}
}
pairHalfedges();
return selectionOut;
}
/**
* Hybrid split faces: midsplit for triangles, quad split otherwise.
*
* @param sel
* the sel
* @return selection of new faces and new vertices
*/
public HE_Selection hybridSplitFaces(final HE_Selection sel) {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = sel.numberOfFaces();
final WB_Point3d[] faceCenters = new WB_Point3d[n];
final int[] faceOrders = new int[n];
HE_Face f;
int i = 0;
final Iterator<HE_Face> fItr = sel.fItr();
while (fItr.hasNext()) {
f = fItr.next();
faceCenters[i] = f.getFaceCenter();
faceOrders[i] = f.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(sel.getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges().getVerticesAsArray());
final HE_Face[] faces = sel.getFacesAsArray();
HE_Vertex vi = new HE_Vertex();
for (i = 0; i < n; i++) {
f = faces[i];
if (f.getFaceOrder() == 3) {
HE_Halfedge startHE = f.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] hec = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
final HE_Face fn = new HE_Face();
fn.label = f.label;
add(fn);
sel.add(fn);
he0[c] = he;
he.setFace(fn);
fn.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
hec[c] = new HE_Halfedge();
add(he2[c]);
add(hec[c]);
hec[c].setVertex(he.getVertex());
hec[c].setPair(he2[c]);
hec[c].setFace(f);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(hec[c]);
hec[c].setEdge(e);
he2[c].setEdge(e);
he2[c].setVertex(he.getNextInFace().getNextInFace()
.getVertex());
he2[c].setNext(he0[c]);
he1[c].setFace(fn);
he2[c].setFace(fn);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
f.setHalfedge(hec[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
hec[j].setNext(hec[(j + 1) % c]);
}
}
else if (f.getFaceOrder() > 3) {
vi = new HE_Vertex(faceCenters[i]);
vi.setLabel(2);
add(vi);
selectionOut.add(vi);
HE_Halfedge startHE = f.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he3 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
HE_Face fc;
if (c == 0) {
fc = f;
} else {
fc = new HE_Face();
fc.label = f.label;
add(fc);
sel.add(fc);
}
he0[c] = he;
he.setFace(fc);
fc.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
he3[c] = new HE_Halfedge();
add(he2[c]);
add(he3[c]);
he2[c].setVertex(he.getNextInFace().getNextInFace()
.getVertex());
he3[c].setVertex(vi);
he2[c].setNext(he3[c]);
he3[c].setNext(he);
he1[c].setFace(fc);
he2[c].setFace(fc);
he3[c].setFace(fc);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
vi.setHalfedge(he3[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
}
}
}
pairHalfedges();
return selectionOut;
}
/**
* Mid split faces.
*
* @return selection of new faces and new vertices
*/
public HE_Selection midSplitFaces() {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = numberOfFaces();
final int[] faceOrders = new int[n];
HE_Face face;
int i = 0;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
face = fItr.next();
faceOrders[i] = face.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges().getVerticesAsArray());
final HE_Face[] faces = getFacesAsArray();
for (i = 0; i < n; i++) {
face = faces[i];
HE_Halfedge startHE = face.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] hec = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
final HE_Face f = new HE_Face();
f.label = face.label;
add(f);
he0[c] = he;
he.setFace(f);
f.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
hec[c] = new HE_Halfedge();
add(he2[c]);
add(hec[c]);
hec[c].setVertex(he.getVertex());
hec[c].setPair(he2[c]);
hec[c].setFace(face);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(hec[c]);
hec[c].setEdge(e);
he2[c].setEdge(e);
he2[c].setVertex(he.getNextInFace().getNextInFace().getVertex());
he2[c].setNext(he0[c]);
he1[c].setFace(f);
he2[c].setFace(f);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
face.setHalfedge(hec[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
hec[j].setNext(hec[(j + 1) % c]);
}
}
return selectionOut;
}
/**
* Mid split faces.
*
* @return selection of new faces and new vertices
*/
public HE_Selection midSplitFacesHole() {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = numberOfFaces();
final int[] faceOrders = new int[n];
HE_Face face;
int i = 0;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
face = fItr.next();
faceOrders[i] = face.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges().getVerticesAsArray());
final HE_Face[] faces = getFacesAsArray();
for (i = 0; i < n; i++) {
face = faces[i];
HE_Halfedge startHE = face.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] hec = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
final HE_Face f = new HE_Face();
f.label = face.label;
add(f);
he0[c] = he;
he.setFace(f);
f.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
hec[c] = new HE_Halfedge();
add(he2[c]);
add(hec[c]);
hec[c].setVertex(he.getVertex());
hec[c].setPair(he2[c]);
hec[c].setFace(face);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(hec[c]);
hec[c].setEdge(e);
he2[c].setEdge(e);
he2[c].setVertex(he.getNextInFace().getNextInFace().getVertex());
he2[c].setNext(he0[c]);
he1[c].setFace(f);
he2[c].setFace(f);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
face.setHalfedge(hec[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
hec[j].setNext(hec[(j + 1) % c]);
}
deleteFace(face);
}
return selectionOut;
}
/**
* Mid split selected faces.
*
* @param selection
* selection to split
* @return selection of new faces and new vertices
*/
public HE_Selection midSplitFaces(final HE_Selection selection) {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = selection.numberOfFaces();
final int[] faceOrders = new int[n];
HE_Face face;
final Iterator<HE_Face> fItr = selection.fItr();
int i = 0;
while (fItr.hasNext()) {
face = fItr.next();
faceOrders[i] = face.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(selection.getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges(orig).getVerticesAsArray());
final HE_Face[] faces = selection.getFacesAsArray();
for (i = 0; i < n; i++) {
face = faces[i];
HE_Halfedge startHE = face.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] hec = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
final HE_Face f = new HE_Face();
add(f);
f.label = face.label;
selection.add(f);
he0[c] = he;
he.setFace(f);
f.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
hec[c] = new HE_Halfedge();
add(he2[c]);
add(hec[c]);
hec[c].setVertex(he.getVertex());
hec[c].setPair(he2[c]);
hec[c].setFace(face);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(hec[c]);
hec[c].setEdge(e);
he2[c].setEdge(e);
he2[c].setVertex(he.getNextInFace().getNextInFace().getVertex());
he2[c].setNext(he0[c]);
he1[c].setFace(f);
he2[c].setFace(f);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
face.setHalfedge(hec[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
hec[j].setNext(hec[(j + 1) % c]);
}
}
return selectionOut;
}
/**
* Mid split faces hole.
*
* @param selection
* the selection
* @return the h e_ selection
*/
public HE_Selection midSplitFacesHole(final HE_Selection selection) {
final HE_Selection selectionOut = new HE_Selection(this);
final int n = selection.numberOfFaces();
final int[] faceOrders = new int[n];
HE_Face face;
final Iterator<HE_Face> fItr = selection.fItr();
int i = 0;
while (fItr.hasNext()) {
face = fItr.next();
faceOrders[i] = face.getFaceOrder();
i++;
}
final HE_Selection orig = new HE_Selection(this);
orig.addFaces(selection.getFacesAsArray());
orig.collectVertices();
orig.collectEdges();
selectionOut.addVertices(splitEdges(orig).getVerticesAsArray());
final HE_Face[] faces = selection.getFacesAsArray();
for (i = 0; i < n; i++) {
face = faces[i];
HE_Halfedge startHE = face.getHalfedge();
while (orig.contains(startHE.getVertex())) {
startHE = startHE.getNextInFace();
}
HE_Halfedge he = startHE;
final HE_Halfedge[] hec = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he0 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he1 = new HE_Halfedge[faceOrders[i]];
final HE_Halfedge[] he2 = new HE_Halfedge[faceOrders[i]];
int c = 0;
do {
final HE_Face f = new HE_Face();
add(f);
f.label = face.label;
selection.add(f);
he0[c] = he;
he.setFace(f);
f.setHalfedge(he);
he1[c] = he.getNextInFace();
he2[c] = new HE_Halfedge();
hec[c] = new HE_Halfedge();
add(he2[c]);
add(hec[c]);
hec[c].setVertex(he.getVertex());
hec[c].setPair(he2[c]);
hec[c].setFace(face);
final HE_Edge e = new HE_Edge();
add(e);
e.setHalfedge(hec[c]);
hec[c].setEdge(e);
he2[c].setEdge(e);
he2[c].setVertex(he.getNextInFace().getNextInFace().getVertex());
he2[c].setNext(he0[c]);
he1[c].setFace(f);
he2[c].setFace(f);
c++;
he = he.getNextInFace().getNextInFace();
} while (he != startHE);
face.setHalfedge(hec[0]);
for (int j = 0; j < c; j++) {
he1[j].setNext(he2[j]);
hec[j].setNext(hec[(j + 1) % c]);
}
deleteFace(face);
}
return selectionOut;
}
/**
* Triangulate all concave faces.
*
*/
public void triangulateConcaveFaces() {
final HE_Face[] f = getFacesAsArray();
final int n = numberOfFaces();
for (int i = 0; i < n; i++) {
if (f[i].getFaceType() == WB_PolygonType2D.CONCAVE) {
triangulate(f[i].key());
}
}
}
/**
* Triangulate face if concave.
*
* @param key
* key of face
*/
public void triangulateConcaveFace(final Integer key) {
triangulateConcaveFace(getFaceByKey(key));
}
/**
* Triangulate face if concave.
*
* @param face
* key of face
*/
public void triangulateConcaveFace(final HE_Face face) {
if (face.getFaceType() == WB_PolygonType2D.CONCAVE) {
triangulate(face);
}
}
/**
* Expand vertex to new edge.
*
* @param v
* vertex to expand
* @param f1
* first face
* @param f2
* second face
* @param vn
* position of new vertex
*/
public void expandVertexToEdge(final HE_Vertex v, final HE_Face f1,
final HE_Face f2, final WB_Point3d vn) {
HE_Halfedge he = v.getHalfedge();
HE_Halfedge he1 = new HE_Halfedge();
HE_Halfedge he2 = new HE_Halfedge();
do {
if (he.getFace() == f1) {
he1 = he;
}
if (he.getFace() == f2) {
he2 = he;
}
he = he.getNextInVertex();
} while (he != v.getHalfedge());
final HE_Vertex vNew = new HE_Vertex(vn);
vNew.setHalfedge(he1);
add(vNew);
he = he1;
do {
he.setVertex(vNew);
he = he.getNextInVertex();
} while (he != he2);
final HE_Halfedge he1p = he1.getPrevInFace();
final HE_Halfedge he2p = he2.getPrevInFace();
final HE_Halfedge he1new = new HE_Halfedge();
final HE_Halfedge he2new = new HE_Halfedge();
add(he1new);
add(he2new);
he1new.setVertex(v);
he2new.setVertex(vNew);
he1p.setNext(he1new);
he1new.setNext(he1);
he2p.setNext(he2new);
he2new.setNext(he2);
he1new.setPair(he2new);
he1new.setFace(f1);
he2new.setFace(f2);
final HE_Edge eNew = new HE_Edge();
add(eNew);
eNew.setHalfedge(he1new);
he1new.setEdge(eNew);
he2new.setEdge(eNew);
}
/**
* Check consistency of datastructure.
*
* @param verbose
* true: print to console, HE.SILENT: no output
* @param force
* true: full scan, HE.BREAK: stop on first error
* @return true or false
*/
public boolean validate(final boolean verbose, final boolean force) {
boolean result = true;
if (verbose == true) {
System.out.println("Checking face (" + numberOfFaces()
+ ") properties");
}
HE_Face face;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
face = fItr.next();
if (face.getHalfedge() == null) {
if (verbose == true) {
System.out.println("Null reference in face " + face.key()
+ ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(face.getHalfedge())) {
if (verbose == true) {
System.out.println("External reference in face "
+ face.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (face.getHalfedge().getFace() != null) {
if (face.getHalfedge().getFace() != face) {
if (verbose == true) {
System.out.println("Wrong reference in face "
+ face.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
}
}
if (verbose == true) {
System.out.println("Checking vertex (" + numberOfVertices()
+ ") properties");
}
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
if (v.getHalfedge() == null) {
if (verbose == true) {
System.out.println("Null reference in vertex " + v.key()
+ ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(v.getHalfedge())) {
if (verbose == true) {
System.out.println("External reference in vertex "
+ v.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
if (v.getHalfedge().getVertex() != null) {
if (v.getHalfedge().getVertex() != v) {
if (verbose == true) {
System.out.println("Wrong reference in vertex "
+ v.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
}
if (verbose == true) {
System.out.println("Checking edge (" + numberOfEdges()
+ ") properties");
}
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
while (eItr.hasNext()) {
e = eItr.next();
if (e.getHalfedge() == null) {
if (verbose == true) {
System.out.println("Null reference in edge " + e.key()
+ ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(e.getHalfedge())) {
if (verbose == true) {
System.out.println("External reference in edge "
+ e.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
if (e.getHalfedge().getEdge() != null) {
if (e.getHalfedge().getEdge() != e) {
if (verbose == true) {
System.out.println("Wrong reference in edge "
+ e.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
}
if (verbose == true) {
System.out.println("Checking half edge (" + numberOfHalfedges()
+ ") properties");
}
HE_Halfedge he;
final Iterator<HE_Halfedge> heItr = heItr();
while (heItr.hasNext()) {
he = heItr.next();
if (he.getNextInFace() == null) {
if (verbose == true) {
System.out.println("Null reference (next) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(he.getNextInFace())) {
if (verbose == true) {
System.out
.println("External reference (next) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
if ((he.getFace() != null)
&& (he.getNextInFace().getFace() != null)) {
if (he.getFace() != he.getNextInFace().getFace()) {
if (verbose == true) {
System.out
.println("Incosistent reference (face) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
if (he.getPair() == null) {
if (verbose == true) {
System.out.println("Null reference (pair) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(he.getPair())) {
if (verbose == true) {
System.out
.println("External reference (pair) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
if (he.getPair().getPair() == null) {
if (verbose == true) {
System.out
.println("No pair reference back to half edge "
+ he.key() + ".");
}
} else {
if (he.getPair().getPair() != he) {
if (verbose == true) {
System.out
.println("Wrong pair reference back to half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
if ((he.getEdge() != null) && (he.getPair().getEdge() != null)) {
if (he.getEdge() != he.getPair().getEdge()) {
if (verbose == true) {
System.out
.println("Inconsistent reference (edge) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
if ((he.getNextInFace() != null) && (he.getPair() != null)) {
if ((he.getNextInFace().getVertex() != null)
&& (he.getPair().getVertex() != null)) {
if (he.getNextInFace().getVertex() != he.getPair()
.getVertex()) {
if (verbose == true) {
System.out
.println("Inconsistent reference (pair)/(next) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
if (he.getFace() == null) {
if (verbose == true) {
System.out.println("Null reference (face) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(he.getFace())) {
if (verbose == true) {
System.out
.println("External reference (face) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
if (he.getVertex() == null) {
if (verbose == true) {
System.out.println("Null reference (vert) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(he.getVertex())) {
if (verbose == true) {
System.out
.println("External reference (vert) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
if (he.getEdge() == null) {
if (verbose == true) {
System.out.println("Null reference (edge) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
} else {
if (!contains(he.getEdge())) {
if (verbose == true) {
System.out
.println("External reference (edge) in half edge "
+ he.key() + ".");
}
if (force == true) {
result = false;
} else {
return false;
}
}
}
}
if (verbose == true) {
System.out.println("Validation complete!");
}
return result;
}
/**
* Check if point lies inside mesh.
*
* @param p
* point to check
* @param isConvex
* do fast check, convex meshes only
* @return true or false
*/
public boolean contains(final WB_Point3d p, final boolean isConvex) {
final WB_Point3d dir = new WB_Point3d(Math.random() - 0.5,
Math.random() - 0.5, Math.random() - 0.5);
final WB_Ray R = new WB_Ray(p, dir);
int c = 0;
WB_Plane P;
WB_IntersectionResult lpi;
HE_Face face;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
face = fItr.next();
P = face.toPlane();
if (isConvex) {
if (P.classifyPointToPlane(p) == WB_ClassifyPointToPlane.POINT_BEHIND_PLANE) {
return false;
}
} else {
lpi = WB_Intersection.getIntersection(R, P);
if (lpi.intersection) {
if (pointIsInFace((WB_Point3d) lpi.object, face)) {
if (!HE_Mesh.pointIsStrictlyInFace(
(WB_Point3d) lpi.object, face)) {
return contains(p, isConvex);
}
c++;
}
}
}
}
return (isConvex) ? true : (c % 2 == 1);
}
/**
* Check if point lies inside or on edge of face.
*
* @param p
* point
* @param f
* the f
* @return true/false
*/
public static boolean pointIsInFace(final WB_Point3d p, final HE_Face f) {
return WB_Epsilon.isZeroSq(WB_Distance.sqDistance(p,
WB_Intersection.closestPoint(p, f.toPolygon())));
}
/**
* Check if point lies strictly inside face.
*
* @param p
* point
* @param f
* the f
* @return true/false
*/
public static boolean pointIsStrictlyInFace(final WB_Point3d p,
final HE_Face f) {
final WB_ExplicitPolygon poly = f.toPolygon();
final List<WB_IndexedTriangle> tris = poly.triangulate();
if (!WB_Epsilon.isZeroSq(WB_Distance.sqDistance(p,
WB_Intersection.closestPoint(p, tris)))) {
return false;
}
if (WB_Epsilon.isZeroSq(WB_Distance.sqDistance(p,
WB_Intersection.closestPointOnPeriphery(p, poly, tris)))) {
return false;
}
return true;
}
/**
* Fit in aabb.
*
* @param AABB
* the aabb
*/
public void fitInAABB(final WB_AABB3D AABB) {
final WB_AABB3D self = getAABB();
move(AABB.getMin().subToVector(self.getMin()));
scale(AABB.getWidth() / self.getWidth(),
AABB.getHeight() / self.getHeight(),
AABB.getDepth() / self.getDepth(), AABB.getMin());
}
/**
* Fit in aabb constrained.
*
* @param AABB
* the aabb
*/
public double fitInAABBConstrained(final WB_AABB3D AABB) {
final WB_AABB3D self = getAABB();
move(AABB.getCenter().subToVector(self.getCenter()));
double f = Math.min(AABB.getWidth() / self.getWidth(), AABB.getHeight()
/ self.getHeight());
f = Math.min(f, AABB.getDepth() / self.getDepth());
scale(f, AABB.getCenter());
return f;
}
/**
* Delete face and remove all references.
*
* @param faces
* faces to delete
*/
public void delete(final HE_Selection faces) {
HE_Face f;
final Iterator<HE_Face> fItr = faces.fItr();
while (fItr.hasNext()) {
f = fItr.next();
remove(f);
}
cleanUnusedElementsByFace();
capHalfedges();
}
/**
* Select all faces.
*
* @return the h e_ selection
*/
public HE_Selection selectAllFaces() {
final HE_Selection _selection = new HE_Selection(this);
_selection.addFaces(getFacesAsArray());
return _selection;
}
/**
* Select all faces with given label.
*
* @param label
* the label
* @return the h e_ selection
*/
public HE_Selection selectFaces(final int label) {
final HE_Selection _selection = new HE_Selection(this);
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
if (f.getLabel() == label) {
_selection.add(f);
}
}
return _selection;
}
/**
* Select faces.
*
* @param v
* the v
* @return the h e_ selection
*/
public HE_Selection selectFaces(final WB_Vector3d v) {
final HE_Selection _selection = new HE_Selection(this);
final WB_Vector3d w = v.get();
w.normalize();
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
if (f.getFaceNormal().dot(v) > (1.0 - WB_Epsilon.EPSILON)) {
_selection.add(f);
}
}
return _selection;
}
/**
* Select faces.
*
* @param P
* the p
* @return the h e_ selection
*/
public HE_Selection selectFaces(final WB_Plane P) {
final HE_Selection _selection = new HE_Selection(this);
_selection.addFaces(HE_Intersection.getPotentialIntersectedFaces(this,
P));
return _selection;
}
/**
* Select all faces except with given label.
*
* @param label
* the label
* @return the h e_ selection
*/
public HE_Selection selectOtherFaces(final int label) {
final HE_Selection _selection = new HE_Selection(this);
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
if (f.getLabel() != label) {
_selection.add(f);
}
}
return _selection;
}
/**
* Select all edges.
*
* @return the h e_ selection
*/
public HE_Selection selectAllEdges() {
final HE_Selection _selection = new HE_Selection(this);
_selection.addEdges(getEdgesAsArray());
return _selection;
}
/**
* Select all halfedges.
*
* @return the h e_ selection
*/
public HE_Selection selectAllHalfedges() {
final HE_Selection _selection = new HE_Selection(this);
_selection.addHalfedges(getHalfedgesAsArray());
return _selection;
}
/**
* Select all vertices.
*
* @return the h e_ selection
*/
public HE_Selection selectAllVertices() {
final HE_Selection _selection = new HE_Selection(this);
_selection.addVertices(getVerticesAsArray());
return _selection;
}
/**
* Select all vertices with given label.
*
* @param label
* the label
* @return the h e_ selection
*/
public HE_Selection selectVertices(final int label) {
final HE_Selection _selection = new HE_Selection(this);
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
if (v.getLabel() == label) {
_selection.add(v);
}
}
return _selection;
}
/**
* Select all vertices except with given label.
*
* @param label
* the label
* @return the h e_ selection
*/
public HE_Selection selectOtherVertices(final int label) {
final HE_Selection _selection = new HE_Selection(this);
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
if (v.getLabel() != label) {
_selection.add(v);
}
}
return _selection;
}
/**
* Select all halfedges on inside of boundary.
*
* @return the h e_ selection
*/
public HE_Selection selectAllInnerBoundaryHalfedges() {
final HE_Selection _selection = new HE_Selection(this);
final Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
if (he.getPair().getFace() == null) {
_selection.add(he);
}
}
return _selection;
}
/**
* Select all halfedges on outside of boundary.
*
* @return the h e_ selection
*/
public HE_Selection selectAllOuterBoundaryHalfedges() {
final HE_Selection _selection = new HE_Selection(this);
final Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
if (he.getFace() == null) {
_selection.add(he);
}
}
return _selection;
}
/**
* Select all edges on boundary.
*
* @return the h e_ selection
*/
public HE_Selection selectAllBoundaryEdges() {
final HE_Selection _selection = new HE_Selection(this);
final Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
if (he.getFace() == null) {
_selection.add(he.getEdge());
}
}
return _selection;
}
/**
* Select all faces on boundary.
*
* @return the h e_ selection
*/
public HE_Selection selectAllBoundaryFaces() {
final HE_Selection _selection = new HE_Selection(this);
final Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
if (he.getFace() == null) {
_selection.add(he.getPair().getFace());
}
}
return _selection;
}
/**
* Select all vertices on boundary.
*
* @return the h e_ selection
*/
public HE_Selection selectAllBoundaryVertices() {
final HE_Selection _selection = new HE_Selection(this);
final Iterator<HE_Halfedge> heItr = heItr();
HE_Halfedge he;
while (heItr.hasNext()) {
he = heItr.next();
if (he.getFace() == null) {
_selection.add(he.getVertex());
}
}
return _selection;
}
/**
* Fuse all coplanar faces connected to face. New face can be concave.
*
* @param face
* starting face
* @param a
* the a
* @return new face
*/
public HE_Face fuseCoplanarFace(final HE_Face face, final double a) {
List<HE_Face> neighbors;
FastTable<HE_Face> facesToCheck = new FastTable<HE_Face>();
final FastTable<HE_Face> newFacesToCheck = new FastTable<HE_Face>();
facesToCheck.add(face);
final HE_Selection sel = new HE_Selection(this);
sel.add(face);
HE_Face f;
HE_Face fn;
int ni = -1;
int nf = 0;
double sa = Math.sin(a);
sa *= sa;
while (ni < nf) {
newFacesToCheck.clear();
for (int i = 0; i < facesToCheck.size(); i++) {
f = facesToCheck.get(i);
neighbors = f.getNeighborFaces();
for (int j = 0; j < neighbors.size(); j++) {
fn = neighbors.get(j);
if (!sel.contains(fn)) {
if (f.getFaceNormal()
.isParallel(fn.getFaceNormal(), sa)) {
sel.add(fn);
newFacesToCheck.add(fn);
}
}
}
}
facesToCheck = newFacesToCheck;
ni = nf;
nf = sel.numberOfFaces();
}
if (sel.numberOfFaces() == 1) {
return face;
}
final List<HE_Halfedge> halfedges = sel.getOuterHalfedgesInside();
final HE_Face newFace = new HE_Face();
add(newFace);
newFace.setHalfedge(halfedges.get(0));
for (int i = 0; i < halfedges.size(); i++) {
final HE_Halfedge hei = halfedges.get(i);
final HE_Halfedge hep = halfedges.get(i).getPair();
for (int j = 0; j < halfedges.size(); j++) {
final HE_Halfedge hej = halfedges.get(j);
if ((i != j) && (hep.getVertex() == hej.getVertex())) {
hei.setNext(hej);
}
}
hei.setFace(newFace);
hei.getVertex().setHalfedge(hei);
}
removeFaces(sel.getFacesAsArray());
cleanUnusedElementsByFace();
return newFace;
}
/**
* Fuse all planar faces. Can lead to concave faces.
*
*/
public void fuseCoplanarFaces() {
fuseCoplanarFaces(0);
}
/**
* Fuse all planar faces. Can lead to concave faces.
*
* @param a
* the a
*/
public void fuseCoplanarFaces(final double a) {
final List<HE_Face> faces = this.getFacesAsList();
for (int i = 0; i < faces.size(); i++) {
final HE_Face f = faces.get(i);
if (contains(f)) {
fuseCoplanarFace(f, a);
}
}
}
/**
* Remove all redundant vertices in straight edges.
*
*/
public void removeColinearVertices() {
final Iterator<HE_Vertex> vItr = vItr();
HE_Vertex v;
HE_Halfedge he;
while (vItr.hasNext()) {
v = vItr.next();
if (v.getVertexOrder() == 2) {
he = v.getHalfedge();
if (he.getHalfedgeTangent().isParallel(
he.getNextInVertex().getHalfedgeTangent())) {
he.getPrevInFace().setNext(he.getNextInFace());
he.getPair().getPrevInFace()
.setNext(he.getPair().getNextInFace());
he.getPair().getNextInFace()
.setVertex(he.getNextInFace().getVertex());
if (he.getFace() != null) {
if (he.getFace().getHalfedge() == he) {
he.getFace().setHalfedge(he.getNextInFace());
}
}
if (he.getPair().getFace() != null) {
if (he.getPair().getFace().getHalfedge() == he
.getPair()) {
he.getPair().getFace()
.setHalfedge(he.getPair().getNextInFace());
}
}
vItr.remove();
remove(he);
remove(he.getPair());
remove(he.getEdge());
}
}
}
}
/**
* Reset labels.
*/
public void resetLabels() {
resetVertexLabels();
resetFaceLabels();
resetEdgeLabels();
}
/**
* Reset vertex labels.
*/
public void resetVertexLabels() {
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
vItr.next().setLabel(-1);
}
}
/**
* Reset face labels.
*/
public void resetFaceLabels() {
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
fItr.next().setLabel(-1);
}
}
/**
* Reset edge labels.
*/
public void resetEdgeLabels() {
final Iterator<HE_Edge> eItr = eItr();
while (eItr.hasNext()) {
eItr.next().setLabel(-1);
}
}
/**
* Label all faces of a selection.
*
* @param sel
* selection
* @param label
* label to use
*/
public void labelFaceSelection(final HE_Selection sel, final int label) {
final Iterator<HE_Face> fItr = sel.fItr();
while (fItr.hasNext()) {
fItr.next().label = label;
}
}
/**
* Update selection to include all face swith given label.
*
* @param sel
* selection to update
* @param label
* label to search
*/
public void updateFaceSelection(final HE_Selection sel, final int label) {
final Iterator<HE_Face> fItr = fItr();
HE_Face f;
sel.clear();
while (fItr.hasNext()) {
f = fItr.next();
if (f.label == label) {
sel.add(f);
}
}
}
/**
* Label edge selection.
*
* @param sel
* the sel
* @param label
* the label
*/
public void labelEdgeSelection(final HE_Selection sel, final int label) {
final Iterator<HE_Edge> eItr = sel.eItr();
while (eItr.hasNext()) {
eItr.next().label = label;
}
}
/**
* Update edge selection.
*
* @param sel
* the sel
* @param label
* the label
*/
public void updateEdgeSelection(final HE_Selection sel, final int label) {
final Iterator<HE_Edge> eItr = eItr();
HE_Edge e;
sel.clear();
while (eItr.hasNext()) {
e = eItr.next();
if (e.label == label) {
sel.add(e);
}
}
}
/**
* Label vertex selection.
*
* @param sel
* the sel
* @param label
* the label
*/
public void labelVertexSelection(final HE_Selection sel, final int label) {
final Iterator<HE_Vertex> vItr = sel.vItr();
while (vItr.hasNext()) {
vItr.next().label = label;
}
}
/**
* Update vertex selection.
*
* @param sel
* the sel
* @param label
* the label
*/
public void updateVertexSelection(final HE_Selection sel, final int label) {
final Iterator<HE_Vertex> vItr = vItr();
HE_Vertex v;
sel.clear();
while (vItr.hasNext()) {
v = vItr.next();
if (v.label == label) {
sel.add(v);
}
}
}
/**
* Return a KD-tree containing all face centers.
*
* @return WB_KDTree
*/
public WB_KDTree3Dold<Integer> getFaceTree() {
final WB_KDTree3Dold<Integer> tree = new WB_KDTree3Dold<Integer>();
HE_Face f;
final Iterator<HE_Face> fItr = fItr();
while (fItr.hasNext()) {
f = fItr.next();
tree.put(f.getFaceCenter(), f.key());
}
return tree;
}
/**
* Return a KD-tree containing all vertices.
*
* @return WB_KDTree
*/
public WB_KDTree3Dold<Integer> getVertexTree() {
final WB_KDTree3Dold<Integer> tree = new WB_KDTree3Dold<Integer>();
HE_Vertex v;
final Iterator<HE_Vertex> vItr = vItr();
while (vItr.hasNext()) {
v = vItr.next();
tree.put(v, v.key());
}
return tree;
}
/**
* Return the closest vertex on the mesh.
*
* @param p
* query point
* @param vertexTree
* KD-tree from mesh (from vertexTree())
* @return HE_Vertex closest vertex
*/
public HE_Vertex getClosestVertex(final WB_Point3d p,
final WB_KDTree3Dold<Integer> vertexTree) {
final WB_KDNeighbor<Integer>[] closestVertex = vertexTree
.getNearestNeighbors(p, 1);
if (closestVertex.length == 0) {
return null;
}
return getVertexByKey(closestVertex[0].value());
}
/**
* Return the closest point on the mesh.
*
* @param p
* query point
* @param vertexTree
* KD-tree from mesh (from vertexTree())
* @return WB_Point closest point
*/
public WB_Point3d getClosestPoint(final WB_Point3d p,
final WB_KDTree3Dold<Integer> vertexTree) {
final WB_KDNeighbor<Integer>[] closestVertex = vertexTree
.getNearestNeighbors(p, 1);
final HE_Vertex v = getVertexByKey(closestVertex[0].value());
if (v == null) {
return null;
}
final List<HE_Face> faces = v.getFaceStar();
double d;
double dmin = Double.POSITIVE_INFINITY;
WB_Point3d result = new WB_Point3d();
for (int i = 0; i < faces.size(); i++) {
final WB_ExplicitPolygon poly = faces.get(i).toPolygon();
final WB_Point3d tmp = WB_Intersection.closestPoint(p, poly);
d = WB_Distance.sqDistance(tmp, p);
if (d < dmin) {
dmin = d;
result = tmp;
}
}
return result;
}
/**
* Split the closest face in the query point.
*
* @param p
* query point
* @param vertexTree
* KD-tree from mesh (from vertexTree())
*/
public void addPointInClosestFace(final WB_Point3d p,
final WB_KDTree3Dold<Integer> vertexTree) {
final WB_KDNeighbor<Integer>[] closestVertex = vertexTree
.getNearestNeighbors(p, 1);
final HE_Vertex v = getVertexByKey(closestVertex[0].value());
final List<HE_Face> faces = v.getFaceStar();
double d;
double dmin = Double.POSITIVE_INFINITY;
HE_Face face = new HE_Face();
for (int i = 0; i < faces.size(); i++) {
final WB_ExplicitPolygon poly = faces.get(i).toPolygon();
final WB_Point3d tmp = WB_Intersection.closestPoint(p, poly);
d = WB_Distance.sqDistance(tmp, p);
if (d < dmin) {
dmin = d;
face = faces.get(i);
;
}
}
final HE_Vertex nv = triSplitFace(face, p).vItr().next();
vertexTree.put(nv, nv.key());
}
/**
* Get all faces shared between two vertices.
*
* @param v1
* the v1
* @param v2
* the v2
* @return shared faces as FastList<HE_Face>
*/
public List<HE_Face> getSharedFaces(final HE_Vertex v1, final HE_Vertex v2) {
final List<HE_Face> result = v1.getFaceStar();
final List<HE_Face> compare = v2.getFaceStar();
final Iterator<HE_Face> it = result.iterator();
while (it.hasNext()) {
if (!compare.contains(it.next())) {
it.remove();
}
}
return result;
}
/**
* Gets the boundary as polygons.
*
* @return the boundary as polygons
*/
public List<WB_ExplicitPolygon> getBoundaryAsPolygons() {
final List<WB_ExplicitPolygon> polygons = new FastList<WB_ExplicitPolygon>();
final List<HE_Halfedge> halfedges = getBoundaryHalfedges();
final List<HE_Halfedge> loop = new FastList<HE_Halfedge>();
final List<WB_Point3d> points = new FastList<WB_Point3d>();
while (halfedges.size() > 0) {
points.clear();
loop.clear();
HE_Halfedge he = halfedges.get(0);
do {
loop.add(he);
points.add(he.getVertex());
he = he.getNextInFace();
if (loop.contains(he)) {
break;
}
} while (he != halfedges.get(0));
polygons.add(new WB_ExplicitPolygon(points));
halfedges.removeAll(loop);
}
return polygons;
}
/**
* Gets the boundary loop halfedges.
*
* @return the boundary loop halfedges
*/
public List<HE_Halfedge> getBoundaryLoopHalfedges() {
final List<HE_Halfedge> hes = new FastList<HE_Halfedge>();
final List<HE_Halfedge> halfedges = getBoundaryHalfedges();
final List<HE_Halfedge> loop = new FastList<HE_Halfedge>();
while (halfedges.size() > 0) {
loop.clear();
HE_Halfedge he = halfedges.get(0);
hes.add(he);
do {
loop.add(he);
he = he.getNextInFace();
if (loop.contains(he)) {
break;
}
} while (he != halfedges.get(0));
halfedges.removeAll(loop);
}
return hes;
}
/**
* Try to identify and correct corner and edge welds. Can occur when
* combining meshes joined at a single vertex or edge. Needs two passes to
* complete.
*/
public void resolvePinchPoints() {
Iterator<HE_Vertex> vItr = vItr();
Iterator<HE_Halfedge> heItr;
HE_Vertex v;
HE_Halfedge he;
boolean pinchFound;
final FastList<HE_Halfedge> vHalfedges = new FastList<HE_Halfedge>();
int run = 0;
do {
vItr = vItr();
pinchFound = false;
run++;
// System.out.println("HE_Mesh, trying to resolve pinch points pass "
// + run + ".");
while (vItr.hasNext()) {
v = vItr.next();
heItr = heItr();
vHalfedges.clear();
while (heItr.hasNext()) {
he = heItr.next();
if (he.getVertex() == v) {
vHalfedges.add(he);
}
}
final List<HE_Halfedge> vStar = v.getHalfedgeStar();
if (vStar.size() != vHalfedges.size()) {
pinchFound = true;
final HE_Vertex vc = new HE_Vertex(v);
add(vc);
for (int i = 0; i < vStar.size(); i++) {
vStar.get(i).setVertex(vc);
}
vc.setHalfedge(vStar.get(0));
for (int i = 0; i < vHalfedges.size(); i++) {
he = vHalfedges.get(i);
if (he.getVertex() == v) {
v.setHalfedge(he);
break;
}
}
}
}
} while (pinchFound && run < 10);
}
/**
* Gets the area.
*
* @return the area
*/
public double getArea() {
final Iterator<HE_Face> fItr = fItr();
double A = 0.0;
while (fItr.hasNext()) {
A += fItr.next().getFaceArea();
}
return A;
}
/**
* Triangulate face.
*
* @param key
* key of face
*/
public void triangulate(final Integer key) {
triangulate(getFaceByKey(key));
}
/**
* Triangulate.
*
* @param face
* the face
*/
public void triangulate(final HE_Face face) {
if (face.getFaceOrder() > 3) {
final List<WB_IndexedTriangle2D> tris = face.triangulate();
final List<HE_Vertex> vertices = face.getFaceVertices();
HE_Halfedge he = face.getHalfedge();
remove(face);
do {
he.getPair().clearEdge();
he.getPair().clearPair();
remove(he.getEdge());
remove(he);
he = he.getNextInFace();
} while (he != face.getHalfedge());
for (int i = 0; i < tris.size(); i++) {
final WB_IndexedTriangle2D tri = tris.get(i);
final HE_Face f = new HE_Face();
add(f);
f.setLabel(face.getLabel());
final HE_Halfedge he1 = new HE_Halfedge();
final HE_Halfedge he2 = new HE_Halfedge();
final HE_Halfedge he3 = new HE_Halfedge();
he1.setVertex(vertices.get(tri.i1));
he2.setVertex(vertices.get(tri.i2));
he3.setVertex(vertices.get(tri.i3));
he1.getVertex().setHalfedge(he1);
he2.getVertex().setHalfedge(he2);
he3.getVertex().setHalfedge(he3);
he1.setFace(f);
he2.setFace(f);
he3.setFace(f);
he1.setNext(he2);
he2.setNext(he3);
he3.setNext(he1);
f.setHalfedge(he1);
add(he1);
add(he2);
add(he3);
}
pairHalfedges();
}
}
/**
* Triangulate no pairing.
*
* @param face
* the face
*/
private void triangulateNoPairing(final HE_Face face) {
if (face.getFaceOrder() > 3) {
final List<WB_IndexedTriangle2D> tris = face.triangulate();
final List<HE_Vertex> vertices = face.getFaceVertices();
HE_Halfedge he = face.getHalfedge();
remove(face);
do {
if (he.getPair() != null) {
he.getPair().clearEdge();
he.getPair().clearPair();
}
if (he.getEdge() != null) {
remove(he.getEdge());
}
remove(he);
he = he.getNextInFace();
} while (he != face.getHalfedge());
for (int i = 0; i < tris.size(); i++) {
final WB_IndexedTriangle2D tri = tris.get(i);
final HE_Face f = new HE_Face();
add(f);
f.setLabel(face.getLabel());
final HE_Halfedge he1 = new HE_Halfedge();
final HE_Halfedge he2 = new HE_Halfedge();
final HE_Halfedge he3 = new HE_Halfedge();
he1.setVertex(vertices.get(tri.i1));
he2.setVertex(vertices.get(tri.i2));
he3.setVertex(vertices.get(tri.i3));
he1.getVertex().setHalfedge(he1);
he2.getVertex().setHalfedge(he2);
he3.getVertex().setHalfedge(he3);
he1.setFace(f);
he2.setFace(f);
he3.setFace(f);
he1.setNext(he2);
he2.setNext(he3);
he3.setNext(he1);
f.setHalfedge(he1);
add(he1);
add(he2);
add(he3);
}
}
}
/**
* Triangulate all faces.
*
*/
public void triangulate() {
final HE_Face[] f = getFacesAsArray();
final int n = numberOfFaces();
for (int i = 0; i < n; i++) {
triangulateNoPairing(f[i]);
}
pairHalfedges();
}
/**
* Triangulate.
*
* @param sel
* the sel
*/
public void triangulate(final HE_Selection sel) {
final HE_Face[] f = sel.getFacesAsArray();
final int n = sel.numberOfFaces();
for (int i = 0; i < n; i++) {
triangulateNoPairing(f[i]);
}
pairHalfedges();
}
/**
* Clean.
*/
public void clean() {
WB_ExplicitPolygon[] polygons = getPolygons();
HEC_FromPolygons creator = new HEC_FromPolygons();
creator.setPolygons(polygons);
set(creator.create());
}
/*
* (non-Javadoc)
*
* @see wblut.core.WB_HasData#setData(java.lang.String, java.lang.Object)
*/
public void setData(final String s, final Object o) {
if (_data == null) {
_data = new HashMap<String, Object>();
}
_data.put(s, o);
}
/*
* (non-Javadoc)
*
* @see wblut.core.WB_HasData#getData(java.lang.String)
*/
public Object getData(final String s) {
return _data.get(s);
}
/**
* Smooth.
*/
public void smooth() {
subdivide(new HES_CatmullClark());
}
/**
* Smooth.
*
* @param rep
* the rep
*/
public void smooth(int rep) {
subdivide(new HES_CatmullClark(), rep);
}
/**
* Fix loops.
*/
public void fixLoops() {
for (HE_Halfedge he : getHalfedgesAsList()) {
if (he.getPrevInFace() == null) {
HE_Halfedge hen = he.getNextInFace();
while (hen.getNextInFace() != he) {
hen = hen.getNextInFace();
}
hen.setNext(he);
}
}
}
}