private static void EdgeEvent(DTSweepContext tcx, PolygonPoint ep, PolygonPoint eq, DelaunayTriangle triangle, PolygonPoint point) { PolygonPoint p1, p2; if (IsEdgeSideOfTriangle(triangle, ep, eq)) { return; } p1 = triangle.PointCCWFrom(point); Orientation o1 = TriangulationUtil.Orient2D(eq, p1, ep); if (o1 == Orientation.Collinear) { // TODO: Split edge in two //// splitEdge( ep, eq, p1 ); // edgeEvent( tcx, p1, eq, triangle, point ); // edgeEvent( tcx, ep, p1, triangle, p1 ); // return; throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet", eq, p1, ep); } p2 = triangle.PointCWFrom(point); Orientation o2 = TriangulationUtil.Orient2D(eq, p2, ep); if (o2 == Orientation.Collinear) { // TODO: Split edge in two // edgeEvent( tcx, p2, eq, triangle, point ); // edgeEvent( tcx, ep, p2, triangle, p2 ); // return; throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet", eq, p2, ep); } 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.NeighborCCWFrom(point); } else { triangle = triangle.NeighborCWFrom(point); } EdgeEvent(tcx, ep, eq, triangle, point); } else { // This triangle crosses constraint so lets flippin start! FlipEdgeEvent(tcx, ep, eq, triangle, point); } }
private static void FlipEdgeEvent(DTSweepContext tcx, PolygonPoint ep, PolygonPoint eq, DelaunayTriangle t, PolygonPoint p) { DelaunayTriangle ot = t.NeighborAcrossFrom(p); PolygonPoint 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"); } bool inScanArea = TriangulationUtil.InScanArea(p, t.PointCCWFrom(p), t.PointCWFrom(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 { Orientation o = TriangulationUtil.Orient2D(eq, op, ep); t = NextFlipTriangle(tcx, o, t, ot, p, op); FlipEdgeEvent(tcx, ep, eq, t, p); } } else { PolygonPoint newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, t, ot, newP); EdgeEvent(tcx, ep, eq, t, p); } }
private static void FillLeftAboveEdgeEvent(DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node) { while (node.Prev.Point.X > edge.P.X) { // Check if next node is below the edge Orientation o1 = TriangulationUtil.Orient2D(edge.Q, node.Prev.Point, edge.P); if (o1 == Orientation.CW) { FillLeftBelowEdgeEvent(tcx, edge, node); } else { node = node.Prev; } } }
private static void FillLeftBelowEdgeEvent(DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node) { if (node.Point.X > edge.P.X) { if (TriangulationUtil.Orient2D(node.Point, node.Prev.Point, node.Prev.Prev.Point) == Orientation.CW) { // Concave FillLeftConcaveEdgeEvent(tcx, edge, node); } else { // Convex FillLeftConvexEdgeEvent(tcx, edge, node); // Retry this one FillLeftBelowEdgeEvent(tcx, edge, node); } } }
/// <summary> /// Recursive algorithm to fill a Basin with triangles /// </summary> private static void FillBasinReq(DTSweepContext tcx, AdvancingFrontNode node) { if (IsShallow(tcx, node)) { return; // if shallow stop filling } Fill(tcx, node); if (node.Prev == tcx.Basin.LeftNode && node.Next == tcx.Basin.RightNode) { return; } else if (node.Prev == tcx.Basin.LeftNode) { Orientation o = TriangulationUtil.Orient2D(node.Point, node.Next.Point, node.Next.Next.Point); if (o == Orientation.CW) { return; } node = node.Next; } else if (node.Next == tcx.Basin.RightNode) { Orientation o = TriangulationUtil.Orient2D(node.Point, node.Prev.Point, node.Prev.Prev.Point); if (o == Orientation.CCW) { return; } node = node.Prev; } else { // Continue with the neighbor node with lowest Y value if (node.Prev.Point.Y < node.Next.Point.Y) { node = node.Prev; } else { node = node.Next; } } FillBasinReq(tcx, node); }
/// <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 PolygonPoint NextFlipPoint(PolygonPoint ep, PolygonPoint eq, DelaunayTriangle ot, PolygonPoint op) { Orientation o2d = TriangulationUtil.Orient2D(eq, op, ep); switch (o2d) { case Orientation.CW: return(ot.PointCCWFrom(op)); case Orientation.CCW: return(ot.PointCWFrom(op)); case Orientation.Collinear: // TODO: implement support for point on constraint edge throw new PointOnEdgeException("Point on constrained edge not supported yet", eq, op, ep); default: throw new NotImplementedException("Orientation not handled"); } }
private static void FillLeftConcaveEdgeEvent(DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node) { Fill(tcx, node.Prev); if (node.Prev.Point != edge.P) { // Next above or below edge? if (TriangulationUtil.Orient2D(edge.Q, node.Prev.Point, edge.P) == Orientation.CW) { // Below if (TriangulationUtil.Orient2D(node.Point, node.Prev.Point, node.Prev.Prev.Point) == Orientation.CW) { // Next is concave FillLeftConcaveEdgeEvent(tcx, edge, node); } else { // Next is convex } } } }
/// <summary> /// Fills a basin that has formed on the Advancing Front to the right /// of given node.<br> /// First we decide a left,bottom and right node that forms the /// boundaries of the basin. Then we do a reqursive fill. /// </summary> /// <param name="tcx"></param> /// <param name="node">starting node, this or next node will be left node</param> private static void FillBasin(DTSweepContext tcx, AdvancingFrontNode node) { if (TriangulationUtil.Orient2D(node.Point, node.Next.Point, node.Next.Next.Point) == Orientation.CCW) { // tcx.basin.leftNode = node.next.next; tcx.Basin.LeftNode = node; } else { tcx.Basin.LeftNode = node.Next; } // Find the bottom and right node tcx.Basin.BottomNode = tcx.Basin.LeftNode; while (tcx.Basin.BottomNode.HasNext && tcx.Basin.BottomNode.Point.Y >= tcx.Basin.BottomNode.Next.Point.Y) { tcx.Basin.BottomNode = tcx.Basin.BottomNode.Next; } if (tcx.Basin.BottomNode == tcx.Basin.LeftNode) { return; // No valid basin } tcx.Basin.RightNode = tcx.Basin.BottomNode; while (tcx.Basin.RightNode.HasNext && tcx.Basin.RightNode.Point.Y < tcx.Basin.RightNode.Next.Point.Y) { tcx.Basin.RightNode = tcx.Basin.RightNode.Next; } if (tcx.Basin.RightNode == tcx.Basin.BottomNode) { return; // No valid basins } tcx.Basin.Width = tcx.Basin.RightNode.Point.X - tcx.Basin.LeftNode.Point.X; tcx.Basin.LeftHighest = tcx.Basin.LeftNode.Point.Y > tcx.Basin.RightNode.Point.Y; FillBasinReq(tcx, tcx.Basin.BottomNode); }
private static void FillRightConvexEdgeEvent(DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node) { // Next concave or convex? if (TriangulationUtil.Orient2D(node.Next.Point, node.Next.Next.Point, node.Next.Next.Next.Point) == Orientation.CCW) { // Concave FillRightConcaveEdgeEvent(tcx, edge, node.Next); } else { // Convex // Next above or below edge? if (TriangulationUtil.Orient2D(edge.Q, node.Next.Next.Point, edge.P) == Orientation.CCW) { // Below FillRightConvexEdgeEvent(tcx, edge, node.Next); } else { // Above } } }