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);
}
}
}