private void processSiteEvent(final Event newSite) { HalfEdge lBnd = coast.getHalfEdgeToTheLeftOf((newSite)); HalfEdge rBnd = coast.getHalfEdgeToTheRightOf(lBnd); // if this half-edge has no edge,bot = bottom site (whatever that is) Site bot = lBnd.getRightReg(bottomSite); Edge e = edgeBisector.newEdgeThatBisects(bot, newSite); HalfEdge bisector1 = new HalfEdge(e, Edge.LE); // insert this new bisector edge between the left and right bounds coast.insert(lBnd, bisector1); // if the new bisector intersects with the left edge, // remove the left edge's vertex, and put in the new one Site p = lBnd.intersect(bisector1); if (p != null) { events.delete(lBnd); events.insert(lBnd, p, p.distTo(newSite)); } HalfEdge bisector2 = new HalfEdge(e, Edge.RE); // insert the new HE to the right of the original bisector coast.insert(bisector1, bisector2); // if this new bisector intersects with the new HalfEdge p = bisector2.intersect(rBnd); if (p != null) { // push the HE into the ordered linked list of vertices events.insert(bisector2, p, p.distTo(newSite)); } }
public void computeNormal(Vector3d normal, double minArea) { computeNormal(normal); if (area < minArea) { System.out.println("area=" + area); // make the normal more robust by removing // components parallel to the longest edge HalfEdge hedgeMax = null; double lenSqrMax = 0; HalfEdge hedge = he0; do { double lenSqr = hedge.lengthSquared(); if (lenSqr > lenSqrMax) { hedgeMax = hedge; lenSqrMax = lenSqr; } hedge = hedge.next; } while (hedge != he0); Point3d p2 = hedgeMax.head().pnt; Point3d p1 = hedgeMax.tail().pnt; double lenMax = Math.sqrt(lenSqrMax); double ux = (p2.x - p1.x) / lenMax; double uy = (p2.y - p1.y) / lenMax; double uz = (p2.z - p1.z) / lenMax; double dot = normal.x * ux + normal.y * uy + normal.z * uz; normal.x -= dot * ux; normal.y -= dot * uy; normal.z -= dot * uz; normal.normalize(); } }
/** searches for all opposite HalfEdges */ public void computeOppositeHalfEdges() { if (debug) { System.out.println("Compute opposite half edges..."); } for (HalfEdge halfEdge : halfEdges) { halfEdge.setOpposite(computeOppositeHalfEdge(halfEdge)); } }
public void getVertexIndices(int[] idxs) { HalfEdge he = he0; int i = 0; do { idxs[i++] = he.head().index; he = he.next; } while (he != he0); }
public void computeCentroid(Point3d centroid) { centroid.setZero(); HalfEdge he = he0; do { centroid.add(he.head().pnt); he = he.next; } while (he != he0); centroid.scale(1 / (double) numVerts); }
private void checkEdgeRing(EdgeGraph graph, Coordinate p, Coordinate[] dest) { HalfEdge e = graph.findEdge(p, dest[0]); HalfEdge onext = e; int i = 0; do { assertTrue(onext.dest().equals2D(dest[i++])); onext = onext.oNext(); } while (onext != e); }
/** * Finds the half-edge within this face which has tail <code>vt</code> and head <code>vh</code>. * * @param vt tail point * @param vh head point * @return the half-edge, or null if none is found. */ public HalfEdge findEdge(Vertex vt, Vertex vh) { HalfEdge he = he0; do { if (he.head() == vh && he.tail() == vt) { return he; } he = he.next; } while (he != he0); return null; }
/** * Determine neighbours vertices for the given vertex. * * @param vertex the vertex to get the neighbour for * @return the determined neighbours. */ private ArrayList<Vertex> getNeighboursVertices(Vertex vertex) { ArrayList<Vertex> neighbours = new ArrayList<Vertex>(); HalfEdge first = vertex.getHalfEdge(); HalfEdge next = first; do { neighbours.add(next.getOpposite().getStartVertex()); next = next.getOpposite().getNext(); } while (next != first); return neighbours; }
public String getVertexString() { String s = null; HalfEdge he = he0; do { if (s == null) { s = "" + he.head().index; } else { s += " " + he.head().index; } he = he.next; } while (he != he0); return s; }
public int mergeAdjacentFace(HalfEdge hedgeAdj, Face[] discarded) { Face oppFace = hedgeAdj.oppositeFace(); int numDiscarded = 0; discarded[numDiscarded++] = oppFace; oppFace.mark = DELETED; HalfEdge hedgeOpp = hedgeAdj.getOpposite(); HalfEdge hedgeAdjPrev = hedgeAdj.prev; HalfEdge hedgeAdjNext = hedgeAdj.next; HalfEdge hedgeOppPrev = hedgeOpp.prev; HalfEdge hedgeOppNext = hedgeOpp.next; while (hedgeAdjPrev.oppositeFace() == oppFace) { hedgeAdjPrev = hedgeAdjPrev.prev; hedgeOppNext = hedgeOppNext.next; } while (hedgeAdjNext.oppositeFace() == oppFace) { hedgeOppPrev = hedgeOppPrev.prev; hedgeAdjNext = hedgeAdjNext.next; } HalfEdge hedge; for (hedge = hedgeOppNext; hedge != hedgeOppPrev.next; hedge = hedge.next) { hedge.face = this; } if (hedgeAdj == he0) { he0 = hedgeAdjNext; } // handle the half edges at the head Face discardedFace; discardedFace = connectHalfEdges(hedgeOppPrev, hedgeAdjNext); if (discardedFace != null) { discarded[numDiscarded++] = discardedFace; } // handle the half edges at the tail discardedFace = connectHalfEdges(hedgeAdjPrev, hedgeOppNext); if (discardedFace != null) { discarded[numDiscarded++] = discardedFace; } computeNormalAndCentroid(); checkConsistency(); return numDiscarded; }
public static Face create(Vertex[] vtxArray, int[] indices) { Face face = new Face(); HalfEdge hePrev = null; for (int i = 0; i < indices.length; i++) { HalfEdge he = new HalfEdge(vtxArray[indices[i]], face); if (hePrev != null) { he.setPrev(hePrev); hePrev.setNext(he); } else { face.he0 = he; } hePrev = he; } face.he0.setPrev(hePrev); hePrev.setNext(face.he0); // compute the normal and offset face.computeNormalAndCentroid(); return face; }
/** * Constructs a triangule Face from vertices v0, v1, and v2. * * @param v0 first vertex * @param v1 second vertex * @param v2 third vertex */ public static Face createTriangle(Vertex v0, Vertex v1, Vertex v2, double minArea) { Face face = new Face(); HalfEdge he0 = new HalfEdge(v0, face); HalfEdge he1 = new HalfEdge(v1, face); HalfEdge he2 = new HalfEdge(v2, face); he0.prev = he2; he0.next = he1; he1.prev = he0; he1.next = he2; he2.prev = he1; he2.next = he0; face.he0 = he0; // compute the normal and offset face.computeNormalAndCentroid(minArea); return face; }
private double areaSquared(HalfEdge hedge0, HalfEdge hedge1) { // return the squared area of the triangle defined // by the half edge hedge0 and the point at the // head of hedge1. Point3d p0 = hedge0.tail().pnt; Point3d p1 = hedge0.head().pnt; Point3d p2 = hedge1.head().pnt; double dx1 = p1.x - p0.x; double dy1 = p1.y - p0.y; double dz1 = p1.z - p0.z; double dx2 = p2.x - p0.x; double dy2 = p2.y - p0.y; double dz2 = p2.z - p0.z; double x = dy1 * dz2 - dz1 * dy2; double y = dz1 * dx2 - dx1 * dz2; double z = dx1 * dy2 - dy1 * dx2; return x * x + y * y + z * z; }
public void computeNormal(Vector3d normal) { HalfEdge he1 = he0.next; HalfEdge he2 = he1.next; Point3d p0 = he0.head().pnt; Point3d p2 = he1.head().pnt; double d2x = p2.x - p0.x; double d2y = p2.y - p0.y; double d2z = p2.z - p0.z; normal.setZero(); numVerts = 2; while (he2 != he0) { double d1x = d2x; double d1y = d2y; double d1z = d2z; p2 = he2.head().pnt; d2x = p2.x - p0.x; d2y = p2.y - p0.y; d2z = p2.z - p0.z; normal.x += d1y * d2z - d1z * d2y; normal.y += d1z * d2x - d1x * d2z; normal.z += d1x * d2y - d1y * d2x; he1 = he2; he2 = he2.next; numVerts++; } area = normal.norm(); normal.scale(1 / area); }
/** * computes opposite halfEdge for the given half edge. * * @param halfEdge the halfEdge to compute the opposite for. * @return the opposite HalfEdge to the given one. */ private HalfEdge computeOppositeHalfEdge(HalfEdge halfEdge) { Vertex destination = halfEdge.getNext().getStartVertex(); for (HalfEdge opposite : halfEdges) { if (!halfEdge.equals(opposite) && opposite.getStartVertex().equals(destination)) { if (opposite.getNext().getStartVertex().equals(halfEdge.getStartVertex())) { return opposite; } } } return null; }
public void computeVertexNormals() { for (HalfEdge first : halfEdges) { HalfEdge cur = first; Vector3 sum = cur.getFacet().getNormal(); do { cur = cur.getOpposite().getNext(); sum.add(cur.getFacet().getNormal()); } while (cur != first); first.getStartVertex().setNormal(sum.getNormalized()); } }
public void triangulate(FaceList newFaces, double minArea) { HalfEdge hedge; if (numVertices() < 4) { return; } Vertex v0 = he0.head(); Face prevFace = null; hedge = he0.next; HalfEdge oppPrev = hedge.opposite; Face face0 = null; for (hedge = hedge.next; hedge != he0.prev; hedge = hedge.next) { Face face = createTriangle(v0, hedge.prev.head(), hedge.head(), minArea); face.he0.next.setOpposite(oppPrev); face.he0.prev.setOpposite(hedge.opposite); oppPrev = face.he0; newFaces.add(face); if (face0 == null) { face0 = face; } } hedge = new HalfEdge(he0.prev.prev.head(), this); hedge.setOpposite(oppPrev); hedge.prev = he0; hedge.prev.next = hedge; hedge.next = he0.prev; hedge.next.prev = hedge; computeNormalAndCentroid(minArea); checkConsistency(); for (Face face = face0; face != null; face = face.next) { face.checkConsistency(); } }
public void addHalfEdge(HalfEdge halfEdge) { this.halfEdges.put(halfEdge.getId(), halfEdge); }
void checkConsistency() { // do a sanity check on the face HalfEdge hedge = he0; double maxd = 0; int numv = 0; if (numVerts < 3) { throw new InternalErrorException("degenerate face: " + getVertexString()); } do { HalfEdge hedgeOpp = hedge.getOpposite(); if (hedgeOpp == null) { throw new InternalErrorException( "face " + getVertexString() + ": " + "unreflected half edge " + hedge.getVertexString()); } else if (hedgeOpp.getOpposite() != hedge) { throw new InternalErrorException( "face " + getVertexString() + ": " + "opposite half edge " + hedgeOpp.getVertexString() + " has opposite " + hedgeOpp.getOpposite().getVertexString()); } if (hedgeOpp.head() != hedge.tail() || hedge.head() != hedgeOpp.tail()) { throw new InternalErrorException( "face " + getVertexString() + ": " + "half edge " + hedge.getVertexString() + " reflected by " + hedgeOpp.getVertexString()); } Face oppFace = hedgeOpp.face; if (oppFace == null) { throw new InternalErrorException( "face " + getVertexString() + ": " + "no face on half edge " + hedgeOpp.getVertexString()); } else if (oppFace.mark == DELETED) { throw new InternalErrorException( "face " + getVertexString() + ": " + "opposite face " + oppFace.getVertexString() + " not on hull"); } double d = Math.abs(distanceToPlane(hedge.head().pnt)); if (d > maxd) { maxd = d; } numv++; hedge = hedge.next; } while (hedge != he0); if (numv != numVerts) { throw new InternalErrorException( "face " + getVertexString() + " numVerts=" + numVerts + " should be " + numv); } }
private void processCircleEvent() { // pop the HalfEdge with the lowest vector off the ordered list // of vectors HalfEdge lBnd = events.extractMin(); // get the HalfEdge to the left of the above HE HalfEdge llBnd = coast.getHalfEdgeToTheLeftOf(lBnd); // get the HalfEdge to the right of the above HE HalfEdge rBnd = coast.getHalfEdgeToTheRightOf(lBnd); // get the HalfEdge to the right of the HE to the right of the // lowest HE HalfEdge rrBnd = coast.getHalfEdgeToTheRightOf(rBnd); // get the Site to the left of the left HE which it bisects Site bot = lBnd.getLeftReg(bottomSite); // get the Site to the right of the right HE which it bisects Site top = rBnd.getRightReg(bottomSite); Site v = lBnd.vertex; // get the vertex that caused this event // set the vertex number - couldn't do this // earlier since we didn't know when it would be processed v.sitenbr = nextVertexNbr++; endpoint(lBnd.edge, lBnd.side, v); // set the endpoint of // the left HalfEdge to be this vector endpoint(rBnd.edge, rBnd.side, v); // set the endpoint of the right HalfEdge to be this vector coast.delete(lBnd); events.delete(rBnd); // remove all vertex events to do with the right HE coast.delete(rBnd); // mark the right HE for // deletion - can't delete yet because there might be pointers // to it in Hash Map int pm = Edge.LE; // set the pm variable to zero if (bot.y > top.y) { // if the site to the left of the event is higher than the // Site // to the right of it, then swap them and set the 'pm' // variable to 1 Site temp = bot; bot = top; top = temp; pm = Edge.RE; } Edge e = edgeBisector.newEdgeThatBisects(bot, top); // create an Edge (or // line) // that is between the two Sites. This creates the formula of // the line, and assigns a line number to it HalfEdge bisector = new HalfEdge(e, pm); // create a HE from the Edge // 'e', // and make it point to that edge // with its ELedge field coast.insert(llBnd, bisector); // insert the new bisector to the // right of the left HE endpoint(e, Edge.RE - pm, v); // set one endpoint to the new edge // to be the vector point 'v'. // If the site to the left of this bisector is higher than the // right Site, then this endpoint // is put in position 0; otherwise in pos 1 // if left HE and the new bisector intersect, then delete // the left HE, and reinsert it Site p = llBnd.intersect(bisector); if (p != null) { events.delete(llBnd); events.insert(llBnd, p, p.distTo(bot)); } // if right HE and the new bisector intersect, then // reinsert it p = bisector.intersect(rrBnd); if (p != null) { events.insert(bisector, p, p.distTo(bot)); } }
private Face connectHalfEdges(HalfEdge hedgePrev, HalfEdge hedge) { Face discardedFace = null; if (hedgePrev.oppositeFace() == hedge.oppositeFace()) { // then there is a redundant edge that we can get rid off Face oppFace = hedge.oppositeFace(); HalfEdge hedgeOpp; if (hedgePrev == he0) { he0 = hedge; } if (oppFace.numVertices() == 3) { // then we can get rid of the opposite face altogether hedgeOpp = hedge.getOpposite().prev.getOpposite(); oppFace.mark = DELETED; discardedFace = oppFace; } else { hedgeOpp = hedge.getOpposite().next; if (oppFace.he0 == hedgeOpp.prev) { oppFace.he0 = hedgeOpp; } hedgeOpp.prev = hedgeOpp.prev.prev; hedgeOpp.prev.next = hedgeOpp; } hedge.prev = hedgePrev.prev; hedge.prev.next = hedge; hedge.opposite = hedgeOpp; hedgeOpp.opposite = hedge; // oppFace was modified, so need to recompute oppFace.computeNormalAndCentroid(); } else { hedgePrev.next = hedge; hedge.prev = hedgePrev; } return discardedFace; }