예제 #1
0
        public void Reset()
        {
            if (!this.beachLine)
            {
                this.beachLine = new RBTree <BeachSection>();
            }

            // Move leftover beachsections to the beachsection junkyard.
            if (this.beachLine.Root)
            {
                BeachSection beachSection = this.beachLine.GetFirst(this.beachLine.Root);
                while (beachSection)
                {
                    this.beachSectionJunkyard.Add(beachSection);
                    beachSection = beachSection.Next;
                }
            }
            this.beachLine.Root = null;

            if (!this.circleEvents)
            {
                this.circleEvents = new RBTree <CircleEvent>();
            }
            this.circleEvents.Root = this.firstCircleEvent = null;

            this.edges = new List <Edge>();
            this.cells = new List <Cell>();
        }
예제 #2
0
        private void ProcessCircleEvent(CircleEvent circleEvent)
        {
            var leftHalfEdge = circleEvent.Left.PartialLine;

            if (leftHalfEdge.Face.Equals(circleEvent.Left.Point))
            {
                leftHalfEdge = leftHalfEdge.Twin;
            }

            var centerHalfEdge = circleEvent.Center.PartialLine;

            if (centerHalfEdge.Face.Equals(circleEvent.Right.Point))
            {
                centerHalfEdge = centerHalfEdge.Twin;
            }

            var rc = centerHalfEdge.Twin;

            rc.CircleEvent           = circleEvent;
            leftHalfEdge.CircleEvent = circleEvent;

            circleEvent.PartialLine = centerHalfEdge;

            var prev = circleEvent.CenterLeaf.GetPreviousLeaf();
            var next = circleEvent.CenterLeaf.GetNextLeaf();

            if (prev?.DisappearEvent != null)
            {
                PriorityQueue.Delete(prev.DisappearEvent.Handle);
                prev.DisappearEvent = null;
            }

            if (next?.DisappearEvent != null)
            {
                PriorityQueue.Delete(next.DisappearEvent.Handle);
                next.DisappearEvent = null;
            }

            var newCircles = Events.RemoveNode(circleEvent, prev, circleEvent.CenterLeaf, next);

            if (newCircles == null)
            {
                return;
            }

            foreach (var ce in newCircles)
            {
                C5.IPriorityQueueHandle <IEvent> h = null;
                PriorityQueue.Add(ref h, ce);
                ce.Handle = h;
            }
        }
예제 #3
0
        public void DetachCircleEvent(BeachSection arc)
        {
            CircleEvent circle = arc.circleEvent;

            if (circle)
            {
                if (!circle.Prev)
                {
                    this.firstCircleEvent = circle.Next;
                }

                this.circleEvents.Remove(circle); // remove from RB-tree
                this.circleEventJunkyard.Add(circle);
                arc.circleEvent = null;
            }
        }
예제 #4
0
        public VoronoiGraph Compute(List <Point> sites, BoundingRect bbox)
        {
            this.Reset();

            // Initialize site event queue
            var siteEvents = sites.Take(sites.Count).ToList();

            /*siteEvents.Sort((a, b) =>
             *  {
             *      float r = b.y - a.y;
             *      if (r != 0) { return Mathf.CeilToInt(r); }
             *      return Mathf.CeilToInt(b.x - a.x);
             *  });*/
            siteEvents = siteEvents.OrderByDescending(s => s.y).ToList();

            // process queue
            Point site = siteEvents.Last();

            siteEvents.Remove(site);
            int   siteid = 0;
            float xsitex = -Mathf.Infinity;
            float xsitey = -Mathf.Infinity;

            // main loop
            for (; ;)
            {
                // we need to figure whether we handle a site or circle event
                // for this we find out if there is a site event and it is
                // 'earlier' than the circle event
                CircleEvent circle = this.firstCircleEvent;

                // add beach section
                if (site && (!circle || site.y < circle.y || (site.y == circle.y && site.x < circle.x)))
                {
                    // only if site is not a duplicate
                    if (site.x != xsitex || site.y != xsitey)
                    {
                        // first create cell for new site
                        //this.cells[siteid] = new Cell(site);
                        this.cells.Insert(siteid, new Cell(site));
                        site.id = siteid++;

                        // then create a beachsection for that site
                        this.AddBeachSection(site);

                        // remember last site coords to detect duplicate
                        xsitey = site.y;
                        xsitex = site.x;
                    }

                    site = siteEvents.Count > 0 ? siteEvents.Last() : null;
                    if (siteEvents.Count > 0)
                    {
                        siteEvents.Remove(site);
                    }
                }
                // remove beach section
                else if (circle)
                {
                    this.RemoveBeachSection(circle.arc);
                }
                // all done, quit
                else
                {
                    break;
                }
            }

            // wrapping-up
            this.ClipEdges(bbox);

            this.CloseCells(bbox);

            VoronoiGraph graph = new VoronoiGraph();

            graph.sites = sites;
            graph.cells = this.cells;
            graph.edges = this.edges;

            this.Reset();

            return(graph);
        }
