Example #1
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
Example #2
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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);
        }
Example #3
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <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;
            }
        }
Example #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);
        }