public void Check() { MeshUtils.Edge e; MeshUtils.Face fPrev = _fHead, f; for (fPrev = _fHead; (f = fPrev._next) != _fHead; fPrev = f) { e = f._anEdge; do { Debug.Assert(e._Sym != e); Debug.Assert(e._Sym._Sym == e); Debug.Assert(e._Lnext._Onext._Sym == e); Debug.Assert(e._Onext._Sym._Lnext == e); Debug.Assert(e._Lface == f); e = e._Lnext; } while (e != f._anEdge); } Debug.Assert(f._prev == fPrev && f._anEdge == null); MeshUtils.Vertex vPrev = _vHead, v; for (vPrev = _vHead; (v = vPrev._next) != _vHead; vPrev = v) { Debug.Assert(v._prev == vPrev); e = v._anEdge; do { Debug.Assert(e._Sym != e); Debug.Assert(e._Sym._Sym == e); Debug.Assert(e._Lnext._Onext._Sym == e); Debug.Assert(e._Onext._Sym._Lnext == e); Debug.Assert(e._Org == v); e = e._Onext; } while (e != v._anEdge); } Debug.Assert(v._prev == vPrev && v._anEdge == null); MeshUtils.Edge ePrev = _eHead; for (ePrev = _eHead; (e = ePrev._next) != _eHead; ePrev = e) { Debug.Assert(e._Sym._next == ePrev._Sym); Debug.Assert(e._Sym != e); Debug.Assert(e._Sym._Sym == e); Debug.Assert(e._Org != null); Debug.Assert(e._Dst != null); Debug.Assert(e._Lnext._Onext._Sym == e); Debug.Assert(e._Onext._Sym._Lnext == e); } Debug.Assert(e._Sym._next == ePrev._Sym && e._Sym == _eHeadSym && e._Sym._Sym == e && e._Org == null && e._Dst == null && e._Lface == null && e._Rface == null); }
/// <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 static void ZapFace(IPool pool, MeshUtils.Face fZap) { MeshUtils.Edge eStart = fZap._anEdge; // walk around face, deleting edges whose right face is also NULL MeshUtils.Edge eNext = eStart._Lnext; MeshUtils.Edge e; do { e = eNext; eNext = e._Lnext; e._Lface = null; switch (e._Rface) { case null: { // delete the edge -- see TESSmeshDelete above if (e._Onext == e) { MeshUtils.KillVertex(pool, 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); } MeshUtils.Edge eSym = e._Sym; if (eSym._Onext == eSym) { MeshUtils.KillVertex(pool, 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(pool, e); break; } } } while (e != eStart); /* delete from circular doubly-linked list */ MeshUtils.Face fPrev = fZap._prev; MeshUtils.Face fNext = fZap._next; fNext._prev = fPrev; fPrev._next = fNext; pool.Return(fZap); }
/// <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; fZap.Free(); }
public override void Reset() { MeshUtils.Face f = _fHead._next; while (true) { MeshUtils.Face fNext = f._next; f.Free(); if (f == _fHead) { break; } f = fNext; } MeshUtils.Vertex v = _vHead._next; while (true) { MeshUtils.Vertex vNext = v._next; v.Free(); if (v == _vHead) { break; } v = vNext; } for (int i = 0; i < _allEdgePairs.Count; i++) { MeshUtils.EdgePair pair = _allEdgePairs[i]; MeshUtils.Edge e = pair._e; if (!e.IsReturnedToPool()) // (can be Free in KillEdge) { e.Free(); } MeshUtils.Edge eSym = pair._eSym; if (!eSym.IsReturnedToPool()) // (can be Free in KillEdge) { eSym.Free(); } pair.Free(); } _allEdgePairs.Clear(); _vHead = null; _fHead = null; _eHead = _eHeadSym = null; }
/// <summary> /// TessellateMonoRegion( face ) 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). /// </summary> private void TessellateMonoRegion(MeshUtils.Face face) { // 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. var up = face._anEdge; Debug.Assert(up._Lnext != up && up._Lnext._Lnext != up); while (Geom.VertLeq(up._Dst, up._Org)) up = up._Lprev; while (Geom.VertLeq(up._Org, up._Dst)) up = up._Lnext; var lo = up._Lprev; while (up._Lnext != lo) { if (Geom.VertLeq(up._Dst, lo._Org)) { // 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._Lnext != up && (Geom.EdgeGoesLeft(lo._Lnext) || Geom.EdgeSign(lo._Org, lo._Dst, lo._Lnext._Dst) <= 0.0f)) { lo = _mesh.Connect(lo._Lnext, lo)._Sym; } lo = lo._Lprev; } else { // lo.Org is on the left. We can make CCW triangles from up.Dst. while (lo._Lnext != up && (Geom.EdgeGoesRight(up._Lprev) || Geom.EdgeSign(up._Dst, up._Org, up._Lprev._Org) >= 0.0f)) { up = _mesh.Connect(up, up._Lprev)._Sym; } up = up._Lnext; } } // Now lo.Org == up.Dst == the leftmost vertex. The remaining region // can be tessellated in a fan from this leftmost vertex. Debug.Assert(lo._Lnext != up); while (lo._Lnext._Lnext != up) { lo = _mesh.Connect(lo._Lnext, lo)._Sym; } }
public override void OnFree() { for (MeshUtils.Face f = _fHead._next, fNext = _fHead; f != _fHead; f = fNext) { fNext = f._next; f.Free(); } for (MeshUtils.Vertex v = _vHead._next, vNext = _vHead; v != _vHead; v = vNext) { vNext = v._next; v.Free(); } for (MeshUtils.Edge e = _eHead._next, eNext = _eHead; e != _eHead; e = eNext) { eNext = e._next; e.Free(); } }
public void MergeConvexFaces(IPool pool, int maxVertsPerFace) { for (MeshUtils.Face f = _fHead._next; f != _fHead; f = f._next) { switch (f._inside) { // Skip faces which are outside the result case false: continue; } MeshUtils.Edge eCur = f._anEdge; MeshUtils.Vertex vStart = eCur._Org; while (true) { MeshUtils.Edge eNext = eCur._Lnext; MeshUtils.Edge eSym = eCur._Sym; if (eSym is { _Lface._inside: true })
public void Reset(IPool pool) { for (MeshUtils.Face f = _fHead, fNext = _fHead; f._next != null; f = fNext) { fNext = f._next; pool.Return(f); } for (MeshUtils.Vertex v = _vHead, vNext = _vHead; v._next != null; v = vNext) { vNext = v._next; pool.Return(v); } for (MeshUtils.Edge e = _eHead, eNext = _eHead; e._next != null; e = eNext) { eNext = e._next; pool.Return(e._Sym); pool.Return(e); } _vHead = null; _fHead = null; _eHead = _eHeadSym = null; }
public void Init(IPool pool) { var v = _vHead = pool.Get <MeshUtils.Vertex>(); var f = _fHead = pool.Get <MeshUtils.Face>(); var pair = MeshUtils.EdgePair.Create(pool); var e = _eHead = pair._e; var eSym = _eHeadSym = pair._eSym; v._next = v._prev = v; v._anEdge = null; f._next = f._prev = f; f._anEdge = null; f._trail = null; f._marked = false; f._inside = false; e._next = e; e._Sym = eSym; e._Onext = null; e._Lnext = null; e._Org = null; e._Lface = null; e._winding = 0; e._activeRegion = null; eSym._next = eSym; eSym._Sym = e; eSym._Onext = null; eSym._Lnext = null; eSym._Org = null; eSym._Lface = null; eSym._winding = 0; eSym._activeRegion = null; }
// ReSharper restore InconsistentNaming public Mesh() { var v = _vHead = new MeshUtils.Vertex(); var f = _fHead = new MeshUtils.Face(); var pair = MeshUtils.EdgePair.Create(); var e = _eHead = pair._e; var eSym = _eHeadSym = pair._eSym; v._next = v._prev = v; v._anEdge = null; f._next = f._prev = f; f._anEdge = null; f._trail = null; f._marked = false; f._inside = false; e._next = e; e._Sym = eSym; e._Onext = null; e._Lnext = null; e._Org = null; e._Lface = null; e._winding = 0; e._activeRegion = null; eSym._next = eSym; eSym._Sym = e; eSym._Onext = null; eSym._Lnext = null; eSym._Org = null; eSym._Lface = null; eSym._winding = 0; eSym._activeRegion = null; }
public override void Reset() { _vHead = null; _fHead = null; _eHead = _eHeadSym = null; }