예제 #5
0
        public void AttachCircleEvent(BeachSection arc)
        {
            BeachSection lArc = arc.Prev;
            BeachSection rArc = arc.Next;

            if (!lArc || !rArc)
            {
                return;
            } // does that ever happen?

            Point lSite = lArc.site;
            Point cSite = arc.site;
            Point rSite = rArc.site;

            // If site of Left beachsection is same as site of
            // Right beachsection, there can't be convergence
            if (lSite == rSite)
            {
                return;
            }

            // Find the circumscribed circle for the three sites associated
            // with the beachsection triplet.
            // rhill 2011-05-26: It is more efficient to calculate in-place
            // rather than getting the resulting circumscribed circle from an
            // object returned by calling Voronoi.circumcircle()
            // http://mathforum.org/library/drmath/view/55002.html
            // Except that I bring the origin at cSite to simplify calculations.
            // The bottom-most part of the circumcircle is our Fortune 'circle
            // event', and its center is a vertex potentially part of the final
            // Voronoi diagram.
            float bx = cSite.x;
            float by = cSite.y;
            float ax = lSite.x - bx;
            float ay = lSite.y - by;
            float cx = rSite.x - bx;
            float cy = rSite.y - by;

            // If points l.c.r are clockwise, then center beach section does not
            // collapse, hence it can't end up as a vertex (we reuse 'd' here, which
            // sign is reverse of the orientation, hence we reverse the test.
            // http://en.wikipedia.org/wiki/Curveorientation#Orientationofasimplepolygon
            // rhill 2011-05-21: Nasty finite precision error which caused circumcircle() to
            // return infinites: 1e-12 seems to fix the problem.
            float d = 2 * (ax * cy - ay * cx);

            if (d >= -2e-12)
            {
                return;
            }

            float ha      = ax * ax + ay * ay;
            float hc      = cx * cx + cy * cy;
            float x       = (cy * ha - ay * hc) / d;
            float y       = (ax * hc - cx * ha) / d;
            float ycenter = y + by;

            // Important: ybottom should always be under or at sweep, so no need
            // to waste CPU cycles by checking

            // recycle circle event object if possible
            CircleEvent circleEvent = this.circleEventJunkyard.Count > 0 ? this.circleEventJunkyard.Last() : null;

            this.circleEventJunkyard.Remove(circleEvent);
            if (!circleEvent)
            {
                circleEvent = new CircleEvent();
            }

            circleEvent.arc     = arc;
            circleEvent.site    = cSite;
            circleEvent.x       = x + bx;
            circleEvent.y       = ycenter + Mathf.Sqrt(x * x + y * y); // y bottom
            circleEvent.yCenter = ycenter;
            arc.circleEvent     = circleEvent;

            // find insertion point in RB-tree: circle events are ordered from
            // smallest to largest
            CircleEvent predecessor = null;
            CircleEvent node        = this.circleEvents.Root;

            while (node)
            {
                if (circleEvent.y < node.y || (circleEvent.y == node.y && circleEvent.x <= node.x))
                {
                    if (node.Left)
                    {
                        node = node.Left;
                    }
                    else
                    {
                        predecessor = node.Prev;
                        break;
                    }
                }
                else
                {
                    if (node.Right)
                    {
                        node = node.Right;
                    }
                    else
                    {
                        predecessor = node;
                        break;
                    }
                }
            }

            this.circleEvents.Insert(predecessor, circleEvent);
            if (!predecessor)
            {
                this.firstCircleEvent = circleEvent;
            }
        }
