//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Insert the edge at infinity into the edge list for the vertices at infinity. </summary> /// <remarks> Darrellp, 2/19/2011. </remarks> /// <param name="edge"> Edge at infinity being added. </param> /// <param name="leadingVtxCw"> Vertex on the left of infinite poly as we look out. </param> /// <param name="trailingVtxCw"> Vertex on the right. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void HookEdgeAtInfinityToVerticesAtInfinity(FortuneEdge edge, FortuneVertex leadingVtxCw, FortuneVertex trailingVtxCw) { // if we've only got one edge at infinity if (leadingVtxCw.FortuneEdges.Count == 1) { // Add this one as a placeholder // // This will be overwritten later but we have to insert here so that we can insert ourselves at // index 2. This is just a placeholder. leadingVtxCw.FortuneEdges.Add(edge); } // Add this edge into it's proper position leadingVtxCw.FortuneEdges.Add(edge); // If we have three edges if (trailingVtxCw.FortuneEdges.Count == 3) { // Overwrite the placeholder we placed in another call // // Here is where the overwriting referred to above occurs // This will happen on a later call - not on the current one trailingVtxCw.FortuneEdges[1] = edge; } else { // Otherwise just add us in to the trailing vertex's list of edges trailingVtxCw.FortuneEdges.Add(edge); } }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Finds the third polygon which created this vertex besides the two on each side of the passed /// in edge. /// </summary> /// <remarks> Darrellp, 2/19/2011. </remarks> /// <param name="edge"> Edge in question. </param> /// <returns> The polygon "opposite" this edge. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// internal FortunePoly PolyThird(FortuneEdge edge) { // Get the indices for the two polys on each side of our passed in edge var i1 = edge.Poly1.Index; var i2 = edge.Poly2.Index; // For each edge incident with this vertex foreach (var edgeDifferent in FortuneEdges) { // If it's not our own edge if (edgeDifferent != edge) { // The polygon we want is on one side or the other of this edge var i1Diff = edgeDifferent.Poly1.Index; // If that edge's poly1 is one of our polygons if (i1Diff == i1 || i1Diff == i2) { // Then we're looking for his poly2 return(edgeDifferent.Poly2); } // Otherwise, we're looking for his poly1 return(edgeDifferent.Poly1); } } return(null); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Insert a new parabola into the beachline when the beachline spans the X axis. </summary> /// <remarks> /// This is the normal case. We insert our new parabola and split the parabola above our site in /// two. This means one new leaf node is created for leftmost of the two nodes in the split (the /// old lfn is recycled to become the right node of the split). Also a new internal node to /// parent all this. /// </remarks> /// <param name="lfnOld"> Parabola above the new site. </param> /// <param name="lfnNewParabola"> parabola for the new site. </param> /// <param name="innSubRoot"> /// Parent node of both lfnOld and lfnNewParabola represneting /// the breakpoint between them. /// </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void InsertAtDifferentY(LeafNode lfnOld, LeafNode lfnNewParabola, InternalNode innSubRoot) { // The old lfn will become the new right half of the split but we need a new leaf node // for the left half of the split... var lfnLeftHalf = new LeafNode(lfnOld.Poly); var innSubRootLeftChild = new InternalNode(lfnOld.Poly, lfnNewParabola.Poly); var edge = new FortuneEdge(); // This is all fairly straightforward (albeit dense) insertion of a node into a binary tree. innSubRoot.RightChild = lfnOld; innSubRoot.LeftChild = innSubRootLeftChild; innSubRoot.SetEdge(edge); innSubRoot.AddEdgeToPolygons(edge); innSubRootLeftChild.LeftChild = lfnLeftHalf; innSubRootLeftChild.RightChild = lfnNewParabola; innSubRootLeftChild.SetEdge(edge); lfnNewParabola.LeftAdjacentLeaf = lfnLeftHalf; lfnNewParabola.RightAdjacentLeaf = lfnOld; lfnLeftHalf.LeftAdjacentLeaf = lfnOld.LeftAdjacentLeaf; lfnLeftHalf.RightAdjacentLeaf = lfnNewParabola; if (lfnOld.LeftAdjacentLeaf != null) { lfnOld.LeftAdjacentLeaf.RightAdjacentLeaf = lfnLeftHalf; } lfnOld.LeftAdjacentLeaf = lfnNewParabola; edge.SetPolys(innSubRoot.PolyRight, innSubRoot.PolyLeft); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Set up the polygon at infinity. The main difficulty here consists in traversing around the /// infinite polygons at the edge of the diagram in order. /// </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="we"> WingedEdge structure we'll add the polygon at infinity to. </param> /// <param name="polyStart"> Infinite polygon to start the polygon at infinity's polygon list with. </param> /// <param name="iLeadingEdgeCw"> Starting infinite edge. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void AddPolygonAtInfinity(WE we, FortunePoly polyStart, int iLeadingEdgeCw) { // See if we've got a degenerate case // // Such as a single point if (polyStart == null) { return; } // Initialize var polyCur = polyStart; int iLeadingEdgeNext; // Create the infamous polygon at infinity... var polyAtInfinity = new FortunePoly(new Vector(0, 0), -1); FortuneEdge edgePreviousAtInfinity = null; // Declare this the official polygon at infinity polyAtInfinity.FAtInfinity = true; // Add it to the winged edge we.AddPoly(polyAtInfinity); do { // Add the edge at infinity between our current poly and the poly at infinity edgePreviousAtInfinity = AddEdgeAtInfinity( polyAtInfinity, polyCur, iLeadingEdgeCw, edgePreviousAtInfinity, out var polyNext, out iLeadingEdgeNext); we.AddEdge(edgePreviousAtInfinity); // Move to the neighboring polygon at infinity polyCur = polyNext; iLeadingEdgeCw = iLeadingEdgeNext; // Save this edge as the "first edge" for the polygon at infinity // // We could check to do this only once, but it doesn't make any difference which // edge we get, just so long as we get one and the check would take as long as // just doing it every time. polyAtInfinity.FirstEdge = edgePreviousAtInfinity; } // we reach the end of the "outer" infinite polygons of the diagram // // Set each of the outer infinite polygons up with an edge at infinity to separate // them from the polygon at infinity while (polyCur != polyStart); // Thread the last poly back to the first var edgeFirstAtInfinity = polyCur.FortuneEdges[iLeadingEdgeNext]; edgePreviousAtInfinity.EdgeCCWPredecessor = edgeFirstAtInfinity; edgeFirstAtInfinity.EdgeCWSuccessor = edgePreviousAtInfinity; }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Remove an edge. </summary> /// <remarks> This really only makes much sense for zero length edges. </remarks> /// <param name="edge"> Edge to remove. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void DetachEdge(FortuneEdge edge) { // Remove the zero length edge // // We do this by removing it's end vertex, reassigning the proper // vertex in each of the edges which formerly connected to that vertex and splicing those edges into the // the proper spot for the edge list of our start vertex and finally removing it from the edge list of // both polygons which it adjoins. edge.ReassignVertexEdges(); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Handle the top N nodes located on a single horizontal line. </summary> /// <remarks> /// This only handles the corner case where the top N nodes are on the same horizontal /// line. In that case the parabolas from previous points are vertically straight up and only /// project to a single point on the x axis so that the beachline is a series of points rather /// than a series of parabolas. When that is the case we can't "intersect" new points with /// parabolas that span the x axis. After the scanline passes that initial set of topmost points, /// there will always be a parabola which projects to the entire x axis so no need for this /// special handling. Normally, we produce two new parabolas at a site event like this - the new /// parabola for the site itself and the new parabola produced when we split the parabola above /// us. In this case there is no parabola above us so we only produce one new parabola - the one /// inserted by the site. /// </remarks> /// <param name="lfn"> LeafNode of the (degenerate) parabola nearest us. </param> /// <param name="lfnNewParabola"> LeafNode we're inserting. </param> /// <param name="innParent"> Parent of lfnOld. </param> /// <param name="innSubRoot"> Root of the tree. </param> /// <param name="fLeftChild"> Left child of innParent. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void NdInsertAtSameY( LeafNode lfn, LeafNode lfnNewParabola, InternalNode innParent, InternalNode innSubRoot, bool fLeftChild) { // Locals LeafNode lfnLeft, lfnRight; var lfnAdjacentParabolaLeft = lfn.LeftAdjacentLeaf; var lfnAdjacentParabolaRight = lfn.RightAdjacentLeaf; if (lfnNewParabola.Poly.VoronoiPoint.X < lfn.Poly.VoronoiPoint.X) { lfnLeft = lfnNewParabola; lfnRight = lfn; } else { //! Note: I don't think this case ever occurs in practice since we pull events off with higher //! x coordinates before events with lower x coordinates lfnLeft = lfn; lfnRight = lfnNewParabola; } innSubRoot.PolyLeft = lfnLeft.Poly; innSubRoot.PolyRight = lfnRight.Poly; innSubRoot.LeftChild = lfnLeft; innSubRoot.RightChild = lfnRight; var edge = new FortuneEdge(); innSubRoot.SetEdge(edge); innSubRoot.AddEdgeToPolygons(edge); lfnLeft.LeftAdjacentLeaf = lfnAdjacentParabolaLeft; lfnLeft.RightAdjacentLeaf = lfnRight; lfnRight.LeftAdjacentLeaf = lfnLeft; lfnRight.RightAdjacentLeaf = lfnAdjacentParabolaRight; if (innParent != null) { if (fLeftChild) { innParent.PolyLeft = lfnRight.Poly; } else { innParent.PolyRight = lfnLeft.Poly; } } edge.SetPolys(innSubRoot.PolyRight, innSubRoot.PolyLeft); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Find the common polygon between two edges. An assertion will be raised if there is no common /// polygon. /// </summary> /// <remarks> Darrellp, 2/19/2011. </remarks> /// <param name="edge"> Edge to find a common poly with. </param> /// <returns> The common polygon. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// private FortunePoly PolyCommon(FortuneEdge edge) { // If my Poly1 is the same as one of the edge's polys if (ReferenceEquals(Poly1, edge.Poly1) || ReferenceEquals(Poly1, edge.Poly2)) { // Return poly1 return(Poly1); } // Otherwise, return Poly2 return(Poly2); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> One of our incoming edges is zero length so note it properly in the polygons. </summary> /// <remarks> /// This happens when cocircular generators cause more than one circle event at the same /// location. /// </remarks> /// <param name="edgeNearSibling"> Immediate sibling. </param> /// <param name="edgeFarSibling"> Far sibling. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void SetZeroLengthFlagOnPolys(FortuneEdge edgeNearSibling, FortuneEdge edgeFarSibling) { // If it's the edge between us and our near sibling that's zero length if (edgeNearSibling.VtxEnd != null && edgeNearSibling.FZeroLength()) { // Both polys on each side of the far edge need to be marked as having a zero length edge edgeNearSibling.Poly1.FZeroLengthEdge = edgeNearSibling.Poly2.FZeroLengthEdge = true; } else { // Both polys on each side of the near edge need to be marked as having a zero length edge edgeFarSibling.Poly1.FZeroLengthEdge = edgeFarSibling.Poly2.FZeroLengthEdge = true; } }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Splits a doubly infinite edge. </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="edge"> Edge we need to split. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void SplitDoublyInfiniteEdge(FortuneEdge edge) { // Initialize var pt1 = edge.Poly1.VoronoiPoint; var pt2 = edge.Poly2.VoronoiPoint; var dx = pt2.X - pt1.X; var dy = pt2.Y - pt1.Y; var ptMid = new Vector((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2); // Infinite vertices have directions in them rather than locations var vtx1 = FortuneVertex.InfiniteVertex(new Vector(-dy, dx)); var vtx2 = FortuneVertex.InfiniteVertex(new Vector(dy, -dx)); // Create the new edge an link it in edge.VtxStart = new FortuneVertex(ptMid); edge.VtxEnd = vtx1; var edgeNew = new FortuneEdge { VtxStart = edge.VtxStart, VtxEnd = vtx2 }; edgeNew.SetPolys(edge.Poly1, edge.Poly2); edge.Poly1.AddEdge(edgeNew); edge.Poly2.AddEdge(edgeNew); ((FortuneVertex)edge.VtxStart).Add(edge); ((FortuneVertex)edge.VtxStart).Add(edgeNew); vtx1.Add(edge); vtx2.Add(edgeNew); edge.FSplit = edgeNew.FSplit = true; // If the edge "leans right" // // We have to be very picky about how we set up the left and right // polygons for our new rays. // ReSharper disable once CompareOfFloatsByEqualityOperator if (dx == 0 || dx * dy > 0) // dy == 0 case needs to fall through... { // Set up left and right polygons one way edge.PolyRight = edgeNew.PolyLeft = edge.Poly1; edge.PolyLeft = edgeNew.PolyRight = edge.Poly2; } else { // Set left and right polygons the other way edge.PolyLeft = edgeNew.PolyRight = edge.Poly1; edge.PolyRight = edgeNew.PolyLeft = edge.Poly2; } }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Turn a null endpoint of the ray into an infinite vertex. </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="edge"> Edge with the null vertex. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void ProcessRay(FortuneEdge edge) { // If it's the start vertex that's null if (edge.VtxStart == null) { // Swap the vertices // // This is what justifies the assertion in WeEdge.FLeftOf() (see the code). edge.VtxStart = edge.VtxEnd; edge.VtxEnd = null; } // Replace the null vertex with an infinite vertex var pt1 = edge.Poly1.VoronoiPoint; var pt2 = edge.Poly2.VoronoiPoint; var ptMid = new Vector((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2); // Point the ray in the proper direction // // We have to be careful to get this ray pointed in the proper orientation. At // this point we just have a vertex and points which are the original voronoi // points which created these infinite polys. That's enough to figure out the // absolute direction the voronoi points but not enough to determine which "orientation" // it points along. To do this, we find the third polygon at the "base" of this ray // and point the ray "away" from it. var polyThird = ((FortuneVertex)edge.VtxStart).PolyThird(edge); var fThirdOnLeft = FLeft(pt1, pt2, polyThird.VoronoiPoint); var dx = pt2.X - pt1.X; var dy = pt2.Y - pt1.Y; var ptProposedDirection = new Vector(dy, -dx); var ptInProposedDirection = new Vector(ptMid.X + dy, ptMid.Y - dx); var fProposedOnLeft = FLeft(pt1, pt2, ptInProposedDirection); // Do we need to reverse orientation? if (fProposedOnLeft == fThirdOnLeft) { // Do it ptProposedDirection.X = -ptProposedDirection.X; ptProposedDirection.Y = -ptProposedDirection.Y; } // Create the new infinite vertex and add it to our edge edge.VtxEnd = FortuneVertex.InfiniteVertex(ptProposedDirection); ((FortuneVertex)edge.VtxEnd).FortuneEdges.Add(edge); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Determine whether two edges connect at a common vertex and if so, how they connect. /// </summary> /// <remarks> Darrellp, 2/19/2011. </remarks> /// <param name="edge1"> First edge. </param> /// <param name="edge2"> Second edge. </param> /// <param name="fEdge1ConnectsAtStartVtx"> /// [out] True if edge1 connects to edge2 at its start /// vertex, else false. /// </param> /// <param name="fEdge2ConnectsAtStartVtx"> /// [out] True if edge2 connects to edge1 at its start /// vertex, else false. /// </param> /// <returns> true if the edges connect. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// internal static bool FConnectsTo( FortuneEdge edge1, FortuneEdge edge2, out bool fEdge1ConnectsAtStartVtx, out bool fEdge2ConnectsAtStartVtx) { // Locals var fRet = false; // Init out parameters to false fEdge1ConnectsAtStartVtx = false; fEdge2ConnectsAtStartVtx = false; // RQS- Compare starting and ending vertices if (ReferenceEquals(edge1.VtxStart, edge2.VtxStart)) { fEdge1ConnectsAtStartVtx = true; fEdge2ConnectsAtStartVtx = true; fRet = true; } else if (ReferenceEquals(edge1.VtxStart, edge2.VtxEnd)) { fEdge1ConnectsAtStartVtx = true; fRet = true; } else if (ReferenceEquals(edge1.VtxEnd, edge2.VtxStart)) { fEdge2ConnectsAtStartVtx = true; fRet = true; } else if (ReferenceEquals(edge1.VtxEnd, edge2.VtxEnd)) { fRet = true; } //-RQS // Return the result return(fRet); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Get the next edge in both the cw and ccw directions from this edge at the given 3 valent /// vertex. /// </summary> /// <remarks> /// This is the simplest and most common case. It's called before the winged edge stuff is set up /// so we have to search manually. The edges have been sorted in clockwise order however. /// Darrellp, 2/19/2011. /// </remarks> /// <param name="vtx"> vertex to use. </param> /// <param name="edgeCW"> [out] returned cw edge. </param> /// <param name="edgeCCW"> [out] returned ccw edge. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private void GetSuccessorEdgesFrom3ValentVertex( FortuneVertex vtx, out FortuneEdge edgeCW, out FortuneEdge edgeCCW) { // Locals int iEdge; // Figure out which of the edges is ours for (iEdge = 0; iEdge < 3; iEdge++) { // If the current edge is us if (ReferenceEquals(vtx.FortuneEdges[iEdge], this)) { break; } } // The next two edges, in order, are the onew we're looking for edgeCW = vtx.FortuneEdges[(iEdge + 1) % 3]; edgeCCW = vtx.FortuneEdges[(iEdge + 2) % 3]; }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Get the next edge in both the cw and ccw directions from this edge at the given vertex. /// </summary> /// <remarks> /// This routine is called before they've been set up as winged edges so we have to search them /// out ourselves. The edges have been ordered in CW order, however. /// Darrellp, 2/19/2011. /// </remarks> /// <param name="vtx"> vertex to use. </param> /// <param name="edgeCW"> [out] returned cw edge. </param> /// <param name="edgeCCW"> [out] returned ccw edge. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private void GetSuccessorEdgesFromVertex( FortuneVertex vtx, out FortuneEdge edgeCW, out FortuneEdge edgeCCW) { // Are we free of zero length edges? if (vtx.FortuneEdges.Count == 3) { // Do the extremely simple, extremely common 3 valent case GetSuccessorEdgesFrom3ValentVertex(vtx, out edgeCW, out edgeCCW); } else { // Locals int iEdge; int cEdges; // If we're looking at our start vertex if (vtx == VtxStart) { // Find our place in the list of edges for start vertex iEdge = EdgeIndex(true); cEdges = ((FortuneVertex)VtxStart).CtEdges; } else { // Find our place in the list of edges for end vertex iEdge = EdgeIndex(false); cEdges = ((FortuneVertex)VtxEnd).CtEdges; } // Get our immediate neighbors on the edge list edgeCW = vtx.FortuneEdges[(iEdge + 1) % cEdges]; edgeCCW = vtx.FortuneEdges[(iEdge + cEdges - 1) % cEdges]; } }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Remove a parabola node from the beachline since it's being squeezed out and insert a vertex /// into the voronoi diagram. /// </summary> /// <remarks> /// This happens when a circle event occurs. It's a rather delicate operation. From the point of /// view of the voronoi diagram, we have two edges from above coming together into the newly /// created vertex and a new edge created which descends below it. This is really where the meat /// of actually creating the voronoi diagram occurs. One of the important details which seems to /// be left totally out of the book is the importance of keeping accurate left and right sibling /// pointers on the leaf nodes. Since each leaf node represents a parabola in the beachline, /// these pointers represent the set of parabolas from the left to the right of the beachline. /// In a case like this where a parabola is being squeezed out, it's left and right siblings will /// not butt up against each other forming a new edge and we need to be able to locate both these /// nodes in order to make everything come out right. /// This is very persnickety code. /// </remarks> /// <param name="cevt"> Circle event which caused this. </param> /// <param name="lfnEliminated"> Leaf node for the parabola being eliminated. </param> /// <param name="voronoiVertex"> The new vertex to be inserted into the voronoi diagram. </param> /// <param name="evq"> Event queue. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void RemoveNodeAndInsertVertex(CircleEvent cevt, LeafNode lfnEliminated, Vector voronoiVertex, EventQueue evq) { // Initialize var yScanLine = cevt.Pt.Y; // Determine whether we're the left or right child of our parent var fLeftChildEliminated = lfnEliminated.IsLeftChild; var innParent = lfnEliminated.NdParent; // Retrieve sibling nodes var lfnLeft = lfnEliminated.LeftAdjacentLeaf; var lfnRight = lfnEliminated.RightAdjacentLeaf; var lfnNearSibling = fLeftChildEliminated ? lfnRight : lfnLeft; // Remove from the queue any circle events which involve the eliminated node or its siblings RemoveAssociatedCircleEvents(lfnEliminated, evq); // remove the leaf from the tree and rearrange the nodes around it RemoveLeaf(lfnEliminated); // Locate the internal node which represents the breakpoint between our near and far sibling var innFarSiblingEdge = lfnNearSibling.InnFindSiblingEdge(fLeftChildEliminated); // Get the edges being developed on each side of us // // The meeting of these edges is what causes the creation of our vertex in the voronoi diagram var edgeNearSibling = innParent.Edge; var edgeFarSibling = innFarSiblingEdge.Edge; // Create a new fortune vertex to insert into the diagram var vertex = new FortuneVertex { Pt = voronoiVertex }; // Give both edges from above their brand new vertex - hooray! edgeFarSibling.AddVertex(vertex); edgeNearSibling.AddVertex(vertex); // Is this a zero length edge? // // Some of our incoming edges are zero length due to cocircular points, // so keep track of it in the polys which border them. This will be used // later in Fortune.BuildWingedEdge() to determine when to try and remove // zero length edges. if (cevt.FZeroLength) { // Mark the poly as having a zero length edge. // // We can't eliminate it here because most of our winged edge machinery // needs to assume three edges entering every vertex. We'll take care of // it later in post-processing. This flag is the signal to do that. SetZeroLengthFlagOnPolys(edgeNearSibling, edgeFarSibling); } // RQS- Add edges to the vertex in proper clockwise direction if (fLeftChildEliminated) { vertex.Add(edgeFarSibling); vertex.Add(edgeNearSibling); } else { vertex.Add(edgeNearSibling); vertex.Add(edgeFarSibling); } // -RQS // Create the new edge which emerges below this vertex var edge = new FortuneEdge(); edge.AddVertex(vertex); vertex.Add(edge); // Add the edge to our siblings // // Since lfnEliminated is being removed, it's siblings now butt against each other forming // the new edge so save that edge on the internal node and add it to the poly for the // generator represented by the near sibling. This means that polygon edges get added in // a fairly random order. They'll be sorted in postprocessing. // Add it to our winged edge polygon lfnNearSibling.Poly.AddEdge(edge); // Also add it to our beachline sibling innFarSiblingEdge.Edge = edge; // Fix up the siblings and the edges/polygons they border // // The inner node which used to represent one of the incoming edges now takes on responsibility // for the newly created edge so it no longer borders the polygon represented by the eliminated // leaf node, but rather borders the polygon represented by its sibling on the other side. // Also, that polygon receives a new edge. // If we eliminated the left child from a parent if (fLeftChildEliminated) { // Reset the data on the inner node representing our far sibling innFarSiblingEdge.PolyRight = lfnNearSibling.Poly; innFarSiblingEdge.PolyLeft.AddEdge(edge); // If this event represented a zero length edge if (cevt.FZeroLength) { // Keep track of it in the fortune polygon innFarSiblingEdge.PolyLeft.FZeroLengthEdge = true; } } else { // Reset the data on the inner node representing our far sibling innFarSiblingEdge.PolyLeft = lfnNearSibling.Poly; innFarSiblingEdge.PolyRight.AddEdge(edge); // If this event represented a zero length edge if (cevt.FZeroLength) { // Keep track of it in the fortune polygon innFarSiblingEdge.PolyRight.FZeroLengthEdge = true; } } // Set the polygons which border the new edge edge.SetPolys(innFarSiblingEdge.PolyRight, innFarSiblingEdge.PolyLeft); // Create new circle events for our siblings // // Now that we're squeezed out, our former left and right siblings become immediate siblings // so we need to set new circle events to represent when they get squeezed out by their // newly acquired siblings CreateCircleEventFromTriple(lfnLeft.LeftAdjacentLeaf, lfnLeft, lfnRight, yScanLine, evq); CreateCircleEventFromTriple(lfnLeft, lfnRight, lfnRight.RightAdjacentLeaf, yScanLine, evq); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Sets our edge for this internal node. </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="edge"> The edge. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void SetEdge(FortuneEdge edge) { Edge = edge; }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Adds an edge to the fortune polygons we abut. </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="edge"> The edge. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void AddEdgeToPolygons(FortuneEdge edge) { PolyLeft.AddEdge(edge); PolyRight.AddEdge(edge); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Edges are assumed to be added in a Clockwise direction. The first edge is random and has no /// particular significance. /// </summary> /// <remarks> Darrellp, 2/19/2011. </remarks> /// <param name="edge"> Next clockwise edge to add. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// public void Add(FortuneEdge edge) { FortuneEdges.Add(edge); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Adds an edge to the Fortune polygon. </summary> /// <remarks> Darrellp, 2/22/2011. </remarks> /// <param name="edge"> The edge to be added. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void AddEdge(FortuneEdge edge) { FortuneEdges.Add(edge); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Add an edge at infinity and step the polygon and edge along to the next infinite polygon and /// rayed edge. /// </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="polyAtInfinity"> Polygon at infinity. </param> /// <param name="poly"> Infinite polygon we're adding the edge to. </param> /// <param name="iLeadingEdgeCw"> index to rayed edge we start with. </param> /// <param name="edgePreviousAtInfinity"> /// Edge at infinity we added in the previous infinite /// polygon. /// </param> /// <param name="polyNextCcw"> /// [out] Returns the next infinite polygon to be /// processed. /// </param> /// <param name="iLeadingEdgeNext"> [out] Returns the next infinite edge. </param> /// <returns> . </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// private static FortuneEdge AddEdgeAtInfinity( FortunePoly polyAtInfinity, FortunePoly poly, int iLeadingEdgeCw, WeEdge edgePreviousAtInfinity, out FortunePoly polyNextCcw, out int iLeadingEdgeNext) { // Get the other infinite edge // // This is the edge to the right as we look outward var iTrailingEdgeCw = (iLeadingEdgeCw + 1) % poly.VertexCount; var edgeLeadingCw = poly.FortuneEdges[iLeadingEdgeCw]; var edgeTrailingCw = poly.FortuneEdges[iTrailingEdgeCw]; // Next polygon in order is to the left of our leading edge polyNextCcw = edgeLeadingCw.PolyLeft as FortunePoly; // Create the edge at infinity // // Create the edge at infinity separating the current infinite polygon from // the polygon at infinity. The vertices for this edge will both be vertices // at infinity. This, of course, doesn't really have any real impact on the // "position" of the edge at infinity, but allows us to maintain a properly // set up winged edge structure. var edgeAtInfinity = new FortuneEdge { PolyRight = poly, PolyLeft = polyAtInfinity, VtxStart = edgeLeadingCw.VtxEnd, VtxEnd = edgeTrailingCw.VtxEnd }; // The poly at infinity is to the left of the edge, the infinite poly is to its right // // Start and end vertices are the trailing and leading infinite edges // Add the edge at infinity to the poly at infinity and the current infinite poly polyAtInfinity.AddEdge(edgeAtInfinity); poly.FortuneEdges.Insert(iTrailingEdgeCw, edgeAtInfinity); // Set up the wings of the wingedEdge edgeAtInfinity.EdgeCWPredecessor = edgeLeadingCw; edgeAtInfinity.EdgeCCWSuccessor = edgeTrailingCw; edgeLeadingCw.EdgeCCWSuccessor = edgeAtInfinity; edgeTrailingCw.EdgeCWSuccessor = edgeAtInfinity; // If we've got a previous edge at infinity if (edgePreviousAtInfinity != null) { // Incorporate it properly edgePreviousAtInfinity.EdgeCCWPredecessor = edgeAtInfinity; edgeAtInfinity.EdgeCWSuccessor = edgePreviousAtInfinity; } // Hook up our edge at infinity to our vertices at infinity HookEdgeAtInfinityToVerticesAtInfinity( edgeAtInfinity, edgeAtInfinity.VtxStart as FortuneVertex, edgeAtInfinity.VtxEnd as FortuneVertex); // Locate the leading edge index in the next polygon // For each Edge of the infinite polygon to our left // ReSharper disable once PossibleNullReferenceException for (iLeadingEdgeNext = 0; iLeadingEdgeNext < polyNextCcw.VertexCount; iLeadingEdgeNext++) { // If it's the same as our leading CW infinite edge if (polyNextCcw.FortuneEdges[iLeadingEdgeNext] == edgeLeadingCw) { // then their leading edge is the one immediately preceding it in CW order iLeadingEdgeNext = (polyNextCcw.VertexCount + iLeadingEdgeNext - 1) % polyNextCcw.VertexCount; break; } } // Return the edge at infinity we've created return(edgeAtInfinity); }