2020-04-14 00:42:04 +01:00
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 {
/// <summary>
/// this class builds the triangulation by a sweep with a horizontal line
/// </summary>
public class CdtSweeper : AlgorithmBase {
RbTree < CdtFrontElement > front = new RbTree < CdtFrontElement > ( ( a , b ) = > a . X . CompareTo ( b . X ) ) ;
readonly Set < CdtTriangle > triangles = new Set < CdtTriangle > ( ) ;
List < CdtSite > listOfSites ;
CdtSite p_2 ;
readonly Func < CdtSite , CdtSite , CdtEdge > createEdgeDelegate ;
CdtSite p_1 ;
internal CdtSweeper ( List < CdtSite > listOfSites , CdtSite p_1 , CdtSite p_2 ,
Func < CdtSite , CdtSite , CdtEdge > 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 < CdtTriangle > Triangles {
get { return triangles ; }
}
/// <summary>
/// the method making the main work
/// </summary>
protected override void RunInternal ( ) {
if ( listOfSites . Count = = 0 ) return ;
for ( int i = 1 ; i < listOfSites . Count ; i + + )
ProcessSite ( listOfSites [ i ] ) ;
FinalizeTriangulation ( ) ;
#if DEBUG & & TEST_MSAGL
//TestTriangles();
//ShowFront(triangles,null,null,null);
#endif
}
void FinalizeTriangulation ( ) {
var list = CreateDoubleLinkedListOfPerimeter ( ) ;
MakePerimeterConvex ( list ) ;
RemoveP1AndP2Triangles ( ) ;
}
void MakePerimeterConvex ( PerimeterEdge firstPerimeterEdge ) {
firstPerimeterEdge = FindPivot ( firstPerimeterEdge ) ;
var firstSite = firstPerimeterEdge . Start ;
var a = firstPerimeterEdge ;
PerimeterEdge b ;
do {
b = a . Next ;
if ( Point . GetTriangleOrientationWithNoEpsilon ( a . Start . Point , a . End . Point , b . End . Point ) = = TriangleOrientation . Counterclockwise ) {
a = ShortcutTwoListElements ( a ) ;
while ( a . Start ! = firstSite ) {
var c = a . Prev ;
if ( Point . GetTriangleOrientationWithNoEpsilon ( c . Start . Point , c . End . Point , a . End . Point ) = = TriangleOrientation . Counterclockwise ) {
a = ShortcutTwoListElements ( c ) ;
} else break ;
}
} else
a = b ;
} while ( a . End ! = firstSite ) ;
}
static PerimeterEdge FindPivot ( PerimeterEdge firstPerimeterEdge ) {
var pivot = firstPerimeterEdge ;
var e = firstPerimeterEdge ;
do {
e = e . Next ;
if ( e . Start . Point . X < pivot . Start . Point . X | |
e . Start . Point . X = = pivot . Start . Point . X & & e . Start . Point . Y < pivot . Start . Point . Y )
pivot = e ;
} while ( e ! = firstPerimeterEdge ) ;
return pivot ;
}
PerimeterEdge CreateDoubleLinkedListOfPerimeter ( ) {
var firstEdge = front . First ( ) . Edge ;
var edge = firstEdge ;
PerimeterEdge pe , prevPe = null , listStart = null ;
do {
pe = CreatePerimeterElementFromEdge ( edge ) ;
edge = FindNextEdgeOnPerimeter ( edge ) ;
if ( prevPe ! = null ) {
pe . Prev = prevPe ;
prevPe . Next = pe ;
} else
listStart = pe ;
prevPe = pe ;
} while ( edge ! = firstEdge ) ;
listStart . Prev = pe ;
pe . Next = listStart ;
return listStart ;
}
static CdtEdge FindNextEdgeOnPerimeter ( CdtEdge e ) {
var t = e . CwTriangle ? ? e . CcwTriangle ;
e = t . Edges [ t . Edges . Index ( e ) + 2 ] ;
while ( e . CwTriangle ! = null & & e . CcwTriangle ! = null ) {
t = e . GetOtherTriangle ( t ) ;
e = t . Edges [ t . Edges . Index ( e ) + 2 ] ;
}
return e ;
}
static PerimeterEdge CreatePerimeterElementFromEdge ( CdtEdge edge ) {
var pe = new PerimeterEdge ( edge ) ;
if ( edge . CwTriangle ! = null ) {
pe . Start = edge . upperSite ;
pe . End = edge . lowerSite ;
} else {
pe . End = edge . upperSite ;
pe . Start = edge . lowerSite ;
}
return pe ;
}
void RemoveP1AndP2Triangles ( ) {
var trianglesToRemove = new Set < CdtTriangle > ( ) ;
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 < CdtTriangle > 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 < CdtTriangle > 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 ;
}
2020-06-04 01:36:55 +01:00
// #if DEBUG && TEST_MSAGL
// void ShowFrontWithSite(CdtSite site, params ICurve[] redCurves) {
// var ls = new List<DebugCurve>();
//
//
// 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<CdtTriangle> cdtTriangles, RbTree<CdtFrontElement> cdtFrontElements, IEnumerable<ICurve> redCurves, IEnumerable<ICurve> blueCurves) {
// List<DebugCurve> ls = new List<DebugCurve>();
// 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
2020-04-14 00:42:04 +01:00
// int count;
// bool db { get { return count == 147; } }
void PointEvent ( CdtSite pi ) {
RBNode < CdtFrontElement > 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 ) ;
}
2020-06-04 01:36:55 +01:00
// #if DEBUG && TEST_MSAGL
// void TestTriangles() {
// var usedSites = new Set<CdtSite>();
// foreach(var t in Triangles)
// usedSites.InsertRange(t.Sites);
// foreach (var triangle in Triangles) {
// TestTriangle(triangle, usedSites);
// }
// }
//
// void TestTriangle(CdtTriangle triangle, Set<CdtSite> 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<ICurve> redCurves=new List<ICurve>();
// redCurves.Add(new Ellipse(2, 2, site.Point));
// List<ICurve> blueCurves = new List<ICurve>();
// 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
2020-04-14 00:42:04 +01:00
CdtSite LeftCase ( CdtSite pi , RBNode < CdtFrontElement > 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 < CdtFrontElement > 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 < CdtFrontElement > 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 ;
}
/// <summary>
/// aNode is to the left of bNode, and they are consecutive
/// </summary>
/// <param name="aNode"></param>
/// <param name="bNode"></param>
RBNode < CdtFrontElement > ShortcutTwoFrontElements ( RBNode < CdtFrontElement > aNode , RBNode < CdtFrontElement > 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 < CdtFrontElement > leftLegNode ) {
if ( ! DropsSharpEnoughToTheLeft ( leftLegNode . Item ) )
return ;
//ShowFrontWithSite(leftLegNode.Item.LeftSite);
var stack = new Stack < CdtSite > ( ) ;
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 < CdtFrontElement > 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 < CdtFrontElement > 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 < CdtFrontElement > piNode ) {
if ( ! DropsSharpEnoughToTheRight ( piNode . Item ) )
return ;
// ShowFrontWithSite(piNode.Item.LeftSite);
var stack = new Stack < CdtSite > ( ) ;
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 < CdtFrontElement > FindNodeInFrontBySite ( RbTree < CdtFrontElement > 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 ) ) ;
}
}
2020-06-04 01:36:55 +01:00
// #if DEBUG && TEST_MSAGL
// List<DebugCurve> ShowIllegalEdge(CdtEdge edge, CdtSite pi, int i) {
// List<DebugCurve> ls = new List<DebugCurve>();
// 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
2020-04-14 00:42:04 +01:00
void LegalizeEdgeForOtherCwTriangle ( CdtSite pi , CdtEdge edge ) {
var i = edge . CwTriangle . Edges . Index ( edge ) ;
// if (i == -1)
// {
// List<DebugCurve> ls = new List<DebugCurve>();
// 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 ) ) ;
}
}
2020-06-04 01:36:55 +01:00
// #if DEBUG && TEST_MSAGL
// void ShowIllegalEdge(CdtEdge edge, int i, CdtSite pi) {
// List<DebugCurve> ls=new List<DebugCurve>();
// 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
2020-04-14 00:42:04 +01:00
static bool IsIllegal ( CdtSite pi , CdtSite a , CdtSite b , CdtSite c ) {
return InCone ( pi , a , b , c ) & & InCircle ( pi , a , b , c ) ;
}
// /// <summary>
// /// 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
// ///
// /// </summary>
// /// <param name="s"></param>
// /// <param name="p"></param>
// /// <param name="q"></param>
// /// <param name="r"></param>
// /// <param name="ccc"></param>
// /// <returns></returns>
// 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;
// }
/// <summary>
/// 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
///
/// </summary>
/// <param name="d"></param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <returns></returns>
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 ;
}
/// <summary>
///
/// </summary>
/// <param name="pi"></param>
/// <param name="a">point on left side of the cone</param>
/// <param name="b">the apex</param>
/// <param name="c">point on the right side of the cone</param>
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 ;
}
2020-06-04 01:36:55 +01:00
// #if DEBUG && TEST_MSAGL
// static void ShowFlip(CdtSite pi, CdtTriangle t, CdtTriangle ot) {
// List<DebugCurve> ls=new List<DebugCurve>();
// 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
2020-04-14 00:42:04 +01:00
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 < CdtFrontElement > frontElement ) {
frontElement = front . FindLast ( s = > s . X < = site . Point . X ) ;
}
}
}