예제 #6
0
        public void RemoveBeachSection(BeachSection beachSection)
        {
            CircleEvent circle = beachSection.circleEvent;
            float       x      = circle.x;
            float       y      = circle.yCenter;
            Point       vertex = new Point(x, y);

            BeachSection previous = beachSection.Prev;
            BeachSection next     = beachSection.Next;

            LinkedList <BeachSection> disappearingTransitions = new LinkedList <BeachSection>();

            disappearingTransitions.AddLast(beachSection);

            // remove collapsed beachsection from beachline
            this.DetachBeachSection(beachSection);

            // there could be more than one empty arc at the deletion point, this
            // happens when more than two edges are linked by the same vertex,
            // so we will collect all those edges by looking up both sides of
            // the deletion point.
            // by the way, there is *always* a predecessor/successor to any collapsed
            // beach section, it's just impossible to have a collapsing first/last
            // beach sections on the beachline, since they obviously are unconstrained
            // on their left/right side.

            // look left
            BeachSection lArc = previous;

            while (lArc.circleEvent &&
                   Mathf.Abs(x - lArc.circleEvent.x) < EPSILON &&
                   Mathf.Abs(y - lArc.circleEvent.yCenter) < EPSILON)
            {
                previous = lArc.Prev;
                disappearingTransitions.AddFirst(lArc);
                this.DetachBeachSection(lArc); // mark for reuse
                lArc = previous;
            }
            // even though it is not disappearing, I will also add the beach section
            // immediately to the left of the left-most collapsed beach section, for
            // convenience, since we need to refer to it later as this beach section
            // is the 'left' site of an edge for which a start point is set.
            disappearingTransitions.AddFirst(lArc);
            this.DetachCircleEvent(lArc);

            // look right
            BeachSection rArc = next;

            while (rArc.circleEvent &&
                   Mathf.Abs(x - rArc.circleEvent.x) < EPSILON &&
                   Mathf.Abs(y - rArc.circleEvent.yCenter) < EPSILON)
            {
                next = rArc.Next;
                disappearingTransitions.AddLast(rArc);
                this.DetachBeachSection(rArc);
                rArc = next;
            }
            // we also have to add the beach section immediately to the right of the
            // right-most collapsed beach section, since there is also a disappearing
            // transition representing an edge's start point on its left.
            disappearingTransitions.AddLast(rArc);
            this.DetachCircleEvent(rArc);

            // walk through all the disappearing transitions between beach sections and
            // set the start point of their (implied) edge.
            int nArcs = disappearingTransitions.Count;

            for (int iArc = 1; iArc < nArcs; iArc++)
            {
                rArc = disappearingTransitions.ElementAt(iArc);
                lArc = disappearingTransitions.ElementAt(iArc - 1);
                rArc.edge.SetStartPoint(lArc.site, rArc.site, vertex);
            }

            // create a new edge as we have now a new transition between
            // two beach sections which were previously not adjacent.
            // since this edge appears as a new vertex is defined, the vertex
            // actually define an end point of the edge (relative to the site
            // on the left)
            lArc      = disappearingTransitions.ElementAt(0);
            rArc      = disappearingTransitions.ElementAt(nArcs - 1);
            rArc.edge = this.CreateEdge(lArc.site, rArc.site, null, vertex);

            // create circle events if any for beach sections left in the beachline
            // adjacent to collapsed sections
            this.AttachCircleEvent(lArc);
            this.AttachCircleEvent(rArc);
        }
