public static bool InScanArea( TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd) { double pdx = pd.X; double pdy = pd.Y; double adx = pa.X - pdx; double ady = pa.Y - pdy; double bdx = pb.X - pdx; double bdy = pb.Y - pdy; double adxbdy = adx * bdy; double bdxady = bdx * ady; double oabd = adxbdy - bdxady; // oabd = orient2d(pa,pb,pd); if (oabd <= 0) { return false; } double cdx = pc.X - pdx; double cdy = pc.Y - pdy; double cdxady = cdx * ady; double adxcdy = adx * cdy; double ocad = cdxady - adxcdy; // ocad = orient2d(pc,pa,pd); if (ocad <= 0) { return false; } return true; }
public PointOnEdgeException(string message, TriangulationPoint a, TriangulationPoint b, TriangulationPoint c) : base(message) { this.A = a; this.B = b; this.C = c; }
/// <summary> /// Requirements: /// 1. a,b and c form a triangle. /// 2. a and d is know to be on opposite side of bc /// <code> /// a /// + /// / \ /// / \ /// b/ \c /// +-------+ /// / B \ /// / \ /// </code> /// Facts: /// d has to be in area B to have a chance to be inside the circle formed by a,b and c /// d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW /// This preknowledge gives us a way to optimize the incircle test /// </summary> /// <param name="pa">triangle point, opposite d</param> /// <param name="pb">triangle point</param> /// <param name="pc">triangle point</param> /// <param name="pd">point opposite a</param> /// <returns>true if d is inside circle, false if on circle edge</returns> public static bool SmartIncircle( TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd ) { double pdx = pd.X; double pdy = pd.Y; double adx = pa.X - pdx; double ady = pa.Y - pdy; double bdx = pb.X - pdx; double bdy = pb.Y - pdy; double adxbdy = adx * bdy; double bdxady = bdx * ady; double oabd = adxbdy - bdxady; // oabd = orient2d(pa,pb,pd); if (oabd <= 0) return false; double cdx = pc.X - pdx; double cdy = pc.Y - pdy; double cdxady = cdx * ady; double adxcdy = adx * cdy; double ocad = cdxady - adxcdy; // ocad = orient2d(pc,pa,pd); if (ocad <= 0) return false; double bdxcdy = bdx * cdy; double cdxbdy = cdx * bdy; double alift = adx * adx + ady * ady; double blift = bdx * bdx + bdy * bdy; double clift = cdx * cdx + cdy * cdy; double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; return det > 0; }
/// <summary> /// Give two points in any order. Will always be ordered so /// that q.y > p.y and q.x > p.x if same y value /// </summary> public DTSweepConstraint(TriangulationPoint p1, TriangulationPoint p2) { this.P = p1; this.Q = p2; if (p1.Y > p2.Y) { this.Q = p1; this.P = p2; } else if (p1.Y == p2.Y) { if (p1.X > p2.X) { this.Q = p1; this.P = p2; } else if (p1.X == p2.X) { // logger.info( "Failed to create constraint {}={}", p1, p2 ); // throw new DuplicatePointException( p1 + "=" + p2 ); // return; } } this.Q.AddEdge(this); }
/// <summary> /// Give two points in any order. Will always be ordered so /// that q.y > p.y and q.x > p.x if same y value /// </summary> public DTSweepConstraint(TriangulationPoint p1, TriangulationPoint p2) { P = p1; Q = p2; if (p1.y > p2.y) { Q = p1; P = p2; } else if (p1.y == p2.y) { if (p1.x > p2.x) { Q = p1; P = p2; } else if (p1.x == p2.x) { // logger.info( "Failed to create constraint {}={}", p1, p2 ); // throw new DuplicatePointException( p1 + "=" + p2 ); // return; } } Q.AddEdge(this); }
public PointOnEdgeException( string message, TriangulationPoint a, TriangulationPoint b, TriangulationPoint c ) : base(message) { A=a; B=b; C=c; }
/// Forumla to calculate signed area /// Positive if CCW /// Negative if CW /// 0 if collinear /// A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1) /// = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3) public static Orientation Orient2d( TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc ) { double detleft = (pa.X - pc.X) * (pb.Y - pc.Y); double detright = (pa.Y - pc.Y) * (pb.X - pc.X); double val = detleft - detright; if (val > -EPSILON && val < EPSILON) { return Orientation.Collinear; } else if (val > 0) { return Orientation.CCW; } return Orientation.CW; }
//TODO: Port note - different implementation public int IndexCW(TriangulationPoint p) { int index = IndexOf(p); switch (index) { case 0: return 2; case 1: return 0; default: return 1; } }
/* public static bool InScanArea(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd) { double pdx = pd.X; double pdy = pd.Y; double adx = pa.X - pdx; double ady = pa.Y - pdy; double bdx = pb.X - pdx; double bdy = pb.Y - pdy; double adxbdy = adx*bdy; double bdxady = bdx*ady; double oabd = adxbdy - bdxady; // oabd = orient2d(pa,pb,pd); if (oabd <= 0) { return false; } double cdx = pc.X - pdx; double cdy = pc.Y - pdy; double cdxady = cdx*ady; double adxcdy = adx*cdy; double ocad = cdxady - adxcdy; // ocad = orient2d(pc,pa,pd); if (ocad <= 0) { return false; } return true; } */ public static bool InScanArea(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd) { double oadb = (pa.X - pb.X) * (pd.Y - pb.Y) - (pd.X - pb.X) * (pa.Y - pb.Y); if (oadb >= -EPSILON) { return false; } double oadc = (pa.X - pc.X) * (pd.Y - pc.Y) - (pd.X - pc.X) * (pa.Y - pc.Y); if (oadc <= EPSILON) { return false; } return true; }
/// <summary> /// We use a balancing tree to locate a node smaller or equal to given key value (in theory) /// </summary> public AdvancingFrontNode LocateNode(TriangulationPoint point) { return LocateNode(point.X); }
/// <summary> /// After a flip we have two triangles and know that only one will still be /// intersecting the edge. So decide which to contiune with and legalize the other /// </summary> /// <param name="tcx"></param> /// <param name="o">should be the result of an TriangulationUtil.orient2d( eq, op, ep )</param> /// <param name="t">triangle 1</param> /// <param name="ot">triangle 2</param> /// <param name="p">a point shared by both triangles</param> /// <param name="op">another point shared by both triangles</param> /// <returns>returns the triangle still intersecting the edge</returns> static DelaunayTriangle NextFlipTriangle(DTSweepContext tcx, Orientation o, DelaunayTriangle t, DelaunayTriangle ot, TriangulationPoint p, TriangulationPoint op) { int edgeIndex; if (o == Orientation.CCW) { // ot is not crossing edge after flip edgeIndex = ot.EdgeIndex(p, op); ot.EdgeIsDelaunay[edgeIndex] = true; Legalize(tcx, ot); ot.EdgeIsDelaunay.Clear(); return(t); } // t is not crossing edge after flip edgeIndex = t.EdgeIndex(p, op); t.EdgeIsDelaunay[edgeIndex] = true; Legalize(tcx, t); t.EdgeIsDelaunay.Clear(); return(ot); }
static void FlipEdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle t, TriangulationPoint p) { DelaunayTriangle ot = t.NeighborAcross(p); TriangulationPoint op = ot.OppositePoint(t, p); if (ot == null) { // If we want to integrate the fillEdgeEvent do it here // With current implementation we should never get here throw new InvalidOperationException("[BUG:FIXME] FLIP failed due to missing triangle"); } if (t.GetConstrainedEdgeAcross(p)) { throw new Exception("Intersecting Constraints"); } bool inScanArea = TriangulationUtil.InScanArea(p, t.PointCCW(p), t.PointCW(p), op); if (inScanArea) { // Lets rotate shared edge one vertex CW RotateTrianglePair(t, p, ot, op); tcx.MapTriangleToNodes(t); tcx.MapTriangleToNodes(ot); if (p == eq && op == ep) { if (eq == tcx.EdgeEvent.ConstrainedEdge.Q && ep == tcx.EdgeEvent.ConstrainedEdge.P) { //if (tcx.IsDebugEnabled) Console.WriteLine("[FLIP] - constrained edge done"); // TODO: remove t.MarkConstrainedEdge(ep, eq); ot.MarkConstrainedEdge(ep, eq); Legalize(tcx, t); Legalize(tcx, ot); } else { //if (tcx.IsDebugEnabled) Console.WriteLine("[FLIP] - subedge done"); // TODO: remove // XXX: I think one of the triangles should be legalized here? } } else { //if (tcx.IsDebugEnabled) // Console.WriteLine("[FLIP] - flipping and continuing with triangle still crossing edge"); // TODO: remove Orientation o = TriangulationUtil.Orient2d(eq, op, ep); t = NextFlipTriangle(tcx, o, t, ot, p, op); FlipEdgeEvent(tcx, ep, eq, t, p); } } else { TriangulationPoint newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, t, ot, newP); EdgeEvent(tcx, ep, eq, t, p); } }
/// Forumla to calculate signed area /// Positive if CCW /// Negative if CW /// 0 if collinear /// A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1) /// = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3) public static Orientation Orient2d(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc) { double detleft = (pa.X - pc.X) * (pb.Y - pc.Y); double detright = (pa.Y - pc.Y) * (pb.X - pc.X); double val = detleft - detright; if (val > -EPSILON && val < EPSILON) { return(Orientation.Collinear); } else if (val > 0) { return(Orientation.CCW); } return(Orientation.CW); }
/// <summary> /// Creates a new front triangle and legalize it /// </summary> private static AdvancingFrontNode NewFrontTriangle(DTSweepContext tcx, TriangulationPoint point, AdvancingFrontNode node) { DelaunayTriangle triangle = new DelaunayTriangle(point, node.Point, node.Next.Point); triangle.MarkNeighbor(node.Triangle); tcx.Triangles.Add(triangle); AdvancingFrontNode newNode = new AdvancingFrontNode(point); newNode.Next = node.Next; newNode.Prev = node; node.Next.Prev = newNode; node.Next = newNode; tcx.AddNode(newNode); // XXX: BST if (!Legalize(tcx, triangle)) { tcx.MapTriangleToNodes(triangle); } return newNode; }
static void EdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle triangle, TriangulationPoint point) { if (IsEdgeSideOfTriangle(triangle, ep, eq)) { return; } TriangulationPoint p1 = triangle.PointCCW(point); Orientation o1 = TriangulationUtil.Orient2d(eq, p1, ep); if (o1 == Orientation.Collinear) { if (triangle.Contains(eq, p1)) { triangle.MarkConstrainedEdge(eq, p1); // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.EdgeEvent.ConstrainedEdge.Q = p1; triangle = triangle.NeighborAcross(point); EdgeEvent(tcx, ep, p1, triangle, p1); } else { throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet"); } if (tcx.IsDebugEnabled) { Debug.WriteLine("EdgeEvent - Point on constrained edge"); } return; } TriangulationPoint p2 = triangle.PointCW(point); Orientation o2 = TriangulationUtil.Orient2d(eq, p2, ep); if (o2 == Orientation.Collinear) { if (triangle.Contains(eq, p2)) { triangle.MarkConstrainedEdge(eq, p2); // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.EdgeEvent.ConstrainedEdge.Q = p2; triangle = triangle.NeighborAcross(point); EdgeEvent(tcx, ep, p2, triangle, p2); } else { throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet"); } if (tcx.IsDebugEnabled) { Debug.WriteLine("EdgeEvent - Point on constrained edge"); } return; } if (o1 == o2) { // Need to decide if we are rotating CW or CCW to get to a triangle // that will cross edge if (o1 == Orientation.CW) { triangle = triangle.NeighborCCW(point); } else { triangle = triangle.NeighborCW(point); } EdgeEvent(tcx, ep, eq, triangle, point); } else { // This triangle crosses constraint so lets flippin start! FlipEdgeEvent(tcx, ep, eq, triangle, point); } }
public bool Contains(TriangulationPoint p, TriangulationPoint q) { return(Contains(p) && Contains(q)); }
private static void FlipEdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle t, TriangulationPoint p) { DelaunayTriangle ot = t.NeighborAcross(p); TriangulationPoint op = ot.OppositePoint(t, p); if (ot == null) { // If we want to integrate the fillEdgeEvent do it here // With current implementation we should never get here throw new InvalidOperationException("[BUG:FIXME] FLIP failed due to missing triangle"); } if (t.GetConstrainedEdgeAcross(p)) { throw new Exception("Intersecting Constraints"); } bool inScanArea = TriangulationUtil.InScanArea(p, t.PointCCW(p), t.PointCW(p), op); if (inScanArea) { // Lets rotate shared edge one vertex CW RotateTrianglePair(t, p, ot, op); tcx.MapTriangleToNodes(t); tcx.MapTriangleToNodes(ot); if (p == eq && op == ep) { if (eq == tcx.EdgeEvent.ConstrainedEdge.Q && ep == tcx.EdgeEvent.ConstrainedEdge.P) { t.MarkConstrainedEdge(ep, eq); ot.MarkConstrainedEdge(ep, eq); Legalize(tcx, t); Legalize(tcx, ot); } else { // XXX: I think one of the triangles should be legalized here? } } else { // TODO: remove Orientation o = TriangulationUtil.Orient2d(eq, op, ep); t = NextFlipTriangle(tcx, o, t, ot, p, op); FlipEdgeEvent(tcx, ep, eq, t, p); } } else { TriangulationPoint newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, t, ot, newP); EdgeEvent(tcx, ep, eq, t, p); } }
/// <summary> /// Give two points in any order. Will always be ordered so /// that q.y > p.y and q.x > p.x if same y value /// </summary> public DTSweepConstraint(TriangulationPoint p1, TriangulationPoint p2) : base(p1, p2) { Q.AddEdge(this); }
public bool Contains(TriangulationPoint p) { return(p == Points[0] || p == Points[1] || p == Points[2]); }
/// <summary> /// We use a balancing tree to locate a node smaller or equal to given key value /// </summary> public AdvancingFrontNode LocateNode(TriangulationPoint point) { return(LocateNode(point.X)); }
public AdvancingFrontNode(TriangulationPoint point) { Point = point; Value = point.X; }
/// <summary> /// Requirements: /// 1. a,b and c form a triangle. /// 2. a and d is know to be on opposite side of bc /// <code> /// a /// + /// / \ /// / \ /// b/ \c /// +-------+ /// / B \ /// / \ /// </code> /// Facts: /// d has to be in area B to have a chance to be inside the circle formed by a,b and c /// d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW /// This preknowledge gives us a way to optimize the incircle test /// </summary> /// <param name="pa">triangle point, opposite d</param> /// <param name="pb">triangle point</param> /// <param name="pc">triangle point</param> /// <param name="pd">point opposite a</param> /// <returns>true if d is inside circle, false if on circle edge</returns> public static bool SmartIncircle(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd) { double pdx = pd.X; double pdy = pd.Y; double adx = pa.X - pdx; double ady = pa.Y - pdy; double bdx = pb.X - pdx; double bdy = pb.Y - pdy; double adxbdy = adx * bdy; double bdxady = bdx * ady; double oabd = adxbdy - bdxady; // oabd = orient2d(pa,pb,pd); if (oabd <= 0) { return(false); } double cdx = pc.X - pdx; double cdy = pc.Y - pdy; double cdxady = cdx * ady; double adxcdy = adx * cdy; double ocad = cdxady - adxcdy; // ocad = orient2d(pc,pa,pd); if (ocad <= 0) { return(false); } double bdxcdy = bdx * cdy; double cdxbdy = cdx * bdy; double alift = adx * adx + ady * ady; double blift = bdx * bdx + bdy * bdy; double clift = cdx * cdx + cdy * cdy; double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; return(det > 0); }
/// <summary> /// Rotates a triangle pair one vertex CW /// n2 n2 /// P +-----+ P +-----+ /// | t /| |\ t | /// | / | | \ | /// n1| / |n3 n1| \ |n3 /// | / | after CW | \ | /// |/ oT | | oT \| /// +-----+ oP +-----+ /// n4 n4 /// </summary> private static void RotateTrianglePair(DelaunayTriangle t, TriangulationPoint p, DelaunayTriangle ot, TriangulationPoint op) { DelaunayTriangle n1 = t.NeighborCCW(p); DelaunayTriangle n2 = t.NeighborCW(p); DelaunayTriangle n3 = ot.NeighborCCW(op); DelaunayTriangle n4 = ot.NeighborCW(op); bool ce1 = t.GetConstrainedEdgeCCW(p); bool ce2 = t.GetConstrainedEdgeCW(p); bool ce3 = ot.GetConstrainedEdgeCCW(op); bool ce4 = ot.GetConstrainedEdgeCW(op); bool de1 = t.GetDelaunayEdgeCCW(p); bool de2 = t.GetDelaunayEdgeCW(p); bool de3 = ot.GetDelaunayEdgeCCW(op); bool de4 = ot.GetDelaunayEdgeCW(op); t.Legalize(p, op); ot.Legalize(op, p); // Remap dEdge ot.SetDelaunayEdgeCCW(p, de1); t.SetDelaunayEdgeCW(p, de2); t.SetDelaunayEdgeCCW(op, de3); ot.SetDelaunayEdgeCW(op, de4); // Remap cEdge ot.SetConstrainedEdgeCCW(p, ce1); t.SetConstrainedEdgeCW(p, ce2); t.SetConstrainedEdgeCCW(op, ce3); ot.SetConstrainedEdgeCW(op, ce4); // Remap neighbors // XXX: might optimize the markNeighbor by keeping track of // what side should be assigned to what neighbor after the // rotation. Now mark neighbor does lots of testing to find // the right side. t.Neighbors.Clear(); ot.Neighbors.Clear(); if (n1 != null) ot.MarkNeighbor(n1); if (n2 != null) t.MarkNeighbor(n2); if (n3 != null) t.MarkNeighbor(n3); if (n4 != null) ot.MarkNeighbor(n4); t.MarkNeighbor(ot); }
static bool AngleExceedsPlus90DegreesOrIsNegative(TriangulationPoint origin, TriangulationPoint pa, TriangulationPoint pb) { double angle = Angle(origin, pa, pb); bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); return(exceedsPlus90DegreesOrIsNegative); }
private static bool AngleExceedsPlus90DegreesOrIsNegative(TriangulationPoint origin, TriangulationPoint pa, TriangulationPoint pb) { double angle = Angle(origin, pa, pb); bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); return exceedsPlus90DegreesOrIsNegative; }
public int IndexOf( TriangulationPoint p ) { int i = points.IndexOf( p ); if( i == -1 ) throw new Exception( "Calling index with a point that doesn't exist in triangle" ); return i; }
private static bool IsEdgeSideOfTriangle(DelaunayTriangle triangle, TriangulationPoint ep, TriangulationPoint eq) { int index = triangle.EdgeIndex(ep, eq); if (index != -1) { triangle.MarkConstrainedEdge(index); triangle = triangle.Neighbors[index]; if (triangle != null) { triangle.MarkConstrainedEdge(ep, eq); } return true; } return false; }
static bool AngleExceeds90Degrees(TriangulationPoint origin, TriangulationPoint pa, TriangulationPoint pb) { double angle = Angle(origin, pa, pb); bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); return(exceeds90Degrees); }
/// <summary> /// After a flip we have two triangles and know that only one will still be /// intersecting the edge. So decide which to contiune with and legalize the other /// </summary> /// <param name="tcx"></param> /// <param name="o">should be the result of an TriangulationUtil.orient2d( eq, op, ep )</param> /// <param name="t">triangle 1</param> /// <param name="ot">triangle 2</param> /// <param name="p">a point shared by both triangles</param> /// <param name="op">another point shared by both triangles</param> /// <returns>returns the triangle still intersecting the edge</returns> private static DelaunayTriangle NextFlipTriangle(DTSweepContext tcx, Orientation o, DelaunayTriangle t, DelaunayTriangle ot, TriangulationPoint p, TriangulationPoint op) { int edgeIndex; if (o == Orientation.CCW) { // ot is not crossing edge after flip edgeIndex = ot.EdgeIndex(p, op); ot.EdgeIsDelaunay[edgeIndex] = true; Legalize(tcx, ot); ot.EdgeIsDelaunay.Clear(); return t; } // t is not crossing edge after flip edgeIndex = t.EdgeIndex(p, op); t.EdgeIsDelaunay[edgeIndex] = true; Legalize(tcx, t); t.EdgeIsDelaunay.Clear(); return ot; }
/// <summary> /// Scan part of the FlipScan algorithm /// When a triangle pair isn't flippable we will scan for the next /// point that is inside the flip triangle scan area. When found /// we generate a new flipEdgeEvent /// </summary> /// <param name="tcx"></param> /// <param name="ep">last point on the edge we are traversing</param> /// <param name="eq">first point on the edge we are traversing</param> /// <param name="flipTriangle">the current triangle sharing the point eq with edge</param> /// <param name="t"></param> /// <param name="p"></param> static void FlipScanEdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle flipTriangle, DelaunayTriangle t, TriangulationPoint p) { DelaunayTriangle ot = t.NeighborAcross(p); TriangulationPoint op = ot.OppositePoint(t, p); if (ot == null) { // If we want to integrate the fillEdgeEvent do it here // With current implementation we should never get here throw new Exception("[BUG:FIXME] FLIP failed due to missing triangle"); } bool inScanArea = TriangulationUtil.InScanArea(eq, flipTriangle.PointCCW(eq), flipTriangle.PointCW(eq), op); if (inScanArea) { // flip with new edge op->eq FlipEdgeEvent(tcx, eq, op, ot, op); // TODO: Actually I just figured out that it should be possible to // improve this by getting the next ot and op before the the above // flip and continue the flipScanEdgeEvent here // set new ot and op here and loop back to inScanArea test // also need to set a new flipTriangle first // Turns out at first glance that this is somewhat complicated // so it will have to wait. } else { TriangulationPoint newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, flipTriangle, ot, newP); } }
/// <summary> /// When we need to traverse from one triangle to the next we need /// the point in current triangle that is the opposite point to the next /// triangle. /// </summary> static TriangulationPoint NextFlipPoint(TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle ot, TriangulationPoint op) { Orientation o2d = TriangulationUtil.Orient2d(eq, op, ep); if (o2d == Orientation.CW) { // Right return(ot.PointCCW(op)); } else if (o2d == Orientation.CCW) { // Left return(ot.PointCW(op)); } else { // TODO: implement support for point on constraint edge throw new PointOnEdgeException("Point on constrained edge not supported yet"); } }
public override TriangulationConstraint NewConstraint(TriangulationPoint a, TriangulationPoint b) { return new DTSweepConstraint(a, b); }
/// <param name="t">Opposite triangle</param> /// <param name="p">The point in t that isn't shared between the triangles</param> public TriangulationPoint OppositePoint(DelaunayTriangle t, TriangulationPoint p) { Debug.Assert(t != this, "self-pointer error"); return(PointCW(t.PointCW(p))); }
/* * public static bool InScanArea(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, * TriangulationPoint pd) * { * double pdx = pd.X; * double pdy = pd.Y; * double adx = pa.X - pdx; * double ady = pa.Y - pdy; * double bdx = pb.X - pdx; * double bdy = pb.Y - pdy; * * double adxbdy = adx*bdy; * double bdxady = bdx*ady; * double oabd = adxbdy - bdxady; * // oabd = orient2d(pa,pb,pd); * if (oabd <= 0) * { * return false; * } * * double cdx = pc.X - pdx; * double cdy = pc.Y - pdy; * * double cdxady = cdx*ady; * double adxcdy = adx*cdy; * double ocad = cdxady - adxcdy; * // ocad = orient2d(pc,pa,pd); * if (ocad <= 0) * { * return false; * } * return true; * } */ public static bool InScanArea(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd) { double oadb = (pa.X - pb.X) * (pd.Y - pb.Y) - (pd.X - pb.X) * (pa.Y - pb.Y); if (oadb >= -EPSILON) { return(false); } double oadc = (pa.X - pc.X) * (pd.Y - pc.Y) - (pd.X - pc.X) * (pa.Y - pc.Y); if (oadc <= EPSILON) { return(false); } return(true); }
public DelaunayTriangle( TriangulationPoint p1, TriangulationPoint p2, TriangulationPoint p3 ) { points[0] = p1; points[1] = p2; points[2] = p3; }
public DelaunayTriangle NeighborCCW(TriangulationPoint point) { return(Neighbors[(Points.IndexOf(point) + 2) % 3]); }
public DelaunayTriangle NeighborAcross(TriangulationPoint point) { return(Neighbors[Points.IndexOf(point)]); }
public TriangulationPoint PointCW(TriangulationPoint point) { return(Points[(IndexOf(point) + 2) % 3]); }
public void AddSteinerPoint(TriangulationPoint point) { if (_steinerPoints == null) { _steinerPoints = new List<TriangulationPoint>(); } _steinerPoints.Add(point); }
/// <summary> /// Legalize triangle by rotating clockwise around oPoint /// </summary> /// <param name="oPoint">The origin point to rotate around</param> /// <param name="nPoint">???</param> public void Legalize(TriangulationPoint oPoint, TriangulationPoint nPoint) { RotateCW(); Points[IndexCCW(oPoint)] = nPoint; }
private static Vector3 ConvertToVertexPosition(TriangulationPoint point, Vector3 normal, float normalComponent) { if (normal.X != 0 && normal.Y == 0 && normal.Z == 0) { return new Vector3(normalComponent, point.Yf, point.Xf); } if (normal.X == 0 && normal.Y != 0 && normal.Z == 0) { return new Vector3(point.Xf, normalComponent, point.Yf); } if (normal.X == 0 && normal.Y == 0 && normal.Z != 0) { return new Vector3(point.Xf, point.Yf, normalComponent); } return new Vector3(0, 0, 0); }
public bool GetConstrainedEdgeCW(TriangulationPoint p) { return(EdgeIsConstrained[(IndexOf(p) + 1) % 3]); }
public override void PrepareTriangulation(Triangulatable t) { base.PrepareTriangulation(t); double xmax, xmin; double ymax, ymin; xmax = xmin = Points[0].x; ymax = ymin = Points[0].y; // Calculate bounds. Should be combined with the sorting foreach (TriangulationPoint p in Points) { if (p.x > xmax) xmax = p.x; if (p.x < xmin) xmin = p.x; if (p.y > ymax) ymax = p.y; if (p.y < ymin) ymin = p.y; } double deltaX = ALPHA*(xmax - xmin); double deltaY = ALPHA*(ymax - ymin); TriangulationPoint p1 = new TriangulationPoint(xmax + deltaX, ymin - deltaY); TriangulationPoint p2 = new TriangulationPoint(xmin - deltaX, ymin - deltaY); Head = p1; Tail = p2; // long time = System.nanoTime(); // Sort the points along y-axis Points.Sort(_comparator); // logger.info( "Triangulation setup [{}ms]", ( System.nanoTime() - time ) / 1e6 ); }
public bool GetConstrainedEdgeAcross(TriangulationPoint p) { return(EdgeIsConstrained[IndexOf(p)]); }
/// <summary> /// This implementation will use simple node traversal algorithm to find a point on the front /// </summary> public AdvancingFrontNode LocatePoint(TriangulationPoint point) { double px = point.X; AdvancingFrontNode node = this.FindSearchNode(px); double nx = node.Point.X; if (px == nx) { if (point != node.Point) { // We might have two nodes with same x value for a short time if (point == node.Prev.Point) { node = node.Prev; } else if (point == node.Next.Point) { node = node.Next; } else { throw new Exception("Failed to find Node for given afront point"); } } } else if (px < nx) { while ((node = node.Prev) != null) { if (point == node.Point) { break; } } } else { while ((node = node.Next) != null) { if (point == node.Point) { break; } } } this.Search = node; return node; }
public void SetConstrainedEdgeCW(TriangulationPoint p, bool ce) { EdgeIsConstrained[(IndexOf(p) + 1) % 3] = ce; }
/// <summary> /// Find closes node to the left of the new point and /// create a new triangle. If needed new holes and basins /// will be filled to. /// </summary> private static AdvancingFrontNode PointEvent(DTSweepContext tcx, TriangulationPoint point) { AdvancingFrontNode node = tcx.LocateNode(point); AdvancingFrontNode newNode = NewFrontTriangle(tcx, point, node); // Only need to check +epsilon since point never have smaller // x value than node due to how we fetch nodes from the front if (point.X <= node.Point.X + TriangulationUtil.EPSILON) { Fill(tcx, node); } tcx.AddNode(newNode); FillAdvancingFront(tcx, newNode); return newNode; }
public void SetConstrainedEdgeAcross(TriangulationPoint p, bool ce) { EdgeIsConstrained[IndexOf(p)] = ce; }
private static bool AngleExceeds90Degrees(TriangulationPoint origin, TriangulationPoint pa, TriangulationPoint pb) { double angle = Angle(origin, pa, pb); bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); return exceeds90Degrees; }
public bool GetDelaunayEdgeCW(TriangulationPoint p) { return(EdgeIsDelaunay[(IndexOf(p) + 1) % 3]); }
private static void EdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle triangle, TriangulationPoint point) { if (IsEdgeSideOfTriangle(triangle, ep, eq)) return; TriangulationPoint p1 = triangle.PointCCW(point); Orientation o1 = TriangulationUtil.Orient2d(eq, p1, ep); if (o1 == Orientation.Collinear) { if (triangle.Contains(eq, p1)) { triangle.MarkConstrainedEdge(eq, p1); // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.EdgeEvent.ConstrainedEdge.Q = p1; triangle = triangle.NeighborAcross(point); EdgeEvent(tcx, ep, p1, triangle, p1); } else { throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet"); } return; } TriangulationPoint p2 = triangle.PointCW(point); Orientation o2 = TriangulationUtil.Orient2d(eq, p2, ep); if (o2 == Orientation.Collinear) { if (triangle.Contains(eq, p2)) { triangle.MarkConstrainedEdge(eq, p2); // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.EdgeEvent.ConstrainedEdge.Q = p2; triangle = triangle.NeighborAcross(point); EdgeEvent(tcx, ep, p2, triangle, p2); } else { throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet"); } return; } if (o1 == o2) { // Need to decide if we are rotating CW or CCW to get to a triangle // that will cross edge if (o1 == Orientation.CW) { triangle = triangle.NeighborCCW(point); } else { triangle = triangle.NeighborCW(point); } EdgeEvent(tcx, ep, eq, triangle, point); } else { // This triangle crosses constraint so lets flippin start! FlipEdgeEvent(tcx, ep, eq, triangle, point); } }
public bool GetDelaunayEdgeAcross(TriangulationPoint p) { return(EdgeIsDelaunay[IndexOf(p)]); }
/// <summary> /// Scan part of the FlipScan algorithm<br> /// When a triangle pair isn't flippable we will scan for the next /// point that is inside the flip triangle scan area. When found /// we generate a new flipEdgeEvent /// </summary> /// <param name="tcx"></param> /// <param name="ep">last point on the edge we are traversing</param> /// <param name="eq">first point on the edge we are traversing</param> /// <param name="flipTriangle">the current triangle sharing the point eq with edge</param> /// <param name="t"></param> /// <param name="p"></param> private static void FlipScanEdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle flipTriangle, DelaunayTriangle t, TriangulationPoint p) { DelaunayTriangle ot = t.NeighborAcross(p); TriangulationPoint op = ot.OppositePoint(t, p); if (ot == null) { // If we want to integrate the fillEdgeEvent do it here // With current implementation we should never get here throw new Exception("[BUG:FIXME] FLIP failed due to missing triangle"); } bool inScanArea = TriangulationUtil.InScanArea(eq, flipTriangle.PointCCW(eq), flipTriangle.PointCW(eq), op); if (inScanArea) { // flip with new edge op->eq FlipEdgeEvent(tcx, eq, op, ot, op); // TODO: Actually I just figured out that it should be possible to // improve this by getting the next ot and op before the the above // flip and continue the flipScanEdgeEvent here // set new ot and op here and loop back to inScanArea test // also need to set a new flipTriangle first // Turns out at first glance that this is somewhat complicated // so it will have to wait. } else { TriangulationPoint newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, flipTriangle, ot, newP); } }
public void SetDelaunayEdgeCW(TriangulationPoint p, bool ce) { EdgeIsDelaunay[(IndexOf(p) + 1) % 3] = ce; }
private static double Angle(TriangulationPoint origin, TriangulationPoint pa, TriangulationPoint pb) { /* Complex plane * ab = cosA +i*sinA * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) * atan2(y,x) computes the principal value of the argument function * applied to the complex number x+iy * Where x = ax*bx + ay*by * y = ax*by - ay*bx */ double px = origin.X; double py = origin.Y; double ax = pa.X - px; double ay = pa.Y - py; double bx = pb.X - px; double by = pb.Y - py; double x = ax * by - ay * bx; double y = ax * bx + ay * by; double angle = Math.Atan2(x, y); return angle; }
public void SetDelaunayEdgeAcross(TriangulationPoint p, bool ce) { EdgeIsDelaunay[IndexOf(p)] = ce; }
/// <summary> /// When we need to traverse from one triangle to the next we need /// the point in current triangle that is the opposite point to the next /// triangle. /// </summary> private static TriangulationPoint NextFlipPoint(TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle ot, TriangulationPoint op) { Orientation o2d = TriangulationUtil.Orient2d(eq, op, ep); if (o2d == Orientation.CW) { // Right return ot.PointCCW(op); } else if (o2d == Orientation.CCW) { // Left return ot.PointCW(op); } else { // TODO: implement support for point on constraint edge throw new PointOnEdgeException("Point on constrained edge not supported yet"); } }
public DelaunayTriangle(TriangulationPoint p1, TriangulationPoint p2, TriangulationPoint p3) { Points[0] = p1; Points[1] = p2; Points[2] = p3; }
public AdvancingFrontNode(TriangulationPoint point) { this.Point = point; Value = point.X; }
static bool IsEdgeSideOfTriangle(DelaunayTriangle triangle, TriangulationPoint ep, TriangulationPoint eq) { int index = triangle.EdgeIndex(ep, eq); if (index != -1) { triangle.MarkConstrainedEdge(index); triangle = triangle.Neighbors[index]; if (triangle != null) { triangle.MarkConstrainedEdge(ep, eq); } return(true); } return(false); }