using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Msagl.Core; using Microsoft.Msagl.Core.DataStructures; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; using Microsoft.Msagl.Core.Layout; using Microsoft.Msagl.DebugHelpers; namespace Microsoft.Msagl.Routing.ConstrainedDelaunayTriangulation { /// /// this class builds the triangulation by a sweep with a horizontal line /// public class CdtSweeper:AlgorithmBase { RbTree front = new RbTree((a, b) => a.X.CompareTo(b.X)); readonly Set triangles=new Set(); List listOfSites; CdtSite p_2; readonly Func createEdgeDelegate; CdtSite p_1; internal CdtSweeper(List listOfSites, CdtSite p_1, CdtSite p_2, Func createEdgeDelegate) { this.listOfSites=listOfSites; if (listOfSites.Count == 0) return; var firstTriangle=new CdtTriangle(p_1, p_2, listOfSites[0], createEdgeDelegate); Triangles.Insert( firstTriangle ); front.Insert(new CdtFrontElement(p_1, firstTriangle.Edges[2] )); front.Insert(new CdtFrontElement(listOfSites[0], firstTriangle.Edges[1])); this.p_1 = p_1; this.p_2 = p_2; this.createEdgeDelegate = createEdgeDelegate; //ShowFront(); } internal Set Triangles { get { return triangles; } } /// /// the method making the main work /// protected override void RunInternal() { if (listOfSites.Count == 0) return; for(int i=1;i(); foreach (var t in triangles) if (t.Sites.Contains(p_1) || t.Sites.Contains(p_2)) trianglesToRemove.Insert(t); foreach (var t in trianglesToRemove) RemoveTriangleWithEdges(triangles, t); } internal static void RemoveTriangleWithEdges(Set cdtTriangles, CdtTriangle t) { cdtTriangles.Remove(t); foreach (var e in t.Edges) { if (e.CwTriangle == t) e.CwTriangle = null; else e.CcwTriangle = null; if (e.CwTriangle == null && e.CcwTriangle == null) e.upperSite.Edges.Remove(e); } } internal static void RemoveTriangleButLeaveEdges(Set cdtTriangles, CdtTriangle t) { cdtTriangles.Remove(t); foreach (var e in t.Edges) if (e.CwTriangle == t) e.CwTriangle = null; else e.CcwTriangle = null; } void ProcessSite(CdtSite site) { PointEvent(site); //ShowFrontWithSite(site); for (int i = 0; i < site.Edges.Count; i++) { var edge = site.Edges[i]; if (edge.Constrained) EdgeEvent(edge); } // TestThatFrontIsConnected(); } #if DEBUG&& TEST_MSAGL void TestThatFrontIsConnected() { CdtFrontElement p = null; foreach (var cdtFrontElement in front) { if(p!=null) Debug.Assert(p.RightSite==cdtFrontElement.LeftSite); p = cdtFrontElement; } } #endif void EdgeEvent(CdtEdge edge) { Debug.Assert(edge.Constrained); if(EdgeIsProcessed(edge)) return; var edgeInserter = new EdgeInserter(edge, Triangles, front, createEdgeDelegate); edgeInserter.Run(); } static bool EdgeIsProcessed(CdtEdge edge) { return edge.CwTriangle != null || edge.CcwTriangle != null; } // #if DEBUG && TEST_MSAGL // void ShowFrontWithSite(CdtSite site, params ICurve[] redCurves) { // var ls = new List(); // // // if (site.Edges != null) // foreach (var e in site.Edges) // ls.Add(new DebugCurve(100, 0.001, e.Constrained?"pink":"brown", new LineSegment(e.upperSite.Point, e.lowerSite.Point))); // // ls.Add(new DebugCurve(100,0.01,"brown", new Ellipse(0.5,0.5,site.Point))); // // foreach (var t in Triangles) { // for (int i = 0; i < 3; i++) { // var e = t.Edges[i]; // ls.Add(new DebugCurve(e.Constrained?(byte)150:(byte)50, e.Constrained?0.002:0.001, e.Constrained? "pink":"navy", new LineSegment(e.upperSite.Point, e.lowerSite.Point))); // } // } // // foreach (var c in redCurves) // ls.Add(new DebugCurve(100, 0.005, "red", c)); // // // foreach (var frontElement in front) // ls.Add(new DebugCurve(100, 0.005, "green", // new LineSegment(frontElement.Edge.upperSite.Point, // frontElement.Edge.lowerSite.Point))); // LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls); // } // void ShowFront() { // ShowFront(Triangles, front, null, null); // } // // internal static void ShowFront(IEnumerable cdtTriangles, RbTree cdtFrontElements, IEnumerable redCurves, IEnumerable blueCurves) { // List ls = new List(); // if (redCurves != null) // foreach (var c in redCurves) // ls.Add(new DebugCurve(100, 0.5, "red", c)); // if (blueCurves != null) // foreach (var c in blueCurves) // ls.Add(new DebugCurve(100, 2, "blue", c)); // // if (cdtFrontElements != null) // foreach (var frontElement in cdtFrontElements) // ls.Add(new DebugCurve(100, 0.001, "green", // new LineSegment(frontElement.Edge.upperSite.Point, // frontElement.Edge.lowerSite.Point))); // foreach (var t in cdtTriangles) { // for (int i = 0; i < 3; i++) { // var e = t.Edges[i]; // ls.Add(GetDebugCurveOfCdtEdge(e)); // } // } // LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls); // } // // static DebugCurve GetDebugCurveOfCdtEdge(CdtEdge e) { // if(e.CcwTriangle==null || e.CwTriangle==null) // return new DebugCurve(255, 4, e.Constrained ? "brown" : "blue", new LineSegment(e.upperSite.Point, e.lowerSite.Point)); // return new DebugCurve(100, e.Constrained?0.002:0.001, e.Constrained?"pink":"navy", new LineSegment(e.upperSite.Point, e.lowerSite.Point)); // } // #endif // int count; // bool db { get { return count == 147; } } void PointEvent(CdtSite pi) { RBNode hittedFrontElementNode; ProjectToFront(pi, out hittedFrontElementNode); CdtSite rightSite; CdtSite leftSite = hittedFrontElementNode.Item.X + ApproximateComparer.DistanceEpsilon < pi.Point.X ? MiddleCase(pi, hittedFrontElementNode, out rightSite) : LeftCase(pi, hittedFrontElementNode, out rightSite); var piNode = InsertSiteIntoFront(leftSite, pi, rightSite); TriangulateEmptySpaceToTheRight(piNode); piNode = FindNodeInFrontBySite(front, leftSite); TriangulateEmptySpaceToTheLeft(piNode); } // #if DEBUG && TEST_MSAGL // void TestTriangles() { // var usedSites = new Set(); // foreach(var t in Triangles) // usedSites.InsertRange(t.Sites); // foreach (var triangle in Triangles) { // TestTriangle(triangle, usedSites); // } // } // // void TestTriangle(CdtTriangle triangle, Set usedSites) { // var tsites=triangle.Sites; // foreach (var site in usedSites) { // if (!tsites.Contains(site)) { // if (!SeparatedByConstrainedEdge(triangle, site) && InCircle(site, tsites[0], tsites[1], tsites[2])) { // List redCurves=new List(); // redCurves.Add(new Ellipse(2, 2, site.Point)); // List blueCurves = new List(); // blueCurves.Add(Circumcircle(tsites[0].Point,tsites[1].Point,tsites[2].Point)); // ShowFront(Triangles, front, redCurves, blueCurves); // } // } // } // } // // static bool SeparatedByConstrainedEdge(CdtTriangle triangle, CdtSite site) { // for (int i = 0; i < 3; i++) // if (SeparatedByEdge(triangle, i, site)) // return true; // return false; // } // // static bool SeparatedByEdge(CdtTriangle triangle, int i, CdtSite site) { // var e = triangle.Edges[i]; // var s = triangle.Sites[i+2]; // var a0 = ApproximateComparer.Sign(Point.SignedDoubledTriangleArea(s.Point, e.upperSite.Point, e.lowerSite.Point)); // var a1 = ApproximateComparer.Sign(Point.SignedDoubledTriangleArea(site.Point, e.upperSite.Point, e.lowerSite.Point)); // return a0 * a1 <= 0; // } // #endif CdtSite LeftCase(CdtSite pi, RBNode hittedFrontElementNode, out CdtSite rightSite) { //left case // if(db)ShowFrontWithSite(pi, new LineSegment(pi.Point, hittedFrontElementNode.Item.Edge.upperSite.Point), new LineSegment(pi.Point, hittedFrontElementNode.Item.Edge.lowerSite.Point)); Debug.Assert(ApproximateComparer.Close(pi.Point.X, hittedFrontElementNode.Item.X)); var hittedFrontElement = hittedFrontElementNode.Item; InsertAndLegalizeTriangle(pi, hittedFrontElement); var prevToHitted = front.Previous(hittedFrontElementNode); var leftSite = prevToHitted.Item.LeftSite; rightSite = hittedFrontElementNode.Item.RightSite; // if(db)ShowFrontWithSite(pi, new LineSegment(pi.Point, leftSite.Point), new LineSegment(pi.Point, prevToHitted.Item.RightSite.Point)); InsertAndLegalizeTriangle(pi, prevToHitted.Item); front.DeleteNodeInternal(prevToHitted); var d = front.Remove(hittedFrontElement); Debug.Assert(d != null); return leftSite; } CdtSite MiddleCase(CdtSite pi, RBNode hittedFrontElementNode, out CdtSite rightSite) { // if(db) // ShowFrontWithSite(pi, new LineSegment(pi.Point, hittedFrontElementNode.Item.Edge.upperSite.Point), new LineSegment(pi.Point, hittedFrontElementNode.Item.Edge.lowerSite.Point)); var leftSite = hittedFrontElementNode.Item.LeftSite; rightSite = hittedFrontElementNode.Item.RightSite; InsertAndLegalizeTriangle(pi, hittedFrontElementNode.Item); front.DeleteNodeInternal(hittedFrontElementNode); return leftSite; } void TriangulateEmptySpaceToTheLeft(RBNode leftLegNode) { var peakSite = leftLegNode.Item.RightSite; var previousNode = front.Previous(leftLegNode); while (previousNode != null) { var prevElement = previousNode.Item; var rp = prevElement.LeftSite; var r=prevElement.RightSite; if ((r.Point - peakSite.Point) * (rp.Point - r.Point) < 0) { //see figures 9(a) and 9(b) of the paper leftLegNode=ShortcutTwoFrontElements(previousNode, leftLegNode); previousNode = front.Previous(leftLegNode); } else { TryTriangulateBasinToTheLeft(leftLegNode); break; } } } PerimeterEdge ShortcutTwoListElements(PerimeterEdge a) { var b = a.Next; Debug.Assert(a.End == b.Start); var t = new CdtTriangle(a.Start, a.End, b.End, a.Edge, b.Edge, createEdgeDelegate); Triangles.Insert(t); var newEdge = t.Edges[2]; Debug.Assert(newEdge.IsAdjacent(a.Start) && newEdge.IsAdjacent(b.End)); LegalizeEdge(a.Start, t.OppositeEdge(a.Start)); t = newEdge.CcwTriangle ?? newEdge.CwTriangle; LegalizeEdge(b.End, t.OppositeEdge(b.End)); var c = new PerimeterEdge(newEdge) { Start = a.Start, End = b.End }; a.Prev.Next = c; c.Prev = a.Prev; c.Next = b.Next; b.Next.Prev = c; return c; } /// /// aNode is to the left of bNode, and they are consecutive /// /// /// RBNode ShortcutTwoFrontElements(RBNode aNode, RBNode bNode) { var aElem = aNode.Item; var bElem = bNode.Item; Debug.Assert(aElem.RightSite == bElem.LeftSite); CdtTriangle t = new CdtTriangle(aElem.LeftSite, aElem.RightSite, bElem.RightSite, aElem.Edge, bElem.Edge, createEdgeDelegate); Triangles.Insert(t); front.DeleteNodeInternal(aNode); //now bNode might b not valid anymore front.Remove(bElem); var newEdge = t.Edges[2]; Debug.Assert(newEdge.IsAdjacent( aElem.LeftSite) && newEdge.IsAdjacent(bElem.RightSite)); LegalizeEdge(aElem.LeftSite, t.OppositeEdge(aElem.LeftSite)); t=newEdge.CcwTriangle ?? newEdge.CwTriangle; LegalizeEdge(bElem.RightSite, t.OppositeEdge(bElem.RightSite)); return front.Insert(new CdtFrontElement(aElem.LeftSite, newEdge)); } void TryTriangulateBasinToTheLeft(RBNode leftLegNode) { if (!DropsSharpEnoughToTheLeft(leftLegNode.Item)) return; //ShowFrontWithSite(leftLegNode.Item.LeftSite); var stack = new Stack(); stack.Push(leftLegNode.Item.LeftSite); while (true) { var site = stack.Pop(); leftLegNode = FindNodeInFrontBySite(front, site); var prev = front.Previous(leftLegNode); if (prev == null) return; if (Point.GetTriangleOrientationWithNoEpsilon(prev.Item.LeftSite.Point, leftLegNode.Item.LeftSite.Point, leftLegNode.Item.RightSite.Point) == TriangleOrientation.Counterclockwise) { stack.Push(prev.Item.LeftSite); ShortcutTwoFrontElements(prev, leftLegNode); // ShowFrontWithSite(site); } else { if (leftLegNode.Item.LeftSite.Point.Y > leftLegNode.Item.RightSite.Point.Y) { stack.Push(prev.Item.LeftSite); } else { if (prev.Item.LeftSite.Point.Y <= prev.Item.RightSite.Point.Y) return; stack.Push(prev.Item.LeftSite); } } } } static bool DropsSharpEnoughToTheLeft(CdtFrontElement frontElement) { var edge = frontElement.Edge; if (frontElement.RightSite != edge.upperSite) return false; var d = edge.lowerSite.Point - edge.upperSite.Point; Debug.Assert(d.X < 0 && d.Y <= 0); return d.X >= 0.5 * d.Y; } RBNode InsertSiteIntoFront(CdtSite leftSite, CdtSite pi, CdtSite rightSite) { CdtEdge leftEdge = null, rightEdge = null; foreach (var edge in pi.Edges) { if (leftEdge==null && edge.lowerSite == leftSite) leftEdge = edge; if ( rightEdge==null && edge.lowerSite == rightSite) rightEdge = edge; if (leftEdge != null && rightEdge != null) break; } Debug.Assert(leftEdge!=null && rightEdge!=null); front.Insert(new CdtFrontElement(leftSite, leftEdge)); return front.Insert(new CdtFrontElement(pi, rightEdge)); } void TriangulateEmptySpaceToTheRight(RBNode piNode) { var piSite=piNode.Item.LeftSite; var piPoint=piSite.Point; var piNext = front.Next(piNode); while (piNext != null) { var frontElem=piNext.Item; var r=frontElem.LeftSite; var rp = frontElem.RightSite; if ((r.Point - piPoint) * (rp.Point - r.Point) < 0) { //see figures 9(a) and 9(b) of the paper piNode = ShortcutTwoFrontElements(piNode, piNext); piNext = front.Next(piNode); } else { TryTriangulateBasinToTheRight(piNode); break; } } } void TryTriangulateBasinToTheRight(RBNode piNode) { if (!DropsSharpEnoughToTheRight(piNode.Item)) return; // ShowFrontWithSite(piNode.Item.LeftSite); var stack = new Stack(); stack.Push(piNode.Item.LeftSite); while(true) { var site = stack.Pop(); piNode = FindNodeInFrontBySite(front, site); var next = front.Next(piNode); if (next == null) return; if (Point.GetTriangleOrientationWithNoEpsilon(piNode.Item.LeftSite.Point, piNode.Item.RightSite.Point, next.Item.RightSite.Point) == TriangleOrientation.Counterclockwise) { ShortcutTwoFrontElements(piNode, next); stack.Push(site); } else { if (piNode.Item.LeftSite.Point.Y > piNode.Item.RightSite.Point.Y) { stack.Push(piNode.Item.RightSite); } else { if (next.Item.LeftSite.Point.Y >= next.Item.RightSite.Point.Y) return; stack.Push(piNode.Item.RightSite); } } } } static bool DropsSharpEnoughToTheRight(CdtFrontElement frontElement) { var edge = frontElement.Edge; if(frontElement.LeftSite!=edge.upperSite) return false; var d=edge.lowerSite.Point-edge.upperSite.Point; Debug.Assert(d.X > 0 && d.Y <= 0); return d.X <= -0.5 * d.Y; } internal static RBNode FindNodeInFrontBySite(RbTree cdtFrontElements, CdtSite piSite) { return cdtFrontElements.FindLast(x => x.LeftSite.Point.X <= piSite.Point.X); } void InsertAndLegalizeTriangle(CdtSite pi, CdtFrontElement frontElement) { if (Point.GetTriangleOrientationWithNoEpsilon(pi.Point, frontElement.LeftSite.Point, frontElement.RightSite.Point) != TriangleOrientation.Collinear) { var tr = new CdtTriangle(pi, frontElement.Edge, createEdgeDelegate); Triangles.Insert(tr); LegalizeEdge(pi, tr.Edges[0]); } else { //we need to split the triangle below the element in to two triangles and legalize the old edges //we also delete, that is forget, the frontElement.Edge var e = frontElement.Edge; e.upperSite.Edges.Remove(e); var t=e.CcwTriangle??e.CwTriangle; var oppositeSite = t.OppositeSite(e); RemoveTriangleButLeaveEdges(triangles, t); t=new CdtTriangle(frontElement.LeftSite, oppositeSite, pi, createEdgeDelegate); var t1 = new CdtTriangle(frontElement.RightSite, oppositeSite, pi, createEdgeDelegate); triangles.Insert(t); triangles.Insert(t1); LegalizeEdge(pi, t.OppositeEdge(pi)); LegalizeEdge(pi,t1.OppositeEdge(pi)); } } void LegalizeEdge(CdtSite pi, CdtEdge edge) { Debug.Assert(pi!=edge.upperSite && pi!=edge.lowerSite); if (edge.Constrained || edge.CcwTriangle == null || edge.CwTriangle == null) return; if (edge.CcwTriangle.Contains(pi)) LegalizeEdgeForOtherCwTriangle(pi, edge); else LegalizeEdgeForOtherCcwTriangle(pi, edge); } void LegalizeEdgeForOtherCcwTriangle(CdtSite pi, CdtEdge edge) { var i = edge.CcwTriangle.Edges.Index(edge); if (IsIllegal(pi, edge.lowerSite, edge.CcwTriangle.Sites[i + 2], edge.upperSite)) { CdtEdge e = Flip(pi, edge); LegalizeEdge(pi, e.CwTriangle.OppositeEdge(pi)); LegalizeEdge(pi, e.CcwTriangle.OppositeEdge(pi)); } } // #if DEBUG && TEST_MSAGL // List ShowIllegalEdge(CdtEdge edge, CdtSite pi, int i) { // List ls = new List(); // ls.Add(new DebugCurve(new Ellipse(2, 2, pi.Point))); // for (int j = 0; j < 3; j++) { // var ee = edge.CcwTriangle.Edges[j]; // ls.Add(new DebugCurve(j == i ? "red" : "blue", new LineSegment(ee.upperSite.Point, ee.lowerSite.Point))); // } // ls.Add(new DebugCurve(100,1, "black", Circumcircle(edge.CcwTriangle.Sites[0].Point,edge.CcwTriangle.Sites[1].Point,edge.CcwTriangle.Sites[2].Point))); // LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls); // return ls; // } // // static Ellipse Circumcircle(Point a, Point b, Point c) { // var mab = 0.5 * (a + b); // var mbc = 0.5 * (c + b); // Point center; // Point.LineLineIntersection(mab, mab + (b - a).Rotate(Math.PI / 2), mbc, mbc + (b - c).Rotate(Math.PI / 2), out center); // var r = (center - a).Length; // return new Ellipse(r,r, center); // } // #endif void LegalizeEdgeForOtherCwTriangle(CdtSite pi, CdtEdge edge) { var i=edge.CwTriangle.Edges.Index(edge); // if (i == -1) // { // List ls = new List(); // ls.Add(new DebugCurve(new Ellipse(2, 2, pi.Point))); // for (int j = 0; j < 3; j++) // { // var ee = edge.CwTriangle.Edges[j]; // ls.Add(new DebugCurve(100,1, j == i ? "red" : "blue", new LineSegment(ee.upperSite.Point, ee.lowerSite.Point))); // } // ls.Add(new DebugCurve("purple", new LineSegment(edge.upperSite.Point, edge.lowerSite.Point))); // // LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls); // } Debug.Assert(i>=0); if (IsIllegal(pi, edge.upperSite, edge.CwTriangle.Sites[i + 2], edge.lowerSite)) { //ShowIllegalEdge(edge, i, pi); CdtEdge e = Flip(pi, edge); LegalizeEdge(pi, e.CwTriangle.OppositeEdge(pi) ); LegalizeEdge(pi, e.CcwTriangle.OppositeEdge(pi)); } } // #if DEBUG && TEST_MSAGL // void ShowIllegalEdge(CdtEdge edge, int i, CdtSite pi) { // List ls=new List(); // ls.Add(new DebugCurve(new Ellipse(2, 2, pi.Point))); // for(int j=0;j<3;j++) { // var ee=edge.CwTriangle.Edges[j]; // ls.Add(new DebugCurve(j==i?"red":"blue", new LineSegment(ee.upperSite.Point,ee.lowerSite.Point))); // } // LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls); // } // #endif static bool IsIllegal(CdtSite pi, CdtSite a, CdtSite b, CdtSite c) { return InCone(pi, a, b, c) && InCircle(pi, a, b, c); } // /// // /// Testing that s in inside of the circumcircle of (p,q,r). // /// The good explanation of this test is in // /// "Guibas, Stolfi,"Primitives for the Manipulation of General Subdivisions and the Computation of Voronoi Diagrams // /// // /// // /// // /// // /// // /// // /// // /// // public static bool InCircle0(CdtSite s, CdtSite p, CdtSite q, CdtSite r, double ccc) { // Debug.Assert(Point.GetTriangleOrientationWithNoEpsilon(p.Point, q.Point, r.Point) == TriangleOrientation.Counterclockwise); // /* // * det(p.x,p.y, p*p,1 // * q.x,q.y, q*q,1 // * r.x,r.y, r*r,1 // * s.x,s.y, s*s,1) > 0 // */ // // var a = p.Point.X; // var b = p.Point.Y; // var c = a*a + b*b; // var d = q.Point.X; // var e = q.Point.Y; // var f = d*d + e*e; // var g = r.Point.X; // var h = r.Point.Y; // var k = g*g + h*h; // var l = s.Point.X; // var m = s.Point.Y; // var n = l*l + m*m; // // // // double ddd = ccc - ((a - d) * (h * n - k * m) + (e - b) * (g * n - k * l) + (c - f) * (g * m - h * l) + // (g - l) * (b * f - c * e) + (m - h) * (a * f - c * d) + (k - n) * (a * e - b * d)); // if (Math.Abs(ddd) > diff) { // diff = ddd; // Console.WriteLine(diff); // } // return (a - d)*(h*n - k*m) + (e - b)*(g*n - k*l) + (c - f)*(g*m - h*l) + // (g - l)*(b*f - c*e) + (m - h)*(a*f - c*d) + (k - n)*(a*e - b*d) > ApproximateComparer.Tolerance; // } /// /// Testing that d in inside of the circumcircle of (a,b,c). /// The good explanation of this test is in /// "Guibas, Stolfi,"Primitives for the Manipulation of General Subdivisions and the Computation of Voronoi Diagrams /// /// /// /// /// /// /// public static bool InCircle(CdtSite d, CdtSite a, CdtSite b, CdtSite c) { Debug.Assert(Point.GetTriangleOrientationWithNoEpsilon(a.Point, b.Point, c.Point) == TriangleOrientation.Counterclockwise); /* * | ax-dx ay-dy (ax-dx)^2+(ay-dy)^2| * | bx-dx by-dy (bx-dx)^2+(by-dy)^2| * | cx-dx cy-dy (cx-dx)^2+(cy-dy)^2| */ var axdx = a.Point.X - d.Point.X; var aydy = a.Point.Y - d.Point.Y; var bxdx = b.Point.X - d.Point.X; var bydy = b.Point.Y - d.Point.Y; var cxdx = c.Point.X - d.Point.X; var cydy = c.Point.Y - d.Point.Y; var t0 = axdx * axdx + aydy * aydy; var t1 = bxdx * bxdx + bydy * bydy; var t2 = cxdx * cxdx + cydy * cydy; return axdx * (bydy * t2 - cydy * t1) - bxdx * (aydy * t2 - cydy * t0) + cxdx * (aydy * t1 - bydy * t0) > ApproximateComparer.Tolerance; } /// /// /// /// /// point on left side of the cone /// the apex /// point on the right side of the cone static bool InCone(CdtSite pi, CdtSite a, CdtSite b, CdtSite c) { Debug.Assert(Point.GetTriangleOrientationWithNoEpsilon(a.Point,b.Point,c.Point)==TriangleOrientation.Counterclockwise); return Point.GetTriangleOrientationWithNoEpsilon(a.Point, pi.Point, b.Point) == TriangleOrientation.Clockwise && Point.GetTriangleOrientationWithNoEpsilon(b.Point, pi.Point, c.Point) == TriangleOrientation.Clockwise; } static CdtEdge Flip(CdtSite pi, CdtEdge edge) { Debug.Assert(!edge.IsAdjacent(pi)); Debug.Assert(edge.CcwTriangle.Contains(pi) || edge.CwTriangle.Contains(pi)); //get surrounding data CdtTriangle t, ot; if (edge.CcwTriangle.Contains(pi)) { t = edge.CcwTriangle; ot = edge.CwTriangle; } else { t = edge.CwTriangle; ot = edge.CcwTriangle; } Debug.Assert(t.Contains(pi)); var eIndex = t.Edges.Index(edge); var eOtherIndex = ot.Edges.Index(edge); Debug.Assert(eIndex > -1 && eOtherIndex > -1); var pl = ot.Sites[eOtherIndex + 2]; var edgeBeforPi = t.Edges[eIndex + 1]; var edgeBeforPl = ot.Edges[eOtherIndex + 1]; //changing t var newEdge = Cdt.GetOrCreateEdge(pi, pl); t.Sites[eIndex + 1] = pl; t.Edges[eIndex] = edgeBeforPl; t.Edges[eIndex + 1] = newEdge; //changing ot ot.Sites[eOtherIndex + 1] = pi; ot.Edges[eOtherIndex] = edgeBeforPi; ot.Edges[eOtherIndex + 1] = newEdge; //orient the new edge and the two edges that move from one triangle to another if (edgeBeforPl.lowerSite == pl) edgeBeforPl.CcwTriangle = t; else edgeBeforPl.CwTriangle = t; if (edgeBeforPi.lowerSite == pi) edgeBeforPi.CcwTriangle = ot; else edgeBeforPi.CwTriangle = ot; if (newEdge.upperSite == pi) { newEdge.CcwTriangle = ot; newEdge.CwTriangle = t; } else { newEdge.CcwTriangle = t; newEdge.CwTriangle = ot; } Debug.Assert(CheckTriangle(t)); Debug.Assert(CheckTriangle(t)); //ShowFlip(pi, t, ot); edge.upperSite.Edges.Remove(edge); //forget the edge return newEdge; } // #if DEBUG && TEST_MSAGL // static void ShowFlip(CdtSite pi, CdtTriangle t, CdtTriangle ot) { // List ls=new List(); // ls.Add(new DebugCurve(new Ellipse(2,2, pi.Point))); // for(int i=0;i<3;i++) { // var e=t.Edges[i]; // ls.Add(new DebugCurve(100, 1, "red", new LineSegment(e.upperSite.Point,e.lowerSite.Point))); // } // for (int i = 0; i < 3; i++) // { // var e = ot.Edges[i]; // ls.Add(new DebugCurve(100, 1, "blue", new LineSegment(e.upperSite.Point, e.lowerSite.Point))); // } // ls.Add(new DebugCurve(Circumcircle(t.Sites[0].Point, t.Sites[1].Point, t.Sites[2].Point))); // LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls); // } // #endif static bool CheckTriangle(CdtTriangle t) { if (Point.GetTriangleOrientationWithNoEpsilon(t.Sites[0].Point, t.Sites[1].Point, t.Sites[2].Point) != TriangleOrientation.Counterclockwise) { return false; } for (int i = 0; i < 3; i++) { var e = t.Edges[i]; var a = t.Sites[i]; var b = t.Sites[i+1]; if (!e.IsAdjacent(a) || !e.IsAdjacent(b)) return false; if (e.upperSite == a) { if (e.CcwTriangle != t) return false; } else if (e.CwTriangle != t) return false; } return true; } void ProjectToFront(CdtSite site, out RBNode frontElement) { frontElement = front.FindLast(s => s.X <= site.Point.X); } } }