예제 #7
0
        private void FinalizeDiagram(IBinaryNode node)
        {
            switch (node)
            {
            case Leaf leafNode when leafNode.BreakpointNode == null:
                return;

            case Leaf leafNode: {
                var breakpoint = leafNode.BreakpointNode.Breakpoint;

                var face     = breakpoint.LeftListEvent.PartialLine.Face;
                var twinFace = breakpoint.LeftListEvent.PartialLine.Twin.Face;

                var centerX = 0.5f
                              * (breakpoint.LeftListEvent.PartialLine.Face.X
                                 + breakpoint.LeftListEvent.PartialLine.Twin.Face.X);
                var centerY = 0.5f
                              * (breakpoint.LeftListEvent.PartialLine.Face.Y
                                 + breakpoint.LeftListEvent.PartialLine.Twin.Face.Y);

                if (Math.Abs(face.Y - twinFace.Y) < EPSILON_DOUBLE)
                {
                    var halfEdge    = breakpoint.LeftListEvent.PartialLine;
                    var circleEvent = new CircleEvent(centerX, -MaximumEdgeLimit);

                    if (halfEdge.CircleEvent == null)
                    {
                        halfEdge.CircleEvent    = circleEvent;
                        circleEvent.PartialLine = halfEdge;
                    }
                    else
                    {
                        halfEdge.Twin.CircleEvent = circleEvent;
                        circleEvent.PartialLine   = halfEdge.Twin;
                    }
                }
                else
                {
                    var grad = -(twinFace.X - face.X) / (twinFace.Y - face.Y);

                    var constant = centerY - grad * centerX;

                    var bpx = breakpoint.X - centerX;
                    var bpy = breakpoint.Y - centerY;

                    var testX = centerX + 10000;
                    var testY = testX * grad + constant;

                    if (testX * bpx + testY * bpy <= 0)
                    {
                        testX = centerX - 10000;
                        testY = (centerX - 10000) * grad + constant;
                    }

                    var circleEvent = new CircleEvent(testX, testY);

                    var halfEdge = breakpoint.LeftListEvent.PartialLine;

                    if (!halfEdge.Face.Equals(breakpoint.LeftListEvent.Point))
                    {
                        halfEdge = halfEdge.Twin;
                    }

                    if (halfEdge.CircleEvent == null)
                    {
                        halfEdge.CircleEvent = circleEvent;
                    }
                    else if (halfEdge.Twin.CircleEvent == null)
                    {
                        halfEdge.Twin.CircleEvent = circleEvent;
                    }
                }

                break;
            }

            case BreakpointNode breakpointNode: {
                breakpointNode.Breakpoint.InitializeBreakpoint(MaximumEdgeLimit);

                if (node.Left != null)
                {
                    FinalizeDiagram(node.Left);
                }

                if (node.Right != null)
                {
                    FinalizeDiagram(node.Right);
                }

                break;
            }
            }
        }
예제 #8
0
        public IBinaryNode GetNearest(Builder builder, IBinaryNode current, SeedEvent seedEvent, double edgeLimit)
        {
            while (true)
            {
                var x = seedEvent.X;
                var y = seedEvent.Y;

                if ((int)y == (int)MaxY)
                {
                    var le   = new TreeItem(seedEvent.Vertex);
                    var evt  = new Leaf(le);
                    var next = Root;

                    while (next.GetNextLeaf() != null)
                    {
                        next = next.GetNextLeaf();
                    }

                    if (!(next is Leaf nextLeaf))
                    {
                        return(null);
                    }

                    var h1 = new PartialLine(nextLeaf.ListItem.Point);
                    var h2 = new PartialLine(le.Point);

                    h1.Twin = h2;
                    h2.Twin = h1;
                    PartialLines.Add(h1);
                    PartialLines.Add(h2);

                    if (nextLeaf.ListItem.Point.PartialLine == null)
                    {
                        nextLeaf.ListItem.Point.PartialLine = h1;
                    }
                    else
                    {
                        var n = nextLeaf.ListItem.Point.PartialLine;

                        while (n.Next != null)
                        {
                            n = n.Next;
                        }

                        n.Next = h1;
                    }

                    nextLeaf.ListItem.PartialLine = h1;
                    le.PartialLine = h1;

                    le.Point.PartialLine = h2;

                    var b0      = new Breakpoint(nextLeaf.ListItem, evt.ListItem);
                    var bpNode0 = new BreakpointNode(b0);
                    nextLeaf.BreakpointNode = bpNode0;

                    if (next == Root)
                    {
                        next.Parent   = bpNode0;
                        Root          = bpNode0;
                        bpNode0.Left  = next;
                        bpNode0.Right = evt;
                        evt.Parent    = bpNode0;
                    }
                    else
                    {
                        var parent = next.Parent;
                        parent.Right   = bpNode0;
                        bpNode0.Parent = parent;
                        bpNode0.Left   = next;
                        next.Parent    = bpNode0;
                        bpNode0.Right  = evt;
                        evt.Parent     = bpNode0;
                    }

                    b0.InitializeBreakpoint(y);
                    b0.X = (x + next.X) / 2;
                    b0.Y = y + edgeLimit;

                    var ce = new CircleEvent(b0.X, b0.Y);

                    h1.CircleEvent = ce;
                    ce.PartialLine = h1;

                    return(null);
                }

                current.CalculateInnerBreakpoint(y - 0.0001);

                if (Math.Abs(x - current.X) < EPSILON_DOUBLE)
                {
                    return(current);
                }

                var child = x > current.X ? current.Right : current.Left;
                if (child is null)
                {
                    return(current);
                }

                current = child;
            }
        }
