/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives * a place to insert the new vertex in the global vertex list. We insert * the new vertex *before* vNext so that algorithms which walk the vertex * list will not see the newly created vertices. */ static void MakeVertex(ContourVertex newVertex, HalfEdge eOrig, ContourVertex vNext) { HalfEdge e; ContourVertex vPrev; ContourVertex vNew = newVertex; /* insert in circular doubly-linked list before vNext */ vPrev = vNext.prevVertex; vNew.prevVertex = vPrev; vPrev.nextVertex = vNew; vNew.nextVertex = vNext; vNext.prevVertex = vNew; vNew.edgeThisIsOriginOf = eOrig; vNew.clientIndex = 0; /* leave coords, s, t undefined */ /* fix other edges on this vertex loop */ e = eOrig; do { e.originVertex = vNew; e = e.nextEdgeCCWAroundOrigin; } while (e != eOrig); }
public static bool VertCCW(ContourVertex <T> u, ContourVertex <T> v, ContourVertex <T> w) { /* For almost-degenerate situations, the results are not reliable. * Unless the floating-point arithmetic can be performed without * rounding errors, *any* implementation will give incorrect results * on some degenerate inputs, so the client must have some way to * handle this situation. */ return((u.x.Multiply(v.y.Subtract(w.y)).Add(v.x.Multiply(w.y.Subtract(u.y))).Add(w.x.Multiply(u.y.Subtract(v.y)))).GreaterThanOrEqualTo(0)); }
public C5.IPriorityQueueHandle <ContourVertex <T> > priorityQueueHandle; /* to allow deletion from priority queue */ public int CompareTo(ContourVertex <T> otherVertex) { if (VertEq(otherVertex)) { return(0); } if (this.VertLeq(otherVertex)) { return(-1); } return(1); }
// __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). // The loop consists of the two new half-edges. public HalfEdge MakeEdge() { ContourVertex newVertex1 = new ContourVertex(); ContourVertex newVertex2 = new ContourVertex(); Face newFace = new Face(); HalfEdge e; e = MakeEdge(this.halfEdgeHead); MakeVertex(newVertex1, e, this.vertexHead); MakeVertex(newVertex2, e.otherHalfOfThisEdge, this.vertexHead); MakeFace(newFace, e, this.faceHead); return(e); }
private void ProjectPolygon() { ContourVertex v, vHead = this.mesh.vertexHead; // Project the vertices onto the sweep plane for (v = vHead.nextVertex; v != vHead; v = v.nextVertex) { v.x = v.coords[0]; v.y = -v.coords[1]; } CheckOrientation(); }
/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the * mesh connectivity and topology. It changes the mesh so that * eOrg.Onext <- OLD( eDst.Onext ) * eDst.Onext <- OLD( eOrg.Onext ) * where OLD(...) means the value before the meshSplice operation. * * This can have two effects on the vertex structure: * - if eOrg.Org != eDst.Org, the two vertices are merged together * - if eOrg.Org == eDst.Org, the origin is split into two vertices * In both cases, eDst.Org is changed and eOrg.Org is untouched. * * Similarly (and independently) for the face structure, * - if eOrg.Lface == eDst.Lface, one loop is split into two * - if eOrg.Lface != eDst.Lface, two distinct loops are joined into one * In both cases, eDst.Lface is changed and eOrg.Lface is unaffected. * * Some special cases: * If eDst == eOrg, the operation has no effect. * If eDst == eOrg.Lnext, the new face will have a single edge. * If eDst == eOrg.Lprev, the old face will have a single edge. * If eDst == eOrg.Onext, the new vertex will have a single edge. * If eDst == eOrg.Oprev, the old vertex will have a single edge. */ public static void meshSplice(HalfEdge eOrg, HalfEdge eDst) { bool joiningLoops = false; bool joiningVertices = false; if (eOrg == eDst) { return; } if (eDst.originVertex != eOrg.originVertex) { /* We are merging two disjoint vertices -- destroy eDst.Org */ joiningVertices = true; KillVertex(eDst.originVertex, eOrg.originVertex); } if (eDst.leftFace != eOrg.leftFace) { /* We are connecting two disjoint loops -- destroy eDst.Lface */ joiningLoops = true; KillFace(eDst.leftFace, eOrg.leftFace); } /* Change the edge structure */ Splice(eDst, eOrg); if (!joiningVertices) { ContourVertex newVertex = new ContourVertex(); /* We split one vertex into two -- the new vertex is eDst.Org. * Make sure the old vertex points to a valid half-edge. */ MakeVertex(newVertex, eDst, eOrg.originVertex); eOrg.originVertex.edgeThisIsOriginOf = eOrg; } if (!joiningLoops) { Face newFace = new Face(); /* We split one loop into two -- the new loop is eDst.Lface. * Make sure the old face points to a valid half-edge. */ MakeFace(newFace, eDst, eOrg.leftFace); eOrg.leftFace.halfEdgeThisIsLeftFaceOf = eOrg; } }
/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that * eNew == eOrg.Lnext, and eNew.Dst is a newly created vertex. * eOrg and eNew will have the same left face. */ static HalfEdge meshAddEdgeVertex(HalfEdge eOrg) { HalfEdge eNewSym; HalfEdge eNew = MakeEdge(eOrg); eNewSym = eNew.otherHalfOfThisEdge; /* Connect the new edge appropriately */ Splice(eNew, eOrg.nextEdgeCCWAroundLeftFace); /* Set the vertex and face information */ eNew.originVertex = eOrg.directionVertex; { ContourVertex newVertex = new ContourVertex(); MakeVertex(newVertex, eNewSym, eNew.originVertex); } eNew.leftFace = eNewSym.leftFace = eOrg.leftFace; return(eNew); }
/* KillVertex( vDel ) destroys a vertex and removes it from the global * vertex list. It updates the vertex loop to point to a given new vertex. */ static void KillVertex(ContourVertex vDel, ContourVertex newOrg) { HalfEdge e, eStart = vDel.edgeThisIsOriginOf; ContourVertex vPrev, vNext; /* change the origin of all affected edges */ e = eStart; do { e.originVertex = newOrg; e = e.nextEdgeCCWAroundOrigin; } while (e != eStart); /* delete from circular doubly-linked list */ vPrev = vDel.prevVertex; vNext = vDel.nextVertex; vNext.prevVertex = vPrev; vPrev.nextVertex = vNext; }
/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that * eNew == eOrg.Lnext, and eNew.Dst is a newly created vertex. * eOrg and eNew will have the same left face. */ private static HalfEdge meshAddEdgeVertex(HalfEdge originalEdge) { HalfEdge newEdgeOtherHalf; HalfEdge newEdge = MakeEdge(originalEdge); newEdgeOtherHalf = newEdge.otherHalfOfThisEdge; /* Connect the new edge appropriately */ Splice(newEdge, originalEdge.nextEdgeCCWAroundLeftFace); /* Set the vertex and face information */ newEdge.originVertex = originalEdge.directionVertex; { ContourVertex newVertex = new ContourVertex(); MakeVertex(newVertex, newEdgeOtherHalf, newEdge.originVertex); } newEdge.leftFace = newEdgeOtherHalf.leftFace = originalEdge.leftFace; return(newEdge); }
public static T EdgeEval(ContourVertex <T> u, ContourVertex <T> v, ContourVertex <T> w) { /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), * evaluates the t-coord of the edge uw at the s-coord of the vertex v. * Returns v.t - (uw)(v.s), ie. the signed distance from uw to v. * If uw is vertical (and thus passes thru v), the result is zero. * * The calculation is extremely accurate and stable, even when v * is very close to u or w. In particular if we set v.t = 0 and * let r be the negated result (this evaluates (uw)(v.s)), then * r is guaranteed to satisfy MIN(u.t,w.t) <= r <= MAX(u.t,w.t). */ T gapL, gapR; if (!((u.VertLeq(u) && v.VertLeq(v)))) { throw new Exception(); } gapL = v.x.Subtract(u.x); gapR = w.x.Subtract(v.x); if (gapL.Add(gapR).GreaterThan(0)) { if (gapL.LessThan(gapR)) { return(v.y.Subtract(u.y).Add( u.y.Subtract(w.y).Multiply( gapL.Divide(gapL.Add(gapR))))); } else { return(v.y.Subtract(w.y).Add(w.y.Subtract(u.y).Multiply(gapR.Divide(gapL.Add(gapR))))); } } // vertical line return(M.Zero <T>()); }
static public T EdgeSign(ContourVertex <T> u, ContourVertex <T> v, ContourVertex <T> w) { /* Returns a number whose sign matches EdgeEval(u,w) but which * is cheaper to evaluate. Returns > 0, == 0 , or < 0 * as v is above, on, or below the edge uw. */ T gapL, gapR; if (!u.VertLeq(v) || !v.VertLeq(w)) { throw new System.Exception(); } gapL = v.x.Subtract(u.x); gapR = w.x.Subtract(v.x); if (gapL.Add(gapR).GreaterThan(0)) { return(v.y.Subtract(w.y).Multiply(gapL).Add(v.y.Subtract(u.y).Multiply(gapR))); } /* vertical line */ return(M.Zero <T>()); }
/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in * both meshes, and returns the new mesh (the old meshes are destroyed). */ Mesh meshUnion(Mesh mesh1, Mesh mesh2) { Face f1 = mesh1.faceHead; ContourVertex v1 = mesh1.vertexHead; HalfEdge e1 = mesh1.halfEdgeHead; Face f2 = mesh2.faceHead; ContourVertex v2 = mesh2.vertexHead; HalfEdge e2 = mesh2.halfEdgeHead; /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ if (f2.nextFace != f2) { f1.prevFace.nextFace = f2.nextFace; f2.nextFace.prevFace = f1.prevFace; f2.prevFace.nextFace = f1; f1.prevFace = f2.prevFace; } if (v2.nextVertex != v2) { v1.prevVertex.nextVertex = v2.nextVertex; v2.nextVertex.prevVertex = v1.prevVertex; v2.prevVertex.nextVertex = v1; v1.prevVertex = v2.prevVertex; } if (e2.nextHalfEdge != e2) { e1.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e2.nextHalfEdge; e2.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1.otherHalfOfThisEdge.nextHalfEdge; e2.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1; e1.otherHalfOfThisEdge.nextHalfEdge = e2.otherHalfOfThisEdge.nextHalfEdge; } mesh2 = null; return(mesh1); }
private void CheckOrientation() { double area; Face curFace, faceHead = this.mesh.faceHead; ContourVertex vHead = this.mesh.vertexHead; HalfEdge curHalfEdge; /* When we compute the normal automatically, we choose the orientation * so that the sum of the signed areas of all contours is non-negative. */ area = 0; for (curFace = faceHead.nextFace; curFace != faceHead; curFace = curFace.nextFace) { curHalfEdge = curFace.halfEdgeThisIsLeftFaceOf; if (curHalfEdge.winding <= 0) { continue; } do { area += (curHalfEdge.originVertex.x - curHalfEdge.directionVertex.x) * (curHalfEdge.originVertex.y + curHalfEdge.directionVertex.y); curHalfEdge = curHalfEdge.nextEdgeCCWAroundLeftFace; } while (curHalfEdge != curFace.halfEdgeThisIsLeftFaceOf); } if (area < 0) { /* Reverse the orientation by flipping all the t-coordinates */ for (ContourVertex curVertex = vHead.nextVertex; curVertex != vHead; curVertex = curVertex.nextVertex) { curVertex.y = -curVertex.y; } } }
/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. */ public void CheckMesh() { Face fHead = this.faceHead; ContourVertex vHead = this.vertexHead; HalfEdge eHead = this.halfEdgeHead; Face f, fPrev; ContourVertex v, vPrev; HalfEdge e, ePrev; fPrev = fHead; for (fPrev = fHead; (f = fPrev.nextFace) != fHead; fPrev = f) { if (f.prevFace != fPrev) { throw new Exception(); } e = f.halfEdgeThisIsLeftFaceOf; do { if (e.otherHalfOfThisEdge == e) { throw new Exception(); } if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e) { throw new Exception(); } if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e) { throw new Exception(); } if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e) { throw new Exception(); } if (e.leftFace != f) { throw new Exception(); } e = e.nextEdgeCCWAroundLeftFace; } while (e != f.halfEdgeThisIsLeftFaceOf); } if (f.prevFace != fPrev || f.halfEdgeThisIsLeftFaceOf != null) { throw new Exception(); } vPrev = vHead; for (vPrev = vHead; (v = vPrev.nextVertex) != vHead; vPrev = v) { if (v.prevVertex != vPrev) { throw new Exception(); } e = v.edgeThisIsOriginOf; do { if (e.otherHalfOfThisEdge == e) { throw new Exception(); } if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e) { throw new Exception(); } if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e) { throw new Exception(); } if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e) { throw new Exception(); } if (e.originVertex != v) { throw new Exception(); } e = e.nextEdgeCCWAroundOrigin; } while (e != v.edgeThisIsOriginOf); } if (v.prevVertex != vPrev || v.edgeThisIsOriginOf != null || v.clientIndex != 0) { throw new Exception(); } ePrev = eHead; for (ePrev = eHead; (e = ePrev.nextHalfEdge) != eHead; ePrev = e) { if (e.otherHalfOfThisEdge.nextHalfEdge != ePrev.otherHalfOfThisEdge) { throw new Exception(); } if (e.otherHalfOfThisEdge == e) { throw new Exception(); } if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e) { throw new Exception(); } if (e.originVertex == null) { throw new Exception(); } if (e.directionVertex == null) { throw new Exception(); } if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e) { throw new Exception(); } if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e) { throw new Exception(); } } if (e.otherHalfOfThisEdge.nextHalfEdge != ePrev.otherHalfOfThisEdge || e.otherHalfOfThisEdge != this.otherHalfOfThisEdgeHead || e.otherHalfOfThisEdge.otherHalfOfThisEdge != e || e.originVertex != null || e.directionVertex != null || e.leftFace != null || e.rightFace != null) { throw new Exception(); } }
public bool TransLeq(ContourVertex <T> v) { return((this.y.LessThan(v.y)) || (this.y.Equals(v.y) && this.x.LessThanOrEqualTo(v.x))); }
public bool VertLeq(ContourVertex <T> v) { return((this.x.LessThan(v.x)) || (this.x.Equals(v.x) && this.y.LessThan(v.y))); }
public bool VertEq(ContourVertex <T> v) { return((this.x.Equals(v.x)) && this.y.Equals(v.y)); }
/* face.TessellateMonoRegion() tessellates a monotone region * (what else would it do??) The region must consist of a single * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this * case means that any vertical line intersects the interior of the * region in a single interval. * * Tessellation consists of adding interior edges (actually pairs of * half-edges), to split the region into non-overlapping triangles. * * The basic idea is explained in Preparata and Shamos (which I don''t * have handy right now), although their implementation is more * complicated than this one. The are two edge chains, an upper chain * and a lower chain. We process all vertices from both chains in order, * from right to left. * * The algorithm ensures that the following invariant holds after each * vertex is processed: the untessellated region consists of two * chains, where one chain (say the upper) is a single edge, and * the other chain is concave. The left vertex of the single edge * is always to the left of all vertices in the concave chain. * * Each step consists of adding the rightmost unprocessed vertex to one * of the two chains, and forming a fan of triangles from the rightmost * of two chain endpoints. Determining whether we can add each triangle * to the fan is a simple orientation test. By making the fan as large * as possible, we restore the invariant (check it yourself). */ internal bool TessellateMonoRegion() { /* All edges are oriented CCW around the boundary of the region. * First, find the half-edge whose origin vertex is rightmost. * Since the sweep goes from left to right, face.anEdge should * be close to the edge we want. */ HalfEdge <T> up = this.halfEdgeThisIsLeftFaceOf; if (up.nextEdgeCCWAroundLeftFace == up || up.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == up) { throw new Exception(); } for (; up.directionVertex.VertLeq(up.originVertex); up = up.Lprev) { ; } for (; up.originVertex.VertLeq(up.directionVertex); up = up.nextEdgeCCWAroundLeftFace) { ; } HalfEdge <T> lo = up.Lprev; while (up.nextEdgeCCWAroundLeftFace != lo) { if (up.directionVertex.VertLeq(lo.originVertex)) { /* up.Dst is on the left. It is safe to form triangles from lo.Org. * The EdgeGoesLeft test guarantees progress even when some triangles * are CW, given that the upper and lower chains are truly monotone. */ while (lo.nextEdgeCCWAroundLeftFace != up && (lo.nextEdgeCCWAroundLeftFace.EdgeGoesLeft() || ContourVertex <T> .EdgeSign(lo.originVertex, lo.directionVertex, lo.nextEdgeCCWAroundLeftFace.directionVertex).LessThanOrEqualTo(0))) { HalfEdge <T> tempHalfEdge = Mesh <T> .meshConnect(lo.nextEdgeCCWAroundLeftFace, lo); lo = tempHalfEdge.otherHalfOfThisEdge; } lo = lo.Lprev; } else { /* lo.Org is on the left. We can make CCW triangles from up.Dst. */ while (lo.nextEdgeCCWAroundLeftFace != up && (up.Lprev.EdgeGoesRight() || ContourVertex <T> .EdgeSign(up.directionVertex, up.originVertex, up.Lprev.originVertex).GreaterThanOrEqualTo(0))) { HalfEdge <T> tempHalfEdge = Mesh <T> .meshConnect(up, up.Lprev); up = tempHalfEdge.otherHalfOfThisEdge; } up = up.nextEdgeCCWAroundLeftFace; } } // Now lo.Org == up.Dst == the leftmost vertex. The remaining region // can be tessellated in a fan from this leftmost vertex. if (lo.nextEdgeCCWAroundLeftFace == up) { throw new Exception(); } while (lo.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace != up) { HalfEdge <T> tempHalfEdge = Mesh <T> .meshConnect(lo.nextEdgeCCWAroundLeftFace, lo); lo = tempHalfEdge.otherHalfOfThisEdge; } return(true); }
public bool Equal2D(ContourVertex <T> OtherVertex) { return(this.x.Equals(OtherVertex.x) && this.y.Equals(OtherVertex.y)); }