コード例 #1
0
        internal void AddBeachSection(FortuneSiteEvent siteEvent, MinHeap <FortuneEvent> eventQueue, HashSet <FortuneCircleEvent> deleted, LinkedList <VoronoiEdge> edges)
        {
            var site      = siteEvent.Site;
            var x         = site.X;
            var directrix = site.Y;

            RBTreeNode <BeachSection> leftSection  = null;
            RBTreeNode <BeachSection> rightSection = null;
            var node = beachLine.Root;

            //find the parabola(s) above this site
            while (node != null && leftSection == null && rightSection == null)
            {
                var distanceLeft = LeftBreakpoint(node, directrix) - x;
                if (distanceLeft > 0)
                {
                    //the new site is before the left breakpoint
                    if (node.Left == null)
                    {
                        rightSection = node;
                    }
                    else
                    {
                        node = node.Left;
                    }
                    continue;
                }

                var distanceRight = x - RightBreakpoint(node, directrix);
                if (distanceRight > 0)
                {
                    //the new site is after the right breakpoint
                    if (node.Right == null)
                    {
                        leftSection = node;
                    }
                    else
                    {
                        node = node.Right;
                    }
                    continue;
                }

                //the point lies below the left breakpoint
                if (distanceLeft.ApproxEqual(0))
                {
                    leftSection  = node.Previous;
                    rightSection = node;
                    continue;
                }

                //the point lies below the right breakpoint
                if (distanceRight.ApproxEqual(0))
                {
                    leftSection  = node;
                    rightSection = node.Next;
                    continue;
                }

                // distance Right < 0 and distance Left < 0
                // this section is above the new site
                leftSection = rightSection = node;
            }

            //our goal is to insert the new node between the
            //left and right sections
            var section = new BeachSection(site);

            //left section could be null, in which case this node is the first
            //in the tree
            var newSection = beachLine.InsertSuccessor(leftSection, section);

            //new beach section is the first beach section to be added
            if (leftSection == null && rightSection == null)
            {
                return;
            }

            //main case:
            //if both left section and right section point to the same valid arc
            //we need to split the arc into a left arc and a right arc with our
            //new arc sitting in the middle
            if (leftSection != null && leftSection == rightSection)
            {
                //if the arc has a circle event, it was a false alarm.
                //remove it
                if (leftSection.Data.CircleEvent != null)
                {
                    deleted.Add(leftSection.Data.CircleEvent);
                    leftSection.Data.CircleEvent = null;
                }

                //we leave the existing arc as the left section in the tree
                //however we need to insert the right section defined by the arc
                var copy = new BeachSection(leftSection.Data.Site);
                rightSection = beachLine.InsertSuccessor(newSection, copy);

                //grab the projection of this site onto the parabola
                var y            = ParabolaMath.EvalParabola(leftSection.Data.Site.X, leftSection.Data.Site.Y, directrix, x);
                var intersection = new Point(x, y);

                //create the two half edges corresponding to this intersection
                var leftEdge  = new VoronoiEdge(intersection, site, leftSection.Data.Site);
                var rightEdge = new VoronoiEdge(intersection, leftSection.Data.Site, site);
                leftEdge.Neighbor = rightEdge;

                //put the edge in the list
                edges.AddFirst(leftEdge);

                //store the left edge on each arc section
                newSection.Data.Edge   = leftEdge;
                rightSection.Data.Edge = rightEdge;

                //store neighbors for delaunay
                leftSection.Data.Site.Neighbours.Add(newSection.Data.Site);
                newSection.Data.Site.Neighbours.Add(leftSection.Data.Site);

                //create circle events
                CheckCircle(leftSection, eventQueue);
                CheckCircle(rightSection, eventQueue);
            }

            //site is the last beach section on the beach line
            //this can only happen if all previous sites
            //had the same y value
            else if (leftSection != null && rightSection == null)
            {
                var start   = new Point((leftSection.Data.Site.X + site.X) / 2, double.MinValue);
                var infEdge = new VoronoiEdge(start, leftSection.Data.Site, site);
                var newEdge = new VoronoiEdge(start, site, leftSection.Data.Site);

                newEdge.Neighbor = infEdge;
                edges.AddFirst(newEdge);

                leftSection.Data.Site.Neighbours.Add(newSection.Data.Site);
                newSection.Data.Site.Neighbours.Add(leftSection.Data.Site);

                newSection.Data.Edge = newEdge;

                //cant check circles since they are colinear
            }

            //site is directly above a break point
            else if (leftSection != null && leftSection != rightSection)
            {
                //remove false alarms
                if (leftSection.Data.CircleEvent != null)
                {
                    deleted.Add(leftSection.Data.CircleEvent);
                    leftSection.Data.CircleEvent = null;
                }

                if (rightSection.Data.CircleEvent != null)
                {
                    deleted.Add(rightSection.Data.CircleEvent);
                    rightSection.Data.CircleEvent = null;
                }

                //the breakpoint will dissapear if we add this site
                //which means we will create an edge
                //we treat this very similar to a circle event since
                //an edge is finishing at the center of the circle
                //created by circumscribing the left center and right
                //sites

                //bring a to the origin
                var leftSite = leftSection.Data.Site;
                var ax       = leftSite.X;
                var ay       = leftSite.Y;
                var bx       = site.X - ax;
                var by       = site.Y - ay;

                var rightSite  = rightSection.Data.Site;
                var cx         = rightSite.X - ax;
                var cy         = rightSite.Y - ay;
                var d          = bx * cy - by * cx;
                var magnitudeB = bx * bx + by * by;
                var magnitudeC = cx * cx + cy * cy;
                var vertex     = new Point(
                    (cy * magnitudeB - by * magnitudeC) / (2 * d) + ax,
                    (bx * magnitudeC - cx * magnitudeB) / (2 * d) + ay);

                rightSection.Data.Edge.EndPoint = vertex;

                //next we create a two new edges
                newSection.Data.Edge   = new VoronoiEdge(vertex, site, leftSection.Data.Site);
                rightSection.Data.Edge = new VoronoiEdge(vertex, rightSection.Data.Site, site);

                edges.AddFirst(newSection.Data.Edge);
                edges.AddFirst(rightSection.Data.Edge);

                //add neighbors for delaunay
                newSection.Data.Site.Neighbours.Add(leftSection.Data.Site);
                leftSection.Data.Site.Neighbours.Add(newSection.Data.Site);

                newSection.Data.Site.Neighbours.Add(rightSection.Data.Site);
                rightSection.Data.Site.Neighbours.Add(newSection.Data.Site);

                CheckCircle(leftSection, eventQueue);
                CheckCircle(rightSection, eventQueue);
            }
        }