예제 #9
0
        public List <CircleEvent> RemoveNode(CircleEvent ce, Leaf prev, Leaf remove, Leaf next)
        {
            var circleEvents = new List <CircleEvent>();

            var currentVector2 = prev.ListItem.Point;
            var newVector2     = next.ListItem.Point;
            var h1             = new PartialLine(currentVector2);
            var h2             = new PartialLine(newVector2);

            h1.Twin = h2;
            h2.Twin = h1;
            PartialLines.Add(h1);
            PartialLines.Add(h2);

            h1.CircleEvent = ce;
            var bp1 = new Breakpoint(prev.ListItem, next.ListItem);

            if (prev.BreakpointNode == remove.Parent)
            {
                prev.BreakpointNode = remove.BreakpointNode;
            }

            prev.BreakpointNode.Breakpoint = bp1;

            {
                var oldEdgeP = prev.ListItem.PartialLine;

                if (!oldEdgeP.Face.Equals(prev.ListItem.Point))
                {
                    oldEdgeP = oldEdgeP.Twin;
                }

                var n = oldEdgeP.Next;
                oldEdgeP.Next = h1;
                h1.Previous   = oldEdgeP;

                if (n != null)
                {
                    h1.Next    = n;
                    n.Previous = h1;
                }
            }

            {
                var oldEdgeN = next.ListItem.PartialLine;

                if (!oldEdgeN.Face.Equals(next.ListItem.Point))
                {
                    oldEdgeN = oldEdgeN.Twin;
                }

                var n = oldEdgeN.Next;

                oldEdgeN.Next = h2;
                h2.Previous   = oldEdgeN;

                if (n != null)
                {
                    h2.Next    = n;
                    n.Previous = h2;
                }
            }

            prev.ListItem.PartialLine = h1;

            if (remove.Parent.Parent == null)
            {
                Root = remove.Parent.Left == remove
                    ? remove.Parent.Right
                    : remove.Parent.Left;
                Root.Parent = null;
            }
            else
            {
                var grandparent = remove.Parent.Parent;
                var parent      = remove.Parent;

                if (remove == remove.Parent.Left)
                {
                    if (remove.Parent == grandparent.Left)
                    {
                        grandparent.Left    = parent.Right;
                        parent.Right.Parent = grandparent;
                    }
                    else
                    {
                        grandparent.Right   = parent.Right;
                        parent.Right.Parent = grandparent;
                    }
                }

                else
                {
                    if (remove.Parent == grandparent.Left)
                    {
                        grandparent.Left   = parent.Left;
                        parent.Left.Parent = grandparent;
                    }
                    else
                    {
                        grandparent.Right  = parent.Left;
                        parent.Left.Parent = grandparent;
                    }
                }

                remove.Parent = null;
            }

            var nextNextTree = next.GetNextLeaf();

            if (nextNextTree != null &&
                nextNextTree is Leaf nextNext)
            {
                var circleEvent        = new CircleEvent(prev, next, nextNext);
                var canCalculateCircle = circleEvent.CalculateCircle();

                next.BreakpointNode.Breakpoint = new Breakpoint(next.ListItem, nextNext.ListItem);

                bp1.InitializeBreakpoint(circleEvent.DistanceFromLine);

                next.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine);

                if (canCalculateCircle && circleEvent.CheckConvergence())
                {
                    next.DisappearEvent = circleEvent;

                    circleEvents.Add(circleEvent);
                }
            }

            var prevPrevTree = prev.GetPreviousLeaf();

            if (prevPrevTree != null &&
                prevPrevTree is Leaf prevPrev)
            {
                var circleEvent        = new CircleEvent(prevPrev, prev, next);
                var canCalculateCircle = circleEvent.CalculateCircle();

                prevPrev.BreakpointNode.Breakpoint = new Breakpoint(prevPrev.ListItem, prev.ListItem);
                bp1.InitializeBreakpoint(circleEvent.DistanceFromLine);

                prevPrev.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine);

                if (canCalculateCircle && circleEvent.CheckConvergence())
                {
                    prev.DisappearEvent = circleEvent;

                    circleEvents.Add(circleEvent);
                }
            }

            return(circleEvents);
        }
