public DelaunayTriangle(TriangulationPoint p1, TriangulationPoint p2, TriangulationPoint p3, int i1, int i2, int i3) { Points[0] = p1; Points[1] = p2; Points[2] = p3; indices = new int[] { i1, i2, i3 }; }
public PointOnEdgeException(string message, TriangulationPoint a, TriangulationPoint b, TriangulationPoint c) : base(message) { A = a; B = b; C = c; }
public EdgeLine(Poly2Tri.TriangulationPoint p, Poly2Tri.TriangulationPoint q) { this.p = p; this.q = q; x0 = p.X; y0 = p.Y; x1 = q.X; y1 = q.Y; //------------------- if (x1 == x0) { this.SlopKind = LineSlopeKind.Vertical; SlopAngle = 1; } else { SlopAngle = Math.Abs(Math.Atan2(Math.Abs(y1 - y0), Math.Abs(x1 - x0))); if (SlopAngle > _85degreeToRad) { SlopKind = LineSlopeKind.Vertical; } else if (SlopAngle < _15degreeToRad) { SlopKind = LineSlopeKind.Horizontal; } else { SlopKind = LineSlopeKind.Other; } } }
/// <summary> /// Creates a triangulation of the vertices given, and gives you the indices of it. /// </summary> /// <param name="aPoints">A list of points to triangulate.</param> /// <param name="aTreatAsPath">Should we discard any triangles at all? Use this if you want to get rid of triangles that are outside the path.</param> /// <param name="aInvert">if we're treating it as a path, should we instead sicard triangles inside the path?</param> /// <returns>A magical list of indices describing the triangulation!</returns> public static List<int> GetIndices (ref List<Vector2> aPoints, bool aTreatAsPath, bool aInvert, float aVertGridSpacing = 0) { Vector4 bounds = GetBounds(aPoints); if (aVertGridSpacing > 0) { SplitEdges(ref aPoints, aVertGridSpacing); } List<PolygonPoint> verts = new List<PolygonPoint>(aPoints.Count); for (int i = 0; i < aPoints.Count; i++) { verts.Add(new PolygonPoint( aPoints[i].x, aPoints[i].y)); } Polygon poly; if (aInvert) { aPoints.Add(new Vector2(bounds.x - (bounds.z - bounds.x) * 1, bounds.w - (bounds.y - bounds.w) * 1)); // 4 aPoints.Add(new Vector2(bounds.z + (bounds.z - bounds.x) * 1, bounds.w - (bounds.y - bounds.w) * 1)); // 3 aPoints.Add(new Vector2(bounds.z + (bounds.z - bounds.x) * 1, bounds.y + (bounds.y - bounds.w) * 1)); // 2 aPoints.Add(new Vector2(bounds.x - (bounds.z - bounds.x) * 1, bounds.y + (bounds.y - bounds.w) * 1)); // 1 List<PolygonPoint> outer = new List<PolygonPoint>(4); for (int i = 0; i < 4; i++) { outer.Add( new PolygonPoint( aPoints[(aPoints.Count - 4) + i].x, aPoints[(aPoints.Count - 4) + i].y) ); } poly = new Polygon(outer); poly.AddHole(new Polygon(verts)); } else { poly = new Polygon(verts); } if (aVertGridSpacing > 0) { if (aInvert) bounds = GetBounds(aPoints); for (float y = bounds.w + aVertGridSpacing; y <= bounds.y; y+=aVertGridSpacing) { for (float x = bounds.x + aVertGridSpacing; x <= bounds.z; x+=aVertGridSpacing) { TriangulationPoint pt = new TriangulationPoint(x, y); bool inside = poly.IsPointInside(pt); if (inside) poly.AddSteinerPoint(pt); } } } P2T.Triangulate(poly); aPoints.Clear(); List<int> result= new List<int>(poly.Triangles.Count * 3); int ind = 0; foreach (DelaunayTriangle triangle in poly.Triangles) { TriangulationPoint p1 = triangle.Points[0]; TriangulationPoint p2 = triangle.PointCWFrom(p1); TriangulationPoint p3 = triangle.PointCWFrom(p2); aPoints.Add(new Vector2(p1.Xf, p1.Yf)); aPoints.Add(new Vector2(p2.Xf, p2.Yf)); aPoints.Add(new Vector2(p3.Xf, p3.Yf)); result.Add(ind++); result.Add(ind++); result.Add(ind++); } return result; }
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; }
public void SetConstrainedEdgeCW(TriangulationPoint p, bool ce) { int idx = (IndexOf(p) + 1) % 3; SetConstrainedEdge(idx, ce); }
public TriangulationPoint PointCWFrom(TriangulationPoint point) { return Points[(IndexOf(point) + 2) % 3]; }
public DelaunayTriangle NeighborCWFrom(TriangulationPoint point) { return Neighbors[(Points.IndexOf(point) + 1) % 3]; }
/// <summary> /// Mark edge as constrained /// </summary> public void MarkConstrainedEdge(TriangulationPoint p, TriangulationPoint q) { int i = EdgeIndex(p, q); if (i != -1) { mEdgeIsConstrained[i] = true; } }
public DelaunayTriangle NeighborAcrossFrom(TriangulationPoint point) { return(Neighbors[Points.IndexOf(point)]); }
private static bool IsEdgeSideOfTriangle(DelaunayTriangle triangle, TriangulationPoint ep, TriangulationPoint eq) { int index = triangle.EdgeIndex(ep, eq); if (index == -1) return false; triangle.MarkConstrainedEdge(index); triangle = triangle.Neighbors[index]; if (triangle != null) triangle.MarkConstrainedEdge(ep, eq); return true; }
/// <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 = 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; } } } Search = node; return node; }
public bool GetDelaunayEdgeCW(TriangulationPoint p) { return(EdgeIsDelaunay[(IndexOf(p) + 1) % 3]); }
public void SetConstrainedEdgeAcross(TriangulationPoint p, bool ce) { int idx = IndexOf(p); SetConstrainedEdge(idx, ce); }
public bool GetConstrainedEdgeAcross(TriangulationPoint p) { return(EdgeIsConstrained[IndexOf(p)]); }
public bool GetConstrainedEdgeCW(TriangulationPoint p) { return(EdgeIsConstrained[(IndexOf(p) + 1) % 3]); }
/// <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[IndexCCWFrom(oPoint)] = nPoint; }
public TriangulationPoint PointCWFrom(TriangulationPoint point) { return(Points[(IndexOf(point) + 2) % 3]); }
public void SetDelaunayEdgeCW(TriangulationPoint p, bool ce) { EdgeIsDelaunay[(IndexOf(p) + 1) % 3] = ce; }
/// <summary> /// Update neighbor pointers /// </summary> /// <param name="p1">Point 1 of the shared edge</param> /// <param name="p2">Point 2 of the shared edge</param> /// <param name="t">This triangle's new neighbor</param> private void MarkNeighbor(TriangulationPoint p1, TriangulationPoint p2, DelaunayTriangle t) { int i = EdgeIndex(p1, p2); if (i == -1) { throw new Exception("Error marking neighbors -- t doesn't contain edge p1-p2!"); } Neighbors[i] = t; }
public bool GetDelaunayEdgeAcross(TriangulationPoint p) { return(EdgeIsDelaunay[IndexOf(p)]); }
/// <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, newNode; node = tcx.LocateNode(point); if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = node; 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; }
/// <summary> /// In the case of a pointset with some constraint edges. If a triangle side is collinear /// with a part of the constraint we split the constraint into two constraints. This could /// happen when the given constraint migth intersect a point in the set.<br/> /// This can never happen in the case when we are working with a polygon. /// /// Think of two triangles that have non shared sides that are collinear and the constraint /// is set from a point in triangle A to a point in triangle B so that the constraint is /// the union of both those sides. We then have to split the constraint into two so we get /// one constraint for each triangle. /// </summary> /// <param name="ep"></param> /// <param name="eq"></param> /// <param name="p">point on the edge between ep->eq</param> private static void SplitEdge(TriangulationPoint ep, TriangulationPoint eq, TriangulationPoint p) { DTSweepConstraint edge = eq.Edges.First(e => e.Q == ep || e.P == ep); edge.P = p; new DTSweepConstraint(ep, p); // Et tu, Brute? --MM // // Redo this edge now that we have split the constraint // newEdgeEvent( tcx, edge, triangle, point ); // // Continue with new edge // newEdgeEvent( tcx, edge, triangle, p2 ); }
public void SetDelaunayEdgeAcross(TriangulationPoint p, bool ce) { EdgeIsDelaunay[IndexOf(p)] = ce; }
public DelaunayTriangle(TriangulationPoint p1, TriangulationPoint p2, TriangulationPoint p3) { Points[0] = p1; Points[1] = p2; Points[2] = p3; }
public DelaunayTriangle NeighborAcrossFrom(TriangulationPoint point) { return Neighbors[Points.IndexOf(point)]; }
public int IndexCCWFrom(TriangulationPoint p) { return((IndexOf(p) + 1) % 3); }
/// <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 PointCWFrom(t.PointCWFrom(p)); }
public bool Contains(TriangulationPoint p) { return(Points.Contains(p)); }
/// <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(PointCWFrom(t.PointCWFrom(p))); }
public static Vector3 V3(Poly2Tri.TriangulationPoint tp) { return(new Vector3((float)tp.X, (float)tp.Y, 0f)); }
public bool SharedEdge(TriangulationPoint p1, TriangulationPoint p2) { bool shared = false; foreach (DelaunayTriangle t in Neighbors) { if (t != null) { if (t.Contains(p1) && t.Contains(p2)) { shared = true; UnityEngine.Debug.Log("Shared edge"); } else { UnityEngine.Debug.Log("Non shared edge"); } } else { UnityEngine.Debug.Log("Null edge"); } } return shared; }
public static Vector2 UV(Poly2Tri.TriangulationPoint tp, float scale = 1.0f, float offset = 0.5f) { return(new Vector2((float)(tp.X + offset) * scale, (float)(tp.Y + offset) * scale)); }
public bool contains(TriangulationPoint p, TriangulationPoint q) { return(contains(p) && contains(q)); }
/// <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> /// 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); 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"); } }
/// <summary> /// Creates a new front triangle and legalize it /// </summary> private static AdvancingFrontNode NewFrontTriangle(DTSweepContext tcx, TriangulationPoint point, AdvancingFrontNode node) { AdvancingFrontNode newNode; DelaunayTriangle triangle; triangle = new DelaunayTriangle(point, node.Point, node.Next.Point); triangle.MarkNeighbor(node.Triangle); tcx.Triangles.Add(triangle); newNode = new AdvancingFrontNode(point); newNode.Next = node.Next; newNode.Prev = node; node.Next.Prev = newNode; node.Next = newNode; tcx.AddNode(newNode); // XXX: BST if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = newNode; if (!Legalize(tcx, triangle)) tcx.MapTriangleToNodes(triangle); return newNode; }
/// <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; TriangulationPoint op, newP; bool inScanArea; ot = t.NeighborAcrossFrom(p); 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"); } if (tcx.IsDebugEnabled) { Console.WriteLine("[FLIP:SCAN] - scan next point"); // TODO: remove tcx.DTDebugContext.PrimaryTriangle = t; tcx.DTDebugContext.SecondaryTriangle = ot; } inScanArea = TriangulationUtil.InScanArea(eq, flipTriangle.PointCCWFrom(eq), flipTriangle.PointCWFrom(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 { newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, flipTriangle, ot, newP); } }
private static void EdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle triangle, TriangulationPoint point) { TriangulationPoint p1, p2; if (tcx.IsDebugEnabled) tcx.DTDebugContext.PrimaryTriangle = triangle; 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); } }
public bool contains(TriangulationPoint p) { return(p == points[0] || p == points[1] || p == points[2]); }
private static void FlipEdgeEvent(DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle t, TriangulationPoint p) { DelaunayTriangle ot = t.NeighborAcrossFrom(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 (tcx.IsDebugEnabled) { tcx.DTDebugContext.PrimaryTriangle = t; tcx.DTDebugContext.SecondaryTriangle = ot; } // TODO: remove 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) { 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); } }
public DelaunayTriangle NeighborCCWFrom(TriangulationPoint point) { return(Neighbors[(Points.IndexOf(point) + 2) % 3]); }
/// <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; }
public int IndexCWFrom(TriangulationPoint p) { return (IndexOf(p) + 2) % 3; }
/// <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, n2, n3, n4; n1 = t.NeighborCCWFrom(p); n2 = t.NeighborCWFrom(p); n3 = ot.NeighborCCWFrom(op); n4 = ot.NeighborCWFrom(op); bool ce1, ce2, ce3, ce4; ce1 = t.GetConstrainedEdgeCCW(p); ce2 = t.GetConstrainedEdgeCW(p); ce3 = ot.GetConstrainedEdgeCCW(op); ce4 = ot.GetConstrainedEdgeCW(op); bool de1, de2, de3, de4; de1 = t.GetDelaunayEdgeCCW(p); de2 = t.GetDelaunayEdgeCW(p); de3 = ot.GetDelaunayEdgeCCW(op); 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); }