コード例 #2
0
    public void AddSection(FortuneSiteEvent siteEvent, MinHeap <FortuneEvent> eventQueue, HashSet <FortuneCircleEvent> deleted, LinkedList <Edge> edges)
    {
        FortuneSite site      = siteEvent.site;
        double      x         = site.x;
        double      directrix = site.y;

        RedBlackTreeNode <BeachSection> leftSection  = null;
        RedBlackTreeNode <BeachSection> rightSection = null;
        RedBlackTreeNode <BeachSection> root         = sections.root;

        while (root != null && leftSection != null && rightSection != null)
        {
            double distanceLeft = LeftBreakpoint(root, directrix) - x;
            if (distanceLeft > 0)
            {
                if (root.left == null)
                {
                    rightSection = root;
                }
                else
                {
                    root = root.left;
                }

                continue;
            }

            double distanceRight = x - RightBreakpoint(root, directrix);
            if (distanceRight > 0)
            {
                if (root.right == null)
                {
                    leftSection = root;
                }
                root = root.right;

                continue;
            }

            if (distanceLeft.ApproxEqual(0))
            {
                leftSection  = root.previous;
                rightSection = root;
                continue;
            }

            if (distanceRight.ApproxEqual(0))
            {
                leftSection  = root;
                rightSection = root.next;
                continue;
            }

            leftSection = rightSection = root;
        }

        BeachSection section = new BeachSection(site);
        RedBlackTreeNode <BeachSection> newSectionNode = sections.Insert(leftSection, section);

        if (leftSection == null && rightSection == null)
        {
            return;
        }

        if (leftSection != null && leftSection == rightSection)
        {
            if (leftSection.data.circleEvent != null)
            {
                deleted.Add(leftSection.data.circleEvent);
                leftSection.data.circleEvent = null;
            }

            BeachSection copy = new BeachSection(leftSection.data.site);
            rightSection = sections.Insert(newSectionNode, copy);

            double y            = ParabolaMath.EvalParabola(leftSection.data.site.x, leftSection.data.site.y, directrix, x);
            Point  intersection = new Point(x, y);

            Edge leftEdge  = new Edge(intersection, site, leftSection.data.site);
            Edge rightEdge = new Edge(intersection, leftSection.data.site, site);
            leftEdge.neighbour = rightEdge;

            edges.AddFirst(leftEdge);

            newSectionNode.data.edge = leftEdge;
            rightSection.data.edge   = rightEdge;

            leftSection.data.site.neighbours.Add(newSectionNode.data.site);
            newSectionNode.data.site.neighbours.Add(leftSection.data.site);

            CheckCircle(leftSection, eventQueue);
            CheckCircle(rightSection, eventQueue);
        }
        else if (leftSection != null && rightSection != null)
        {
            Point start        = new Point((leftSection.data.site.x + site.x) / 2, double.MinValue);
            Edge  infiniteEdge = new Edge(start, leftSection.data.site, site);
            Edge  newEdge      = new Edge(start, site, leftSection.data.site);

            newEdge.neighbour = infiniteEdge;
            edges.AddFirst(newEdge);

            leftSection.data.site.neighbours.Add(newSectionNode.data.site);
            newSectionNode.data.site.neighbours.Add(leftSection.data.site);

            newSectionNode.data.edge = newEdge;
        }
        else if (leftSection != null && leftSection != rightSection)
        {
            if (leftSection.data.circleEvent != null)
            {
                deleted.Add(leftSection.data.circleEvent);
                leftSection.data.circleEvent = null;
            }

            if (rightSection.data.circleEvent != null)
            {
                deleted.Add(rightSection.data.circleEvent);
                rightSection.data.circleEvent = null;
            }

            FortuneSite leftSite = leftSection.data.site;
            double      aX       = leftSite.x;
            double      aY       = leftSite.y;
            double      bX       = site.x - aX;
            double      bY       = site.y - aY;

            FortuneSite rightSite  = rightSection.data.site;
            double      cX         = rightSite.x - aX;
            double      cY         = rightSite.y - aY;
            double      delta      = bX * cY - bY * cX;
            double      magnitudeB = bX * bX + bY * bY;
            double      magnitudeC = cX * cX + cY * cY;

            Point vertex = new Point(
                (cY * magnitudeB - bY * magnitudeC) / (2 * delta) + aX,
                (bX * magnitudeC - cX * magnitudeB) / (2 * delta) + aY
                );

            rightSection.data.edge.end = vertex;

            newSectionNode.data.edge = new Edge(vertex, site, leftSection.data.site);
            rightSection.data.edge   = new Edge(vertex, rightSection.data.site, site);

            edges.AddFirst(newSectionNode.data.edge);
            edges.AddFirst(rightSection.data.edge);

            newSectionNode.data.site.neighbours.Add(leftSection.data.site);
            leftSection.data.site.neighbours.Add(newSectionNode.data.site);

            newSectionNode.data.site.neighbours.Add(rightSection.data.site);
            rightSection.data.site.neighbours.Add(newSectionNode.data.site);

            CheckCircle(leftSection, eventQueue);
            CheckCircle(rightSection, eventQueue);
        }
    }