예제 #10
0
        public List <CircleEvent> AddSeedEvent(Leaf current, TreeItem listItem)
        {
            var circleEvents = new List <CircleEvent>();

            var currentVector2 = current.ListItem.Point;
            var newVector2     = listItem.Point;
            var h1             = new PartialLine(currentVector2);
            var h2             = new PartialLine(newVector2);

            h1.Twin = h2;
            h2.Twin = h1;
            PartialLines.Add(h1);
            PartialLines.Add(h2);

            if (currentVector2.PartialLine == null)
            {
                currentVector2.PartialLine = h1;
            }

            newVector2.PartialLine = h2;

            var oldHe = current.ListItem.PartialLine;

            current.ListItem.PartialLine = h1;
            listItem.PartialLine         = h1;

            var prev = current.GetPreviousLeaf();
            var next = current.GetNextLeaf();

            var newNode  = new Leaf(listItem);
            var copy     = current.ListItem.Copy();
            var copyNode = new Leaf(copy);

            var bp1 = new Breakpoint(current.ListItem, listItem);

            bp1.InitializeBreakpoint(listItem.Point.Y - 1);

            var bp2 = new Breakpoint(listItem, copy);

            bp2.InitializeBreakpoint(listItem.Point.Y - 1);

            var bp1Node = new BreakpointNode(bp1);
            var bp2Node = new BreakpointNode(bp2);

            current.BreakpointNode = bp1Node;
            newNode.BreakpointNode = bp2Node;

            if (current.Parent != null)
            {
                if (current == current.Parent.Left)
                {
                    current.Parent.Left = bp1Node;
                    bp1Node.Parent      = current.Parent;
                }
                else
                {
                    current.Parent.Right = bp1Node;
                    bp1Node.Parent       = current.Parent;
                }
            }
            else
            {
                SetRoot(bp1Node);
                Root.Parent = null;
            }

            bp1Node.Left   = current;
            current.Parent = bp1Node;

            bp1Node.Right  = bp2Node;
            bp2Node.Parent = bp1Node;

            bp2Node.Left   = newNode;
            newNode.Parent = bp2Node;

            bp2Node.Right   = copyNode;
            copyNode.Parent = bp2Node;

            if (oldHe != null)
            {
                copyNode.ListItem.PartialLine = oldHe;
                var pre = copyNode.ListItem.PartialLine;

                if (!pre.Face.Equals(currentVector2))
                {
                    pre = pre.Twin;
                }

                var onext = pre.Next;
                pre.Next    = h1;
                h1.Previous = pre;

                if (onext != null)
                {
                    onext.Previous = h1;
                    h1.Next        = onext;
                }
            }

            if (prev != null && prev is Leaf prevLeaf)
            {
                var circleEvent        = new CircleEvent(prevLeaf, current, newNode);
                var canCalculateCircle = circleEvent.CalculateCircle();

                prevLeaf.BreakpointNode.Breakpoint = new Breakpoint(prevLeaf.ListItem, current.ListItem);

                bp1Node.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine);
                prevLeaf.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine);

                if (canCalculateCircle && circleEvent.CheckConvergence())
                {
                    current.DisappearEvent = circleEvent;
                    circleEvents.Add(circleEvent);
                }
            }

            if (next != null && next is Leaf nextLeaf)
            {
                var circleEvent        = new CircleEvent(newNode, copyNode, nextLeaf);
                var canCalculateCircle = circleEvent.CalculateCircle();

                copyNode.BreakpointNode            = (BreakpointNode)copyNode.GetNext();
                copyNode.BreakpointNode.Breakpoint = new Breakpoint(copy, nextLeaf.ListItem);

                bp2Node.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine);
                copyNode.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine);

                if (canCalculateCircle && circleEvent.CheckConvergence())
                {
                    copyNode.DisappearEvent = circleEvent;
                    circleEvents.Add(circleEvent);
                }
            }
            else
            {
                copyNode.ListItem.PartialLine = h1;
            }

            return(circleEvents);
        }
