/// <summary> /// Creates a new QuadEdge connecting the destination of a to the origin of b, /// in such a way that all three have the same left face after the connection /// is complete. The quadedge is recorded in the edges list. /// </summary> /// <param name="a">A quadedge</param> /// <param name="b">A quadedge</param> /// <returns>A quadedge</returns> public QuadEdge Connect(QuadEdge a, QuadEdge b) { var q = QuadEdge.Connect(a, b); _quadEdges.Add(q); return(q); }
/// <summary> /// Tests whether a QuadEdge is an edge on the border of the frame facets and /// the internal facets. E.g. an edge which does not itself touch a frame /// vertex, but which touches an edge which does. /// </summary> /// <param name="e">the edge to test</param> /// <returns>true if the edge is on the border of the frame</returns> public bool IsFrameBorderEdge(QuadEdge e) { // MD debugging var leftTri = new QuadEdge[3]; GetTriangleEdges(e, leftTri); // System.out.println(new QuadEdgeTriangle(leftTri).ToString()); var rightTri = new QuadEdge[3]; GetTriangleEdges(e.Sym, rightTri); // System.out.println(new QuadEdgeTriangle(rightTri).ToString()); // check other vertex of triangle to left of edge var vLeftTriOther = e.LNext.Dest; if (IsFrameVertex(vLeftTriOther)) { return(true); } // check other vertex of triangle to right of edge var vRightTriOther = e.Sym.LNext.Dest; if (IsFrameVertex(vRightTriOther)) { return(true); } return(false); }
/// <summary> /// Inserts a new site into the Subdivision, connecting it to the vertices of /// the containing triangle (or quadrilateral, if the split point falls on an /// existing edge). /// </summary> /// <remarks> /// <para> /// This method does NOT maintain the Delaunay condition. If desired, this must /// be checked and enforced by the caller. /// </para> /// <para> /// This method does NOT check if the inserted vertex falls on an edge. This /// must be checked by the caller, since this situation may cause erroneous /// triangulation /// </para> /// </remarks> /// <param name="v">the vertex to insert</param> /// <returns>a new quad edge terminating in v</returns> public QuadEdge InsertSite(Vertex v) { var e = Locate(v); if ((v.Equals(e.Orig, _tolerance)) || (v.Equals(e.Dest, _tolerance))) { return(e); // point already in subdivision. } // Connect the new point to the vertices of the containing // triangle (or quadrilateral, if the new point fell on an // existing edge.) var baseQE = MakeEdge(e.Orig, v); QuadEdge.Splice(baseQE, e); var startEdge = baseQE; do { baseQE = Connect(e, baseQE.Sym); e = baseQE.OPrev; } while (e.LNext != startEdge); return(startEdge); }
/// <summary> /// Gets the Voronoi cell around a site specified /// by the origin of a QuadEdge. /// </summary> /// <remarks> /// The userData of the polygon is set to be the <see cref="Coordinate" /> /// of the site. This allows attaching external /// data associated with the site to this cell polygon. /// </remarks> /// <param name="qe">a quadedge originating at the cell site</param> /// <param name="geomFact">a factory for building the polygon</param> /// <returns>a polygon indicating the cell extent</returns> public Polygon GetVoronoiCellPolygon(QuadEdge qe, GeometryFactory geomFact) { var cellPts = new List <Coordinate>(); var startQE = qe; do { // Coordinate cc = circumcentre(qe); // use previously computed circumcentre var cc = qe.Rot.Orig.Coordinate; cellPts.Add(cc); // move to next triangle CW around vertex qe = qe.OPrev; } while (qe != startQE); var coordList = new CoordinateList(); coordList.AddAll(cellPts, false); coordList.CloseRing(); if (coordList.Count < 4) { Debug.WriteLine(coordList); coordList.Add(coordList[coordList.Count - 1], true); } var pts = coordList.ToCoordinateArray(); var cellPoly = geomFact.CreatePolygon(geomFact.CreateLinearRing(pts)); var v = startQE.Orig; cellPoly.UserData = v.Coordinate; return(cellPoly); }
/// <summary> /// Creates a new quadedge, recording it in the edges list. /// </summary> /// <param name="o">The origin vertex</param> /// <param name="d">The destination vertex</param> /// <returns>A new quadedge</returns> public QuadEdge MakeEdge(Vertex o, Vertex d) { var q = QuadEdge.MakeEdge(o, d); _quadEdges.Add(q); return(q); }
/// <summary> /// Tests whether a <see cref="Vertex"/> is the start or end vertex of a /// <see cref="QuadEdge"/>, up to the subdivision tolerance distance. /// </summary> /// <param name="e" /> /// <param name="v" /> /// <returns>true if the vertex is a endpoint of the edge</returns> public bool IsVertexOfEdge(QuadEdge e, Vertex v) { if ((v.Equals(e.Orig, _tolerance)) || (v.Equals(e.Dest, _tolerance))) { return(true); } return(false); }
/// <summary> /// Creates a new QuadEdge connecting the destination of a to the origin of /// b, in such a way that all three have the same left face after the /// connection is complete. Additionally, the data pointers of the new edge /// are set. /// </summary> /// <returns>the connected edge</returns> public static QuadEdge Connect(QuadEdge a, QuadEdge b) { var e = MakeEdge(a.Dest, b.Orig); Splice(e, a.LNext); Splice(e.Sym, b); return(e); }
/// <summary> /// Tests whether a {@link Coordinate} lies on a {@link QuadEdge}, up to a /// tolerance determined by the subdivision tolerance. /// </summary> /// <param name="e">a QuadEdge</param> /// <param name="p">a point</param> /// <returns>true if the vertex lies on the edge</returns> public bool IsOnEdge(QuadEdge e, Coordinate p) { seg.SetCoordinates(e.Orig.Coordinate, e.Dest.Coordinate); double dist = seg.Distance(p); // heuristic (hack?) return(dist < _edgeCoincidenceTolerance); }
/// <summary> /// Tests whether a QuadEdge is an edge incident on a frame triangle vertex. /// </summary> /// <param name="e">the edge to test</param> /// <returns>true if the edge is connected to the frame triangle</returns> public bool IsFrameEdge(QuadEdge e) { if (IsFrameVertex(e.Orig) || IsFrameVertex(e.Dest)) { return(true); } return(false); }
/// <summary> /// Gets the edges for the triangle to the left of the given <see cref="QuadEdge"/>. /// </summary> /// <param name="startQE" /> /// <param name="triEdge" /> /// <exception cref="ArgumentException">if the edges do not form a triangle</exception> public static void GetTriangleEdges(QuadEdge startQE, QuadEdge[] triEdge) { triEdge[0] = startQE; triEdge[1] = triEdge[0].LNext; triEdge[2] = triEdge[1].LNext; if (triEdge[2].LNext != triEdge[0]) { throw new ArgumentException("Edges do not form a triangle"); } }
private static QuadEdge[] CopyOf(QuadEdge[] edge) { var res = new QuadEdge[edge.Length]; for (int i = 0; i < edge.Length; i++) { res[i] = edge[i]; } return(res); }
/// <summary> /// Locates an edge of a triangle which contains a location /// specified by a Vertex v. /// The edge returned has the /// property that either v is on e, or e is an edge of a triangle containing v. /// The search starts from startEdge amd proceeds on the general direction of v. /// </summary> /// <remarks> /// This locate algorithm relies on the subdivision being Delaunay. For /// non-Delaunay subdivisions, this may loop for ever. /// </remarks> /// <param name="v">the location to search for</param> /// <param name="startEdge">an edge of the subdivision to start searching at</param> /// <returns>a QuadEdge which contains v, or is on the edge of a triangle containing v</returns> /// <exception cref="LocateFailureException"> /// if the location algorithm fails to converge in a reasonable /// number of iterations /// </exception> public QuadEdge LocateFromEdge(Vertex v, QuadEdge startEdge) { int iter = 0; int maxIter = _quadEdges.Count; var e = startEdge; while (true) { iter++; /* * So far it has always been the case that failure to locate indicates an * invalid subdivision. So just fail completely. (An alternative would be * to perform an exhaustive search for the containing triangle, but this * would mask errors in the subdivision topology) * * This can also happen if two vertices are located very close together, * since the orientation predicates may experience precision failures. */ if (iter > maxIter) { throw new LocateFailureException(e.ToLineSegment()); // String msg = "Locate failed to converge (at edge: " + e + "). // Possible causes include invalid Subdivision topology or very close // sites"; // System.err.println(msg); // dumpTriangles(); } if ((v.Equals(e.Orig)) || (v.Equals(e.Dest))) { break; } if (v.RightOf(e)) { e = e.Sym; } else if (!v.RightOf(e.ONext)) { e = e.ONext; } else if (!v.RightOf(e.DPrev)) { e = e.DPrev; } else { // on edge or in triangle containing edge break; } } // System.out.println("Locate count: " + iter); return(e); }
/// <summary> /// Creates a new instance of a quad-edge subdivision based on a frame triangle /// that encloses a supplied bounding box. A new super-bounding box that /// contains the triangle is computed and stored. /// </summary> /// <param name="env">the bounding box to surround</param> /// <param name="tolerance">the tolerance value for determining if two sites are equal</param> public QuadEdgeSubdivision(Envelope env, double tolerance) { // currentSubdiv = this; _tolerance = tolerance; _edgeCoincidenceTolerance = tolerance / EdgeCoincidenceToleranceFactor; CreateFrame(env); _startingEdge = InitSubdiv(); _locator = new LastFoundQuadEdgeLocator(this); }
/// <summary> /// Gets the index for the given edge of this triangle /// </summary> /// <param name="e">a QuadEdge</param> /// <returns>the index of the edge in this triangle,<br/> /// or -1 if the edge is not an edge of this triangle /// </returns> public int GetEdgeIndex(QuadEdge e) { for (int i = 0; i < 3; i++) { if (_edge[i] == e) { return(i); } } return(-1); }
/// <summary> /// Turns an edge counterclockwise inside its enclosing quadrilateral. /// </summary> /// <param name="e">the quadedge to turn</param> public static void Swap(QuadEdge e) { var a = e.OPrev; var b = e.Sym.OPrev; Splice(e, a); Splice(e.Sym, b); Splice(e, a.LNext); Splice(e.Sym, b.LNext); e.Orig = a.Dest; e.Dest = b.Dest; }
private QuadEdge InitSubdiv() { // build initial subdivision from frame var ea = MakeEdge(_frameVertex[0], _frameVertex[1]); var eb = MakeEdge(_frameVertex[1], _frameVertex[2]); QuadEdge.Splice(ea.Sym, eb); var ec = MakeEdge(_frameVertex[2], _frameVertex[0]); QuadEdge.Splice(eb.Sym, ec); QuadEdge.Splice(ec.Sym, ea); return(ea); }
/// <summary> /// Gets all edges which are incident on the origin of the given edge. /// </summary> /// <param name="start">the edge to start at</param> /// <returns>a List of edges which have their origin at the origin of the given /// edge</returns> public static IList <QuadEdge> FindEdgesIncidentOnOrigin(QuadEdge start) { var incEdge = new List <QuadEdge>(); var qe = start; do { incEdge.Add(qe); qe = qe.ONext; } while (qe != start); return(incEdge); }
/// <summary> /// Splices two edges together or apart. /// Splice affects the two edge rings around the origins of a and b, and, independently, the two /// edge rings around the left faces of <tt>a</tt> and <tt>b</tt>. /// In each case, (i) if the two rings are distinct, /// Splice will combine them into one, or (ii) if the two are the same ring, Splice will break it /// into two separate pieces. Thus, Splice can be used both to attach the two edges together, and /// to break them apart. /// </summary> /// <param name="a">an edge to splice</param> /// <param name="b">an edge to splice</param> public static void Splice(QuadEdge a, QuadEdge b) { var alpha = a.ONext.Rot; var beta = b.ONext.Rot; var t1 = b.ONext; var t2 = a.ONext; var t3 = beta.ONext; var t4 = alpha.ONext; a.SetNext(t1); b.SetNext(t2); alpha.SetNext(t3); beta.SetNext(t4); }
/// <summary> /// Deletes a quadedge from the subdivision. Linked quadedges are updated to /// reflect the deletion. /// </summary> /// <param name="e">the quadedge to delete</param> public void Delete(QuadEdge e) { QuadEdge.Splice(e, e.OPrev); QuadEdge.Splice(e.Sym, e.Sym.OPrev); var eSym = e.Sym; var eRot = e.Rot; var eRotSym = e.Rot.Sym; // this is inefficient on an ArrayList, but this method should be called infrequently _quadEdges.Remove(e); _quadEdges.Remove(eSym); _quadEdges.Remove(eRot); _quadEdges.Remove(eRotSym); e.Delete(); eSym.Delete(); eRot.Delete(); eRotSym.Delete(); }
/// <summary> /// Creates a new QuadEdge quartet from <see cref="Vertex"/>o to <see cref="Vertex"/> d. /// </summary> /// <param name="o">the origin Vertex</param> /// <param name="d">the destination Vertex</param> /// <returns>the new QuadEdge quartet</returns> public static QuadEdge MakeEdge(Vertex o, Vertex d) { var q0 = new QuadEdge(); var q1 = new QuadEdge(); var q2 = new QuadEdge(); var q3 = new QuadEdge(); q0.Rot = q1; q1.Rot = q2; q2.Rot = q3; q3.Rot = q0; q0.SetNext(q0); q1.SetNext(q3); q2.SetNext(q2); q3.SetNext(q1); var baseQE = q0; baseQE.Orig = o; baseQE.Dest = d; return(baseQE); }
/// <summary> /// Stores the edges for a visited triangle. Also pushes sym (neighbour) edges /// on stack to visit later. /// </summary> /// <param name="edge" /> /// <param name="edgeStack" /> /// <param name="includeFrame" /> /// <param name="visitedEdges"></param> /// <returns>the visited triangle edges,<br/> /// or <c>null</c> if the triangle should not be visited (for instance, if it is outer) /// </returns> private QuadEdge[] FetchTriangleToVisit(QuadEdge edge, Stack <QuadEdge> edgeStack, bool includeFrame, HashSet <QuadEdge> visitedEdges) { var curr = edge; int edgeCount = 0; bool isFrame = false; do { _triEdges[edgeCount] = curr; if (IsFrameEdge(curr)) { isFrame = true; } // push sym edges to visit next var sym = curr.Sym; if (!visitedEdges.Contains(sym)) { edgeStack.Push(sym); } // mark this edge as visited visitedEdges.Add(curr); edgeCount++; curr = curr.LNext; } while (curr != edge); if (isFrame && !includeFrame) { return(null); } return(_triEdges); }
/// <summary> /// Sets the connected edge /// </summary> /// <param name="next">edge</param> public void SetNext(QuadEdge next) { _next = next; }