static bool SplitIsRobust(float2 point, Edge *edge) { // check both sides for highly irregular tris // todo try to remove assert from InsertpointInEdge/FlipEdges as this should only happen with constrained edges? return(Math.TriArea(edge->Org->Point, edge->Dest->Point, point) == 0 || Math.TriArea(edge->LNext->Org->Point, edge->LNext->Dest->Point, point) > 0 && Math.TriArea(edge->LPrev->Org->Point, edge->LPrev->Dest->Point, point) > 0 && Math.TriArea(edge->RNext->Org->Point, edge->RNext->Dest->Point, point) < 0 && Math.TriArea(edge->RPrev->Org->Point, edge->RPrev->Dest->Point, point) < 0); // var o = edge->Org->Point; // var d = edge->Dest->Point; // // if (Math.TriArea(o, d, point) == 0) // return true; // // if (Math.TriArea(o, d, point) > 0 && // Math.TriArea(edge->LNext->Org->Point, edge->LNext->Dest->Point, point) > 0 && // Math.TriArea(edge->LPrev->Org->Point, edge->LPrev->Dest->Point, point) > 0) // return true; // // if (Math.TriArea(o, d, point) < 0 && // Math.TriArea(edge->RNext->Org->Point, edge->RNext->Dest->Point, point) < 0 && // Math.TriArea(edge->RPrev->Org->Point, edge->RPrev->Dest->Point, point) < 0) // return true; // // return false; }
/// <summary> /// Returns an edge for which the specified point is contained within it's left face. If the point lies /// on an edge this edge is returned. If the point lies on a vertex an arbitrary edge with identical origin is returned. /// </summary> /// <param name="collinear">True when the specified point lies on the returned edge</param> public Edge *FindTriangleContainingPoint(float2 p, out bool collinear) { var e = FindClosestVertex(p)->Edge; Assert.IsTrue(e != null); InfiniteLoopDetection.Reset(); while (true) { InfiniteLoopDetection.Register(1000, "FindTriangleContainingPoint"); Edge *collinearEdge = null; var orient = Math.TriArea(e->Org->Point, e->Dest->Point, p); if (orient == 0) { collinearEdge = e; } else if (orient < 0) { e = e->Sym; continue; } orient = Math.TriArea(e->ONext->Org->Point, e->ONext->Dest->Point, p); if (orient == 0) { collinearEdge = e->ONext; } else if (orient > 0) { e = e->ONext; continue; } orient = Math.TriArea(e->DPrev->Org->Point, e->DPrev->Dest->Point, p); if (orient == 0) { collinear = true; return(e->DPrev); } if (orient > 0) { e = e->DPrev; continue; } if (collinearEdge != null) { collinear = true; return(collinearEdge); } collinear = false; return(e); } }
void InsertSegmentNoConstraints(Vertex *a, Vertex *b, Entity id) { var c = GetConnection(a, b); if (c != null) { C.TryAdd((IntPtr)c); if (!c->IsConstrainedBy(id)) { c->AddConstraint(id); } ResetClearance(c); return; } var e = GetLeftEdge(a, b->Point); InfiniteLoopDetection.Reset(); while (e->Dest != b) { InfiniteLoopDetection.Register(1000, "InsertSegmentNoConstraints"); var d = Math.TriArea(a->Point, b->Point, e->Dest->Point); var next = d > 0 ? e->RPrev : e->ONext; if (d < 0) { Assert.IsTrue(!e->Constrained); RemoveEdge(e); } else if (d == 0 && e->Dest != a) { var t = e->Dest; Connect(a, t, id); a = t; } e = next; } Connect(a, b, id); }
void RemoveIfEligible(Vertex *v) { if (v->PointConstraints > 0 || v->ConstraintHandles > 0) { return; } var amount = 0; var constrained = stackalloc Edge *[2]; var e = v->GetEdgeEnumerator(); while (e.MoveNext()) { if (e.Current->Constrained) { if (amount == 2) { return; } constrained[amount++] = e.Current; } } if (amount == 0) { e = v->GetEdgeEnumerator(); while (e.MoveNext()) { V.TryAdd((IntPtr)e.Current->Dest); } var face = RemoveVertex(v); RetriangulateFace(face); return; } if (amount != 2 || !constrained[0]->ConstraintsEqual(constrained[1])) { return; } var e1 = constrained[0]; var e2 = constrained[1]; Assert.IsTrue(e1->Dest != v && e2->Dest != v); Assert.IsTrue(e1->Dest != e2->Dest); var d1 = e1->Dest->Point; var d2 = e2->Dest->Point; var collinear = Math.TriArea(d1, d2, v->Point); if (collinear == 0) { e = v->GetEdgeEnumerator(); while (e.MoveNext()) { V.TryAdd((IntPtr)e.Current->Dest); } var v1 = e1->Dest; var v2 = e2->Dest; var crep = e1->QuadEdge->Crep; RemoveEdge(e1, false); RemoveVertex(v); var e3 = Connect(v1, v2); RetriangulateFace(e3); RetriangulateFace(e3->Sym); e3->QuadEdge->Crep = crep; } else { var t = collinear / math.length(d2 - d1); if (collinear > 0) { if (t < _collinearMargin && Math.TriArea(d1, d2, e1->DPrev->Org->Point) < 0 && Math.TriArea(d1, d2, e2->DNext->Org->Point) < 0) { e = v->GetEdgeEnumerator(); while (e.MoveNext()) { V.TryAdd((IntPtr)e.Current->Dest); } RemoveSemiCollinear(v, e1, e2); } } else if (t > -_collinearMargin && Math.TriArea(d1, d2, e1->DNext->Org->Point) > 0 && Math.TriArea(d1, d2, e2->DPrev->Org->Point) > 0) { e = v->GetEdgeEnumerator(); while (e.MoveNext()) { V.TryAdd((IntPtr)e.Current->Dest); } RemoveSemiCollinear(v, e1, e2); } } }
Point GetNextPoint(Vertex *a, Vertex *b, float2 start, float2 end) { InfiniteLoopDetection.Reset(); var e = GetLeftEdge(a, b->Point); while (e->Dest != b) { InfiniteLoopDetection.Register(1000, "GetNextPoint"); var d = Math.TriArea(a->Point, b->Point, e->Dest->Point); if (d < 0 && e->Constrained) { var p = (float2)Math.IntersectLineSegClamped(start, end, e->Org->Point, e->Dest->Point); var pointExists = TryGetPoint(p, e, out var v); if (v != null) { if (_insertedPoints.Length > 1) { var prev = _insertedPoints[_insertedPoints.Length - 1].Vertex; if (prev == v || e->Org == prev || e->Dest == prev) { continue; } } if (_insertedPoints.Length > 2) { var prev = _insertedPoints[_insertedPoints.Length - 2].Vertex; if (prev == v || e->Org == prev || e->Dest == prev) { continue; } } return(new Point { Vertex = v, FoundExisting = true, P = p }); } if (pointExists || !SplitIsRobust(p, e)) { var pRef = CreatePRef(p, e); if (_insertedPoints.Length > 1 && _insertedPoints[_insertedPoints.Length - 1].Vertex == pRef) { continue; } var point = new Point { Vertex = pRef, Modified = true, P = p }; var proj = (float2)Math.ProjectLine(a->Point, b->Point, point.Vertex->Point); var pproj = proj - p; if (math.dot(b - a, pproj) < 0) { point.Before = proj; point.After = p; } else { point.Before = p; point.After = proj; } return(point); } var vert = InsertPointInEdge(p, e); return(new Point { Vertex = vert, P = p }); } e = d > 0 ? e->RPrev : e->ONext; } return(new Point { Vertex = b, P = b->Point }); }
Vertex *InsertPoint(float2 p) { var closest = _qt.FindClosest(p); if (math.lengthsq(closest->Point - p) <= _e * _e) { return(closest); } var e = closest->Edge; Assert.IsTrue(e != null); InfiniteLoopDetection.Reset(); while (true) { InfiniteLoopDetection.Register(1000, "InsertPoint"); Edge *inEdge = null; var orient = Math.TriArea(e->Org->Point, e->Dest->Point, p); if (orient == 0) { inEdge = e; } if (orient < 0) { e = e->Sym; continue; } orient = Math.TriArea(e->ONext->Org->Point, e->ONext->Dest->Point, p); if (orient == 0) { inEdge = e->ONext; } if (orient > 0) { e = e->ONext; continue; } orient = Math.TriArea(e->DPrev->Org->Point, e->DPrev->Dest->Point, p); if (orient == 0) { inEdge = e->DPrev; } if (orient > 0) { e = e->DPrev; continue; } if (inEdge != null) { Assert.IsTrue(SplitIsRobust(p, inEdge)); return(InsertPointInEdge(p, inEdge)); } return(InsertPointInFace(p, e)); } }