Beispiel #1
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>	Adds a circle event. </summary>
        /// <remarks>	Darrellp, 2/21/2011. </remarks>
        /// <param name="cevt">	The event to add. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        internal void AddCircleEvent(CircleEvent cevt)
        {
            // Add to the priority queue
            Add(cevt);

            // We also have to add them to our linked list of circle events
            CircleEvents.AddFirst(cevt);

            // Keeping track of their LinkedListNode allows us to delete them efficiently later
            cevt.LinkedListNode = CircleEvents.First;
        }
Beispiel #2
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>	Remove the circle event which snuffs this node's parabola from the event queue. </summary>
        /// <remarks>	Darrellp, 2/18/2011. </remarks>
        /// <param name="evq">	Event queue. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        internal void DeleteAssociatedCircleEvent(EventQueue evq)
        {
            // If we've got a circle event
            if (_cevt != null)
            {
                // Delete it
                evq.Delete(_cevt);
                if (_cevt.LinkedListNode != null)
                {
                    evq.CircleEvents.Remove(_cevt.LinkedListNode);
                }

                _cevt = null;
            }
        }
Beispiel #3
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);
        }
Beispiel #4
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
Beispiel #5
0
        /// <summary>
        ///     Remove events from the queue and handle them one at a time.
        /// </summary>
        private void ProcessEvents()
        {
            // Create an impossible fictional "previous event"
            FortuneEvent evtPrev = new CircleEvent(new Vector(Single.MaxValue, Single.MaxValue), 0);

            // While there are events in the queue
            while (QevEvents.Count > 0)
            {
                // Get the next event
                //
                // Events are pulled off in top to bottom, left to right, site event before
                // circle event order.  This ensures that events/sites at identical locations
                // will be pulled off one after the other which allows us to cull them.
                var evt = QevEvents.Pop();

                // If we've got a pair of identically placed events
                if (FCloseEnough(evt.Pt, evtPrev.Pt))
                {
                    // Locals
                    var tpPrev = evtPrev.GetType();
                    var tpCur  = evt.GetType();

                    // If the previous event was a site event
                    if (tpPrev == typeof(SiteEvent))
                    {
                        // And the current one is also
                        if (tpCur == typeof(SiteEvent))
                        {
                            // Skip identical site events.
                            //
                            // This handles cases where the same point is in the input data two or more times.
                            continue;
                        }
                    }
                    // Else if it's a circle event
                    else if (tpCur == typeof(CircleEvent))
                    {
                        // Locals
                        var cevt     = evt as CircleEvent;
                        var cevtPrev = evtPrev as CircleEvent;

                        // If we have identically placed circle events
                        //
                        // Identically placed circle events still have to be processed but we handle the
                        // case specially.  The implication of identically placed circle events is that
                        // we had four or more cocircular points which implies that the polygons
                        // for those points come together to a point.  Since we only allow for vertices
                        // of order three during voronoi processing, we create "zero length" edges for the
                        // polygons which meet at that common point. These will be removed later in postprocessing.
                        //
                        // ReSharper disable PossibleNullReferenceException
                        if (FCloseEnough(cevt.VoronoiVertex, cevtPrev.VoronoiVertex))
                        {
                            // We're going to create a zero length edge.
                            cevt.FZeroLength = true;
                        }
                        // ReSharper restore PossibleNullReferenceException
                    }
                }

                // Handle the event
                evt.Handle(this);
                evtPrev = evt;
            }
        }
Beispiel #6
0
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 /// <summary>	Set the circle event which snuff's this node's parabola. </summary>
 /// <remarks>	Darrellp, 2/18/2011. </remarks>
 /// <param name="cevt">	The cevt. </param>
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 internal void SetCircleEvent(CircleEvent cevt)
 {
     _cevt = cevt;
 }