/// <summary> /// Creates a new edge such that eNew == eOrg.Lnext and eNew.Dst is a newly created vertex. /// eOrg and eNew will have the same left face. /// </summary> public MeshUtils.Edge AddEdgeVertex(MeshUtils.Edge eOrg) { var eNew = MeshUtils.MakeEdge(eOrg); var eNewSym = eNew._Sym; // Connect the new edge appropriately MeshUtils.Splice(eNew, eOrg._Lnext); // Set vertex and face information eNew._Org = eOrg._Dst; MeshUtils.MakeVertex(new MeshUtils.Vertex(), eNewSym, eNew._Org); eNew._Lface = eNewSym._Lface = eOrg._Lface; return(eNew); }
/// <summary> /// Splits eOrg into two edges eOrg and eNew such that eNew == eOrg.Lnext. /// The new vertex is eOrg.Dst == eNew.Org. /// eOrg and eNew will have the same left face. /// </summary> public MeshUtils.Edge SplitEdge(MeshUtils.Edge eOrg) { var eTmp = AddEdgeVertex(eOrg); var eNew = eTmp._Sym; // Disconnect eOrg from eOrg->Dst and connect it to eNew->Org MeshUtils.Splice(eOrg._Sym, eOrg._Sym._Oprev); MeshUtils.Splice(eOrg._Sym, eNew); // Set the vertex and face information eOrg._Dst = eNew._Org; eNew._Dst._anEdge = eNew._Sym; // may have pointed to eOrg->Sym eNew._Rface = eOrg._Rface; eNew._winding = eOrg._winding; // copy old winding information eNew._Sym._winding = eOrg._Sym._winding; return(eNew); }
/// <summary> /// Splice 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. /// </summary> public void Splice(MeshUtils.Edge eOrg, MeshUtils.Edge eDst) { if (eOrg == eDst) { return; } bool joiningVertices = false; if (eDst._Org != eOrg._Org) { // We are merging two disjoint vertices -- destroy eDst->Org joiningVertices = true; MeshUtils.KillVertex(eDst._Org, eOrg._Org); } bool joiningLoops = false; if (eDst._Lface != eOrg._Lface) { // We are connecting two disjoint loops -- destroy eDst->Lface joiningLoops = true; MeshUtils.KillFace(eDst._Lface, eOrg._Lface); } // Change the edge structure MeshUtils.Splice(eDst, eOrg); if (!joiningVertices) { // We split one vertex into two -- the new vertex is eDst->Org. // Make sure the old vertex points to a valid half-edge. MeshUtils.MakeVertex(new MeshUtils.Vertex(), eDst, eOrg._Org); eOrg._Org._anEdge = eOrg; } if (!joiningLoops) { // We split one loop into two -- the new loop is eDst->Lface. // Make sure the old face points to a valid half-edge. MeshUtils.MakeFace(new MeshUtils.Face(), eDst, eOrg._Lface); eOrg._Lface._anEdge = eOrg; } }
/// <summary> /// 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). /// </summary> public static float EdgeEval(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(VertLeq(u, v) && VertLeq(v, w)); float gapL = v._s - u._s; float gapR = w._s - v._s; if (gapL + gapR > 0.0f) { if (gapL < gapR) { return (v._t - u._t) + (u._t - w._t) * (gapL / (gapL + gapR)); } else { return (v._t - w._t) + (w._t - u._t) * (gapR / (gapL + gapR)); } } /* vertical line */ return 0.0f; }
static void Swap(ref MeshUtils.Vertex a, ref MeshUtils.Vertex b) { var tmp = a; a = b; b = tmp; }
/// <summary> /// Given edges (o1,d1) and (o2,d2), compute their point of intersection. /// The computed point is guaranteed to lie in the intersection of the /// bounding rectangles defined by each edge. /// </summary> public static void EdgeIntersect(MeshUtils.Vertex o1, MeshUtils.Vertex d1, MeshUtils.Vertex o2, MeshUtils.Vertex d2, MeshUtils.Vertex v) { float z1, z2; // This is certainly not the most efficient way to find the intersection // of two line segments, but it is very numerically stable. // // Strategy: find the two middle vertices in the VertLeq ordering, // and interpolate the intersection s-value from these. Then repeat // using the TransLeq ordering to find the intersection t-value. if (!VertLeq(o1, d1)) { Swap(ref o1, ref d1); } if (!VertLeq(o2, d2)) { Swap(ref o2, ref d2); } if (!VertLeq(o1, o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); } if (!VertLeq(o2, d1)) { // Technically, no intersection -- do our best v._s = (o2._s + d1._s) / 2.0f; } else if (VertLeq(d1, d2)) { // Interpolate between o2 and d1 z1 = EdgeEval(o1, o2, d1); z2 = EdgeEval(o2, d1, d2); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._s = Interpolate(z1, o2._s, z2, d1._s); } else { // Interpolate between o2 and d2 z1 = EdgeSign(o1, o2, d1); z2 = -EdgeSign(o1, d2, d1); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._s = Interpolate(z1, o2._s, z2, d2._s); } // Now repeat the process for t if (!TransLeq(o1, d1)) { Swap(ref o1, ref d1); } if (!TransLeq(o2, d2)) { Swap(ref o2, ref d2); } if (!TransLeq(o1, o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); } if (!TransLeq(o2, d1)) { // Technically, no intersection -- do our best v._t = (o2._t + d1._t) / 2.0f; } else if (TransLeq(d1, d2)) { // Interpolate between o2 and d1 z1 = TransEval(o1, o2, d1); z2 = TransEval(o2, d1, d2); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._t = Interpolate(z1, o2._t, z2, d1._t); } else { // Interpolate between o2 and d2 z1 = TransSign(o1, o2, d1); z2 = -TransSign(o1, d2, d1); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._t = Interpolate(z1, o2._t, z2, d2._t); } }
/// <summary> /// Purpose: connect a "left" vertex (one where both edges go right) /// to the processed portion of the mesh. Let R be the active region /// containing vEvent, and let U and L be the upper and lower edge /// chains of R. There are two possibilities: /// /// - the normal case: split R into two regions, by connecting vEvent to /// the rightmost vertex of U or L lying to the left of the sweep line /// /// - the degenerate case: if vEvent is close enough to U or L, we /// merge vEvent into that edge chain. The subcases are: /// - merging with the rightmost vertex of U or L /// - merging with the active edge of U or L /// - merging with an already-processed portion of U or L /// </summary> private void ConnectLeftVertex(MeshUtils.Vertex vEvent) { var tmp = new ActiveRegion(); // Get a pointer to the active region containing vEvent tmp._eUp = vEvent._anEdge._Sym; var regUp = _dict.Find(tmp).Key; var regLo = RegionBelow(regUp); if (regLo == null) { // This may happen if the input polygon is coplanar. return; } var eUp = regUp._eUp; var eLo = regLo._eUp; // Try merging with U or L first if (Geom.EdgeSign(eUp._Dst, vEvent, eUp._Org) == 0.0f) { ConnectLeftDegenerate(regUp, vEvent); return; } // Connect vEvent to rightmost processed vertex of either chain. // e._Dst is the vertex that we will connect to vEvent. var reg = Geom.VertLeq(eLo._Dst, eUp._Dst) ? regUp : regLo; if (regUp._inside || reg._fixUpperEdge) { MeshUtils.Edge eNew; if (reg == regUp) { eNew = _mesh.Connect(vEvent._anEdge._Sym, eUp._Lnext); } else { eNew = _mesh.Connect(eLo._Dnext, vEvent._anEdge)._Sym; } if (reg._fixUpperEdge) { FixUpperEdge(reg, eNew); } else { ComputeWinding(AddRegionBelow(regUp, eNew)); } SweepEvent(vEvent); } else { // The new vertex is in a region which does not belong to the polygon. // We don't need to connect this vertex to the rest of the mesh. AddRightEdges(regUp, vEvent._anEdge, vEvent._anEdge, null, true); } }
/// <summary> /// Find some weights which describe how the intersection vertex is /// a linear combination of "org" and "dest". Each of the two edges /// which generated "isect" is allocated 50% of the weight; each edge /// splits the weight between its org and dst according to the /// relative distance to "isect". /// </summary> private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUtils.Vertex dst, out float w0, out float w1) { var t1 = Geom.VertL1dist(org, isect); var t2 = Geom.VertL1dist(dst, isect); w0 = 0.5f * t2 / (t1 + t2); w1 = 0.5f * t1 / (t1 + t2); isect._coords.X += w0 * org._coords.X + w1 * dst._coords.X; isect._coords.Y += w0 * org._coords.Y + w1 * dst._coords.Y; isect._coords.Z += w0 * org._coords.Z + w1 * dst._coords.Z; }
/// <summary> /// Purpose: connect a "right" vertex vEvent (one where all edges go left) /// to the unprocessed portion of the mesh. Since there are no right-going /// edges, two regions (one above vEvent and one below) are being merged /// into one. "regUp" is the upper of these two regions. /// /// There are two reasons for doing this (adding a right-going edge): /// - if the two regions being merged are "inside", we must add an edge /// to keep them separated (the combined region would not be monotone). /// - in any case, we must leave some record of vEvent in the dictionary, /// so that we can merge vEvent with features that we have not seen yet. /// For example, maybe there is a vertical edge which passes just to /// the right of vEvent; we would like to splice vEvent into this edge. /// /// However, we don't want to connect vEvent to just any vertex. We don''t /// want the new edge to cross any other edges; otherwise we will create /// intersection vertices even when the input data had no self-intersections. /// (This is a bad thing; if the user's input data has no intersections, /// we don't want to generate any false intersections ourselves.) /// /// Our eventual goal is to connect vEvent to the leftmost unprocessed /// vertex of the combined region (the union of regUp and regLo). /// But because of unseen vertices with all right-going edges, and also /// new vertices which may be created by edge intersections, we don''t /// know where that leftmost unprocessed vertex is. In the meantime, we /// connect vEvent to the closest vertex of either chain, and mark the region /// as "fixUpperEdge". This flag says to delete and reconnect this edge /// to the next processed vertex on the boundary of the combined region. /// Quite possibly the vertex we connected to will turn out to be the /// closest one, in which case we won''t need to make any changes. /// </summary> private void ConnectRightVertex(ActiveRegion regUp, MeshUtils.Edge eBottomLeft) { var eTopLeft = eBottomLeft._Onext; var regLo = RegionBelow(regUp); var eUp = regUp._eUp; var eLo = regLo._eUp; bool degenerate = false; if (eUp._Dst != eLo._Dst) { CheckForIntersect(regUp); } // Possible new degeneracies: upper or lower edge of regUp may pass // through vEvent, or may coincide with new intersection vertex if (Geom.VertEq(eUp._Org, _event)) { _mesh.Splice(eTopLeft._Oprev, eUp); regUp = TopLeftRegion(regUp); eTopLeft = RegionBelow(regUp)._eUp; FinishLeftRegions(RegionBelow(regUp), regLo); degenerate = true; } if (Geom.VertEq(eLo._Org, _event)) { _mesh.Splice(eBottomLeft, eLo._Oprev); eBottomLeft = FinishLeftRegions(regLo, null); degenerate = true; } if (degenerate) { AddRightEdges(regUp, eBottomLeft._Onext, eTopLeft, eTopLeft, true); return; } // Non-degenerate situation -- need to add a temporary, fixable edge. // Connect to the closer of eLo.Org, eUp.Org. MeshUtils.Edge eNew; if (Geom.VertLeq(eLo._Org, eUp._Org)) { eNew = eLo._Oprev; } else { eNew = eUp; } eNew = _mesh.Connect(eBottomLeft._Lprev, eNew); // Prevent cleanup, otherwise eNew might disappear before we've even // had a chance to mark it as a temporary edge. AddRightEdges(regUp, eNew, eNew._Onext, eNew._Onext, false); eNew._Sym._activeRegion._fixUpperEdge = true; WalkDirtyRegions(regUp); }
/// <summary> /// Replace an upper edge which needs fixing (see ConnectRightVertex). /// </summary> private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge) { Debug.Assert(reg._fixUpperEdge); _mesh.Delete(reg._eUp); reg._fixUpperEdge = false; reg._eUp = newEdge; newEdge._activeRegion = reg; }
/// <summary> /// Purpose: insert right-going edges into the edge dictionary, and update /// winding numbers and mesh connectivity appropriately. All right-going /// edges share a common origin vOrg. Edges are inserted CCW starting at /// eFirst; the last edge inserted is eLast.Oprev. If vOrg has any /// left-going edges already processed, then eTopLeft must be the edge /// such that an imaginary upward vertical segment from vOrg would be /// contained between eTopLeft.Oprev and eTopLeft; otherwise eTopLeft /// should be null. /// </summary> private void AddRightEdges(ActiveRegion regUp, MeshUtils.Edge eFirst, MeshUtils.Edge eLast, MeshUtils.Edge eTopLeft, bool cleanUp) { bool firstTime = true; var e = eFirst; do { Debug.Assert(Geom.VertLeq(e._Org, e._Dst)); AddRegionBelow(regUp, e._Sym); e = e._Onext; } while (e != eLast); // Walk *all* right-going edges from e.Org, in the dictionary order, // updating the winding numbers of each region, and re-linking the mesh // edges to match the dictionary ordering (if necessary). if (eTopLeft == null) { eTopLeft = RegionBelow(regUp)._eUp._Rprev; } ActiveRegion regPrev = regUp, reg; var ePrev = eTopLeft; while (true) { reg = RegionBelow(regPrev); e = reg._eUp._Sym; if (e._Org != ePrev._Org) break; if (e._Onext != ePrev) { // Unlink e from its current position, and relink below ePrev _mesh.Splice(e._Oprev, e); _mesh.Splice(ePrev._Oprev, e); } // Compute the winding number and "inside" flag for the new regions reg._windingNumber = regPrev._windingNumber - e._winding; reg._inside = Geom.IsWindingInside(_windingRule, reg._windingNumber); // Check for two outgoing edges with same slope -- process these // before any intersection tests (see example in tessComputeInterior). regPrev._dirty = true; if (!firstTime && CheckForRightSplice(regPrev)) { Geom.AddWinding(e, ePrev); DeleteRegion(regPrev); _mesh.Delete(ePrev); } firstTime = false; regPrev = reg; ePrev = e; } regPrev._dirty = true; Debug.Assert(regPrev._windingNumber - e._winding == reg._windingNumber); if (cleanUp) { // Check for intersections between newly adjacent edges. WalkDirtyRegions(regPrev); } }
public static bool VertCCW(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { return (u._s * (v._t - w._t) + v._s * (w._t - u._t) + w._s * (u._t - v._t)) >= 0.0f; }
public static bool VertLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { return (lhs._s < rhs._s) || (lhs._s == rhs._s && lhs._t <= rhs._t); }
/// <summary> /// Removes the edge eDel. There are several cases: /// if (eDel->Lface != eDel->Rface), we join two loops into one; the loop /// eDel->Lface is deleted. Otherwise, we are splitting one loop into two; /// the newly created loop will contain eDel->Dst. If the deletion of eDel /// would create isolated vertices, those are deleted as well. /// </summary> public void Delete(MeshUtils.Edge eDel) { var eDelSym = eDel._Sym; // First step: disconnect the origin vertex eDel->Org. We make all // changes to get a consistent mesh in this "intermediate" state. bool joiningLoops = false; if (eDel._Lface != eDel._Rface) { // We are joining two loops into one -- remove the left face joiningLoops = true; MeshUtils.KillFace(eDel._Lface, eDel._Rface); } if (eDel._Onext == eDel) { MeshUtils.KillVertex(eDel._Org, null); } else { // Make sure that eDel->Org and eDel->Rface point to valid half-edges eDel._Rface._anEdge = eDel._Oprev; eDel._Org._anEdge = eDel._Onext; MeshUtils.Splice(eDel, eDel._Oprev); if (!joiningLoops) { // We are splitting one loop into two -- create a new loop for eDel. MeshUtils.MakeFace(new MeshUtils.Face(), eDel, eDel._Lface); } } // Claim: the mesh is now in a consistent state, except that eDel->Org // may have been deleted. Now we disconnect eDel->Dst. if (eDelSym._Onext == eDelSym) { MeshUtils.KillVertex(eDelSym._Org, null); MeshUtils.KillFace(eDelSym._Lface, null); } else { // Make sure that eDel->Dst and eDel->Lface point to valid half-edges eDel._Lface._anEdge = eDelSym._Oprev; eDelSym._Org._anEdge = eDelSym._Onext; MeshUtils.Splice(eDelSym, eDelSym._Oprev); } // Any isolated vertices or faces have already been freed. MeshUtils.KillEdge(eDel); }
/// <summary> /// Creates a new edge such that eNew == eOrg.Lnext and eNew.Dst is a newly created vertex. /// eOrg and eNew will have the same left face. /// </summary> public MeshUtils.Edge AddEdgeVertex(MeshUtils.Edge eOrg) { var eNew = MeshUtils.MakeEdge(eOrg); var eNewSym = eNew._Sym; // Connect the new edge appropriately MeshUtils.Splice(eNew, eOrg._Lnext); // Set vertex and face information eNew._Org = eOrg._Dst; MeshUtils.MakeVertex(new MeshUtils.Vertex(), eNewSym, eNew._Org); eNew._Lface = eNewSym._Lface = eOrg._Lface; return eNew; }
/// <summary> /// Destroys a face and removes it from the global face list. All edges of /// fZap will have a NULL pointer as their left face. Any edges which /// also have a NULL pointer as their right face are deleted entirely /// (along with any isolated vertices this produces). /// An entire mesh can be deleted by zapping its faces, one at a time, /// in any order. Zapped faces cannot be used in further mesh operations! /// </summary> public void ZapFace(MeshUtils.Face fZap) { var eStart = fZap._anEdge; // walk around face, deleting edges whose right face is also NULL var eNext = eStart._Lnext; MeshUtils.Edge e, eSym; do { e = eNext; eNext = e._Lnext; e._Lface = null; if (e._Rface == null) { // delete the edge -- see TESSmeshDelete above if (e._Onext == e) { MeshUtils.KillVertex(e._Org, null); } else { // Make sure that e._Org points to a valid half-edge e._Org._anEdge = e._Onext; MeshUtils.Splice(e, e._Oprev); } eSym = e._Sym; if (eSym._Onext == eSym) { MeshUtils.KillVertex(eSym._Org, null); } else { // Make sure that eSym._Org points to a valid half-edge eSym._Org._anEdge = eSym._Onext; MeshUtils.Splice(eSym, eSym._Oprev); } MeshUtils.KillEdge(e); } } while (e != eStart); /* delete from circular doubly-linked list */ var fPrev = fZap._prev; var fNext = fZap._next; fNext._prev = fPrev; fPrev._next = fNext; }
/// <summary> /// Creates a new edge from eOrg->Dst to eDst->Org, and returns the corresponding half-edge eNew. /// If eOrg->Lface == eDst->Lface, this splits one loop into two, /// and the newly created loop is eNew->Lface. Otherwise, two disjoint /// loops are merged into one, and the loop eDst->Lface is destroyed. /// /// If (eOrg == eDst), the new face will have only two edges. /// If (eOrg->Lnext == eDst), the old face is reduced to a single edge. /// If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges. /// </summary> public MeshUtils.Edge Connect(MeshUtils.Edge eOrg, MeshUtils.Edge eDst) { var eNew = MeshUtils.MakeEdge(eOrg); var eNewSym = eNew._Sym; bool joiningLoops = false; if (eDst._Lface != eOrg._Lface) { // We are connecting two disjoint loops -- destroy eDst->Lface joiningLoops = true; MeshUtils.KillFace(eDst._Lface, eOrg._Lface); } // Connect the new edge appropriately MeshUtils.Splice(eNew, eOrg._Lnext); MeshUtils.Splice(eNewSym, eDst); // Set the vertex and face information eNew._Org = eOrg._Dst; eNewSym._Org = eDst._Org; eNew._Lface = eNewSym._Lface = eOrg._Lface; // Make sure the old face points to a valid half-edge eOrg._Lface._anEdge = eNewSym; if (!joiningLoops) { MeshUtils.MakeFace(new MeshUtils.Face(), eNew, eOrg._Lface); } return eNew; }
/// <summary> /// Splits eOrg into two edges eOrg and eNew such that eNew == eOrg.Lnext. /// The new vertex is eOrg.Dst == eNew.Org. /// eOrg and eNew will have the same left face. /// </summary> public MeshUtils.Edge SplitEdge(MeshUtils.Edge eOrg) { var eTmp = AddEdgeVertex(eOrg); var eNew = eTmp._Sym; // Disconnect eOrg from eOrg->Dst and connect it to eNew->Org MeshUtils.Splice(eOrg._Sym, eOrg._Sym._Oprev); MeshUtils.Splice(eOrg._Sym, eNew); // Set the vertex and face information eOrg._Dst = eNew._Org; eNew._Dst._anEdge = eNew._Sym; // may have pointed to eOrg->Sym eNew._Rface = eOrg._Rface; eNew._winding = eOrg._winding; // copy old winding information eNew._Sym._winding = eOrg._Sym._winding; return eNew; }
public static bool VertEq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { return lhs._s == rhs._s && lhs._t == rhs._t; }
/// <summary> /// Returns a number whose sign matches EdgeEval(u,v,w) but which /// is cheaper to evaluate. Returns > 0, == 0 , or < 0 /// as v is above, on, or below the edge uw. /// </summary> public static float EdgeSign(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(VertLeq(u, v) && VertLeq(v, w)); float gapL = v._s - u._s; float gapR = w._s - v._s; if (gapL + gapR > 0.0f) { return (v._t - w._t) * gapL + (v._t - u._t) * gapR; } /* vertical line */ return 0.0f; }
public static bool TransLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { return (lhs._t < rhs._t) || (lhs._t == rhs._t && lhs._s <= rhs._s); }
public static float TransSign(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(TransLeq(u, v) && TransLeq(v, w)); float gapL = v._t - u._t; float gapR = w._t - v._t; if (gapL + gapR > 0.0f) { return (v._s - w._s) * gapL + (v._s - u._s) * gapR; } /* vertical line */ return 0.0f; }
/// <summary> /// Add a new active region to the sweep line, *somewhere* below "regAbove" /// (according to where the new edge belongs in the sweep-line dictionary). /// The upper edge of the new region will be "eNewUp". /// Winding number and "inside" flag are not updated. /// </summary> private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp) { var regNew = new ActiveRegion(); regNew._eUp = eNewUp; regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew); regNew._fixUpperEdge = false; regNew._sentinel = false; regNew._dirty = false; eNewUp._activeRegion = regNew; return regNew; }
public static bool EdgeGoesLeft(MeshUtils.Edge e) { return VertLeq(e._Dst, e._Org); }
/// <summary> /// Two vertices with idential coordinates are combined into one. /// e1.Org is kept, while e2.Org is discarded. /// </summary> private void SpliceMergeVertices(MeshUtils.Edge e1, MeshUtils.Edge e2) { _mesh.Splice(e1, e2); }
public static bool EdgeGoesRight(MeshUtils.Edge e) { return VertLeq(e._Org, e._Dst); }
/// <summary> /// We've computed a new intersection point, now we need a "data" pointer /// from the user so that we can refer to this new vertex in the /// rendering callbacks. /// </summary> private void GetIntersectData(MeshUtils.Vertex isect, MeshUtils.Vertex orgUp, MeshUtils.Vertex dstUp, MeshUtils.Vertex orgLo, MeshUtils.Vertex dstLo) { isect._coords = Vec3.Zero; float w0, w1, w2, w3; VertexWeights(isect, orgUp, dstUp, out w0, out w1); VertexWeights(isect, orgLo, dstLo, out w2, out w3); if (_combineCallback != null) { isect._data = _combineCallback( isect._coords, new object[] { orgUp._data, dstUp._data, orgLo._data, dstLo._data }, new float[] { w0, w1, w2, w3 } ); } }
public static float VertL1dist(MeshUtils.Vertex u, MeshUtils.Vertex v) { return Math.Abs(u._s - v._s) + Math.Abs(u._t - v._t); }
/// <summary> /// The event vertex lies exacty on an already-processed edge or vertex. /// Adding the new vertex involves splicing it into the already-processed /// part of the mesh. /// </summary> private void ConnectLeftDegenerate(ActiveRegion regUp, MeshUtils.Vertex vEvent) { var e = regUp._eUp; if (Geom.VertEq(e._Org, vEvent)) { // e.Org is an unprocessed vertex - just combine them, and wait // for e.Org to be pulled from the queue // C# : in the C version, there is a flag but it was never implemented // the vertices are before beginning the tesselation throw new InvalidOperationException("Vertices should have been merged before"); } if (!Geom.VertEq(e._Dst, vEvent)) { // General case -- splice vEvent into edge e which passes through it _mesh.SplitEdge(e._Sym); if (regUp._fixUpperEdge) { // This edge was fixable -- delete unused portion of original edge _mesh.Delete(e._Onext); regUp._fixUpperEdge = false; } _mesh.Splice(vEvent._anEdge, e); SweepEvent(vEvent); // recurse return; } // See above throw new InvalidOperationException("Vertices should have been merged before"); }
public static void AddWinding(MeshUtils.Edge eDst, MeshUtils.Edge eSrc) { eDst._winding += eSrc._winding; eDst._Sym._winding += eSrc._Sym._winding; }
/// <summary> /// Does everything necessary when the sweep line crosses a vertex. /// Updates the mesh and the edge dictionary. /// </summary> private void SweepEvent(MeshUtils.Vertex vEvent) { _event = vEvent; // Check if this vertex is the right endpoint of an edge that is // already in the dictionary. In this case we don't need to waste // time searching for the location to insert new edges. var e = vEvent._anEdge; while (e._activeRegion == null) { e = e._Onext; if (e == vEvent._anEdge) { // All edges go right -- not incident to any processed edges ConnectLeftVertex(vEvent); return; } } // Processing consists of two phases: first we "finish" all the // active regions where both the upper and lower edges terminate // at vEvent (ie. vEvent is closing off these regions). // We mark these faces "inside" or "outside" the polygon according // to their winding number, and delete the edges from the dictionary. // This takes care of all the left-going edges from vEvent. var regUp = TopLeftRegion(e._activeRegion); var reg = RegionBelow(regUp); var eTopLeft = reg._eUp; var eBottomLeft = FinishLeftRegions(reg, null); // Next we process all the right-going edges from vEvent. This // involves adding the edges to the dictionary, and creating the // associated "active regions" which record information about the // regions between adjacent dictionary edges. if (eBottomLeft._Onext == eTopLeft) { // No right-going edges -- add a temporary "fixable" edge ConnectRightVertex(regUp, eBottomLeft); } else { AddRightEdges(regUp, eBottomLeft._Onext, eTopLeft, eTopLeft, true); } }
private int GetNeighbourFace(MeshUtils.Edge edge) { if (edge._Rface == null) return MeshUtils.Undef; if (!edge._Rface._inside) return MeshUtils.Undef; return edge._Rface._n; }