예제 #11
0
파일: Program.cs 프로젝트: y08git/voronoi
        void ProcessCircleEvent(FortunesAlgorithmState state, CircleEvent e)
        {
            var sweepY = e.Y;
            var swallowedParabolaNode = e.SwallowedParabolaNode;

            if (swallowedParabolaNode.Parent == null)
            {
                return;
            }

            Node leftAncestor, rightAncestor;

            swallowedParabolaNode.FindDirectionalAncestors(out leftAncestor, out rightAncestor);

            var leftAncestorEdgeRayData  = (EdgeRayData)leftAncestor.Data;
            var rightAncestorEdgeRayData = (EdgeRayData)rightAncestor.Data;

            state.VoronoiEdges.Add(new VoronoiEdge(
                                       leftAncestorEdgeRayData.Origin, leftAncestorEdgeRayData.IsOriginBounded, e.Circumcenter, true));
            state.VoronoiEdges.Add(new VoronoiEdge(
                                       rightAncestorEdgeRayData.Origin, leftAncestorEdgeRayData.IsOriginBounded, e.Circumcenter, true));

            var leftParabolaNode  = leftAncestor.Left.GetRightmost();
            var leftParabolaFocus = ((ParabolaData)leftParabolaNode.Data).Focus;

            var rightParabolaNode  = rightAncestor.Right.GetLeftmost();
            var rightParabolaFocus = ((ParabolaData)rightParabolaNode.Data).Focus;

            state.DelanayEdges.Add(new VoronoiEdge(leftParabolaFocus, true, rightParabolaFocus, true));

            // One ancestor will become the edge shared by the left/right parabolas,
            // the other will be deleted. It's guranteed our parent is one of these
            // ancestors and that the other ancestor is above it (that is, it has a
            // parent) so opt to delete our parent.
            var nodeParent  = swallowedParabolaNode.Parent;
            var nodeSibling = nodeParent.Left == swallowedParabolaNode ? nodeParent.Right : nodeParent.Left;

            NodeOperations.ReplaceNode(nodeParent, nodeSibling, ref state.Root);
            nodeParent.Left = nodeParent.Right = null;

            var olderAncestor        = nodeParent == leftAncestor ? rightAncestor : leftAncestor;
            var leftFocusRightFocus  = rightParabolaFocus - leftParabolaFocus; // x is positive
            var edgeDirection        = new TVector2(-leftFocusRightFocus.Y, leftFocusRightFocus.X);
            var leftRightFocusCenter = new TVector2(
                MathUtil.Average(leftParabolaFocus.X, rightParabolaFocus.X),
                MathUtil.Average(leftParabolaFocus.Y, rightParabolaFocus.Y));
            var dy        = sweepY - leftRightFocusCenter.Y;
            var edgeStart = e.Circumcenter;

            olderAncestor.Data = new EdgeRayData(true, edgeStart, edgeDirection);

            // add new potential circle events
            Node leftLeftLeaf;

            if (leftParabolaNode.TryGetLeftLeaf(out leftLeftLeaf))
            {
                HandleAddCircleEvent(state, leftLeftLeaf, leftParabolaNode, rightParabolaNode, sweepY);
            }

            Node rightRightLeaf;

            if (rightParabolaNode.TryGetRightLeaf(out rightRightLeaf))
            {
                HandleAddCircleEvent(state, leftParabolaNode, rightParabolaNode, rightRightLeaf, sweepY);
            }
        }