예제 #1
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
예제 #2
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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;
        }
예제 #3
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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();
        }
예제 #4
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
예제 #5
0
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 /// <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;
     }
 }
예제 #6
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
예제 #7
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
예제 #8
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
            }
        }
예제 #9
0
 internal InternalNode(FortunePoly polyLeft, FortunePoly polyRight)
 {
     DHtLeftMinusRight = 0;
     PolyLeft          = polyLeft;
     PolyRight         = polyRight;
 }
예제 #10
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
예제 #11
0
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 /// <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;
 }
예제 #12
0
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 /// <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);
 }
예제 #13
0
        private CircleEvent _cevt;         // Circle event which will snuff this parabola out
        #endregion

        #region Constructor
        internal LeafNode(FortunePoly poly)
        {
            LeftAdjacentLeaf  = null;
            RightAdjacentLeaf = null;
            Poly = poly;
        }
예제 #14
0
 internal SiteEvent(FortunePoly poly) : base(poly.VoronoiPoint)
 {
     Poly = poly;
 }