//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary>Return the Winged edge structure for the voronoi diagram.</summary> /// <remarks> /// Return the Winged edge structure for the voronoi diagram. This is a complicated procedure /// with a lot of details to be taken care of as follows: /// <list type="bullet"> /// <item> /// Every polygon has to have its edges sorted in clockwise order. /// </item> /// <item> /// Set all the successor edges to each edge and the list of edges to each vertex. /// </item> /// <item> /// Zero length edges are eliminated and their "endpoints" are consolidated. /// </item> /// <item> /// The polygon at infinity and all the edges at infinity must be added /// </item> /// </list> /// <para> /// If the callers only want a list of undifferentiated polygons, they can call Voronoi and /// get such a list. This is fine for drawing and other things, but doesn't give any kind of /// relationship between elements in diagram. If that is desired, this routine can be called /// to get a much richer Winged Edge data structure for the diagram. It is a relatively expensive /// thing to compute which is why it's made separate from the raw voronoi diagram calculations. /// </para> /// <para> /// NOTE ON THE POLYGON AT INFINITY: In the WingedEdge structure each polygon has a list of edges /// with another polygon on the other side of each edge. This poses a bit of a difficulty for /// the polygons in a Voronoi diagram whose edges are rays to infinity. We'll call these /// "infinite polygons" for convenience. The solution we use in our WingedEdge data structure is /// to produce a single "polygon at infinity" (not to be confused with the infinite polygons /// previously mentioned) and a series of edges at infinity which "separate" the infinite /// polygons from the polygon at infinity. These edges have to be ordered in the same order as /// the infinite polygons around the border. All of this is done in AddEdgeAtInfinity(). /// </para> /// <para> /// In order to set up the polygon at infinity we have to start with an infinite polygon and /// then work our way around the exterior of the diagram, moving from one infinite polygon to /// another and adding them to the list of polygons "adjacent" to the polygon at infinity. We /// keep track of the first infinite polygon we find to use it as the starting polygon in that /// process. /// </para> /// Darrellp, 2/17/2011. /// </remarks> /// <returns> The winged edge structure for the diagram. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// private WE BuildWingedEdge() { // Initialize // The first infinite polygon we locate and the index for the infinite // edge ccw to that polygon FortunePoly polyInfinityStart = null; var iLeadingInfiniteEdgeCw = -1; var we = new WE(); // for all polygons in the voronoi diagram foreach (var poly in Polygons) { // Process the polygon's edges ProcessPolygonEdges(poly, we, ref polyInfinityStart, ref iLeadingInfiniteEdgeCw); } // Take care of a corner case with zero length edges in the starting infinite polygon // // In odd cases, if the starting infinite polygon has zero length edges, then when they were dropped // in HandleZeroLengthEdges, our indices changed so that iLeadingInfiniteEdgeCw might be wrong // now. In that corner case, we have to just do a linear search once again to recalculate // iLeadingInfiniteEdgeCw correctly. if (polyInfinityStart != null && polyInfinityStart.FZeroLengthEdge) { // Recalc the index iLeadingInfiniteEdgeCw = RecalcLeadingInfiniteEdge(polyInfinityStart); } // Create the polygon at infinity AddPolygonAtInfinity(we, polyInfinityStart, iLeadingInfiniteEdgeCw); // Return the final winged edge return(we); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <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> Process a polygon's edges. </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="poly"> Polygon to be processed. </param> /// <param name="we"> WingedEdge structure we'll add the polygon to. </param> /// <param name="polyInfinityStart"> /// [in,out] The current infinite polygon or null if none /// yet found. /// </param> /// <param name="iLeadingInfiniteEdgeCw"> /// [in,out] The CW leading infinite edge if a new /// infinite polygon has been found. /// </param> //////////////////////////////////////////////////////////////////////////////////////////////////// private static void ProcessPolygonEdges( FortunePoly poly, WE we, ref FortunePoly polyInfinityStart, ref int iLeadingInfiniteEdgeCw) { // Add the poly to the winged edge struct and sort it's edges if (poly.FortuneEdges != null && poly.FortuneEdges.Count > 0) { poly.FirstEdge = poly.FortuneEdges[0]; } we.AddPoly(poly); poly.SortEdges(); // For each edge in the polygon for (var iEdge = 0; iEdge < poly.VertexCount; iEdge++) { // Get the edge following the current edge in clockwise order var edge = poly.FortuneEdges[iEdge]; var iEdgeNext = (iEdge + 1) % poly.VertexCount; var edgeNextCW = poly.FortuneEdges[iEdgeNext]; // Incorporate the edge into the winged edge // // Set up it's CW and CCW successor and predecessor, etc. edge.HookToWingedEdge(poly, we); // If this is an infinite polygon and we've not located any infinite polygons before if (polyInfinityStart == null && edge.VtxEnd.FAtInfinity && edgeNextCW.VtxEnd.FAtInfinity) { // Keep track of the first infinite polygon we've seen // // We define the "leading edge" as the one to the left when looking outward. We have to // explicitly locate this first one - after that, we can locate the leading edge // for the next polygon to the right as the edge which follows the current poly's // leading edge. As we work our way around the outside in AddPolygonAtInfinity we can // therefore easily set leading edges on successive polygons. It should be noted that // after AddPolygonAtInfinity there will be an edge at infinite between these two // infinite edges to maintain proper Winged Edge structure. iLeadingInfiniteEdgeCw = iEdge; polyInfinityStart = poly; } } // Remove any zero length edges in the polygon // // We have to put this after we've processed all the edges since that's where we determine any // zero length edges. This is not as good as I'd like it because it means that this may, on rare // occasions, jigger the indices for the poly which in turn means that the iLeadingInfiniteEdgeCw // value may be off and we have to check for that later. It's ugly code, but rarely occurs in // practice so isn't a performance hit. poly.HandleZeroLengthEdges(); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Each polygon in the final solution is associated with a point in the input. We initialize /// that polygon for this point here. /// </summary> /// <remarks> Darrellp, 2/17/2011. </remarks> /// <param name="pt"> The point. </param> /// <param name="index"> Identifying index for this point/poly</param> /// <returns> The polygon produced. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// private FortunePoly InsertPoly(Vector pt, int index) { // The count is being passed in only as a unique identifier for this point. var poly = new FortunePoly(pt, Polygons.Count) { Cookie = index }; Polygons.Add(poly); return(poly); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Place the poly on the proper side of this edge. We use the generator of the poly to locate /// it properly WRT this edge. /// </summary> /// <remarks> Darrellp, 2/18/2011. </remarks> /// <param name="poly"> Polygon to locate. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void SetOrderedPoly(FortunePoly poly) { if (FLeftOf(poly.VoronoiPoint)) { PolyLeft = poly; } else { PolyRight = poly; } }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Recalc leading infinite edge. </summary> /// <remarks> /// This is a lot of code for a very corner case. If an initial infinite polygon has some of /// it's edges removed because they were zero length then a previously calculated index for the /// leading infinite edge will be wrong and we have to recalculate it which is the purpose of /// this little routine. Darrellp, 2/18/2011. /// </remarks> /// <param name="polyInfinityStart"> The starting infinite polygon. </param> /// <returns> The newly calculated index for the leading edge. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// private static int RecalcLeadingInfiniteEdge(FortunePoly polyInfinityStart) { // Initialize our return index to an illegal value var iLeadingInfiniteEdgeCw = -1; // If this is a fully infinite line // // If there are only two edges, they'll both be at infinity and we can't just use their // indices to distinguish - we have to actually check the angles to see which one is // "leading". if (polyInfinityStart.VertexCount == 2) { // Diagnostics // If we run clockwise from the origin through edge 0 through edge 1 // ReSharper disable ConvertIfStatementToConditionalTernaryExpression if (ICcw(new Vector(0, 0), polyInfinityStart.FortuneEdges[0].VtxEnd.Pt, polyInfinityStart.FortuneEdges[1].VtxEnd.Pt) > 0) // ReSharper restore ConvertIfStatementToConditionalTernaryExpression { // Edge 1 is our leading edge iLeadingInfiniteEdgeCw = 1; } else { // Edge 0 is our leading edge iLeadingInfiniteEdgeCw = 0; } } else { // Find the leading edge which goes to infinity for (var iEdge = 0; iEdge < polyInfinityStart.VertexCount; iEdge++) { // Retrieve the edge and the next edge in CW order var edge = polyInfinityStart.FortuneEdges[iEdge]; var iEdgeNext = (iEdge + 1) % polyInfinityStart.VertexCount; var edgeNextCW = polyInfinityStart.FortuneEdges[iEdgeNext]; // If this edge and the next both end at infinity if (edge.VtxEnd.FAtInfinity && edgeNextCW.VtxEnd.FAtInfinity) { // Then this is our leading CW edge to infinity // // This means that this edge is to the left as we look "out" from the // interior of the voronoi diagram. iLeadingInfiniteEdgeCw = iEdge; break; } } } return(iLeadingInfiniteEdgeCw); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Creates a circle event. </summary> /// <remarks>Circle events are created at the circumcenters of three sites - the sites for poly1/2/3.</remarks> /// <param name="poly1"> The first polygon. </param> /// <param name="poly2"> The second polygon. </param> /// <param name="poly3"> The third polygon. </param> /// <param name="yScanLine"> The y coordinate scan line. </param> /// <returns> A new circle event. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// internal static CircleEvent CreateCircleEvent(FortunePoly poly1, FortunePoly poly2, FortunePoly poly3, double yScanLine) { // Locals CircleEvent cevtRet = null; // Determine a circumcenter for the sites of poly1/2/3. if (Geometry2D.FFindCircumcenter(poly1.VoronoiPoint, poly2.VoronoiPoint, poly3.VoronoiPoint, out var ptCenter)) { // Determine y coordinate for the side of the circle // The event will fire when the scan line hits that y position var radius = Geometry2D.Distance(poly1.VoronoiPoint, ptCenter); ptCenter.Y -= radius; // If the circumcenter is above the scan line we've already passed it by, so don't put it in the queue if (ptCenter.Y <= yScanLine) { cevtRet = new CircleEvent(ptCenter, radius); } } return(cevtRet); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Add a poly to the edge and the edge to the winged edge data structure. </summary> /// <remarks> /// This is where we set up the CW and CCW successor and predecessor edges, the polygons on each /// side of the edge, it's start and end vertices. Also, the edge is added to to start and end /// vertices' edge list and it's actually added to the winged edge structure itself /// Darrellp, 2/18/2011. /// </remarks> /// <param name="poly"> Polygon to add. </param> /// <param name="we"> Winged edge structure to add edge to. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void HookToWingedEdge(FortunePoly poly, WE we) { // Put the poly properly to the left or right of this edge SetOrderedPoly(poly); // Avoid adding the edge twice // // We'll attempt to add this edge twice, once from each polygon on each side of // it. We only want to add it one of those times. if (!_fAddedToWingedEdge) { // Set up the successor edges SetSuccessorEdges(); we.AddEdge(this); _fAddedToWingedEdge = true; // Set up the start and end vertex VtxStart.FirstEdge = this; VtxEnd.FirstEdge = this; ((FortuneVertex)VtxStart).AddToWingedEdge(we); ((FortuneVertex)VtxEnd).AddToWingedEdge(we); } }
internal InternalNode(FortunePoly polyLeft, FortunePoly polyRight) { DHtLeftMinusRight = 0; PolyLeft = polyLeft; PolyRight = polyRight; }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <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); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Set up the polygons which surround this edge. </summary> /// <remarks> /// During the sweepline processing we don't necessarily know where the final vertex for an edge /// will be before we know the polygons on each side of the edge so we can't actually determine /// which side of the edge the polygons will lie on. Consequently, we have to just keep them /// handy until we finally get our second point. /// Darrellp, 2/19/2011. /// </remarks> /// <param name="poly1"> First poly. </param> /// <param name="poly2"> Second poly. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// internal void SetPolys(FortunePoly poly1, FortunePoly poly2) { _arPoly[0] = poly1; _arPoly[1] = poly2; }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Returns the polygon on the other side of this edge. </summary> /// <remarks> Darrellp, 2/19/2011. </remarks> /// <param name="polyThis"> The polygon on "this" side. </param> /// <returns> The polygon on the "other" side. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// internal FortunePoly OtherPoly(FortunePoly polyThis) { return(Poly1 == polyThis ? Poly2 : Poly1); }
private CircleEvent _cevt; // Circle event which will snuff this parabola out #endregion #region Constructor internal LeafNode(FortunePoly poly) { LeftAdjacentLeaf = null; RightAdjacentLeaf = null; Poly = poly; }
internal SiteEvent(FortunePoly poly) : base(poly.VoronoiPoint) { Poly = poly; }