Example #1
0
 public void DetachBeachSection(BeachSection beachSection)
 {
     this.DetachCircleEvent(beachSection); // detach potentially attached circle event
     this.beachLine.Remove(beachSection); // remove from RB-tree
     this.beachSectionJunkyard.Add(beachSection); // mark for reuse
 }
Example #2
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;
            }
        }
Example #3
0
        // rhill 2011-06-02: A lot of Beachsection instanciations
        // occur during the computation of the Voronoi diagram,
        // somewhere between the number of sites and twice the
        // number of sites, while the number of Beachsections on the
        // beachline at any given time is comparatively low. For this
        // reason, we reuse already created Beachsections, in order
        // to avoid new memory allocation. This resulted in a measurable
        // performance gain.
        public BeachSection CreateBeachSection(Point site)
        {
            BeachSection beachSection = this.beachSectionJunkyard.Count > 0 ? this.beachSectionJunkyard.Last() : null;
            if (beachSection)
            {
                this.beachSectionJunkyard.Remove(beachSection);
                beachSection.site = site;
            }
            else
            {
                beachSection = new BeachSection(site);
            }

            return beachSection;
        }
Example #4
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;
            }
        }
Example #5
0
        // calculate the right break point of a particular beach section,
        // given a particular directrix
        public float RightBreakPoint(BeachSection arc, float directrix)
        {
            BeachSection rArc = arc.Next;
            if (rArc)
            {
                return this.LeftBreakPoint(rArc, directrix);
            }

            Point site = arc.site;
            return site.y == directrix ? site.x : Mathf.Infinity;
        }
Example #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);
        }
Example #7
0
        // calculate the left break point of a particular beach section,
        // given a particular sweep line
        public float LeftBreakPoint(BeachSection arc, float directrix)
        {
            // http://en.wikipedia.org/wiki/Parabola
            // http://en.wikipedia.org/wiki/Quadratic_equation
            // h1 = x1,
            // k1 = (y1+directrix)/2,
            // h2 = x2,
            // k2 = (y2+directrix)/2,
            // p1 = k1-directrix,
            // a1 = 1/(4*p1),
            // b1 = -h1/(2*p1),
            // c1 = h1*h1/(4*p1)+k1,
            // p2 = k2-directrix,
            // a2 = 1/(4*p2),
            // b2 = -h2/(2*p2),
            // c2 = h2*h2/(4*p2)+k2,
            // x = (-(b2-b1) + Math.sqrt((b2-b1)*(b2-b1) - 4*(a2-a1)*(c2-c1))) / (2*(a2-a1))
            // When x1 become the x-origin:
            // h1 = 0,
            // k1 = (y1+directrix)/2,
            // h2 = x2-x1,
            // k2 = (y2+directrix)/2,
            // p1 = k1-directrix,
            // a1 = 1/(4*p1),
            // b1 = 0,
            // c1 = k1,
            // p2 = k2-directrix,
            // a2 = 1/(4*p2),
            // b2 = -h2/(2*p2),
            // c2 = h2*h2/(4*p2)+k2,
            // x = (-b2 + Math.sqrt(b2*b2 - 4*(a2-a1)*(c2-k1))) / (2*(a2-a1)) + x1

            // change code below at your own risk: care has been taken to
            // reduce errors due to computers' finite arithmetic precision.
            // Maybe can still be improved, will see if any more of this
            // kind of errors pop up again.
            Point site = arc.site;
            float rfocx = site.x;
            float rfocy = site.y;
            float pby2 = rfocy - directrix;

            // parabola in degenerate case where focus is on directrix
            if (pby2 == 0)
            {
                return rfocx;
            }

            BeachSection lArc = arc.Prev;
            if (!lArc)
            {
                return -Mathf.Infinity;
            }

            site = lArc.site;
            float lfocx = site.x;
            float lfocy = site.y;
            float plby2 = lfocy - directrix;

            // parabola in degenerate case where focus is on directrix
            if (plby2 == 0)
            {
                return lfocx;
            }

            float hl = lfocx - rfocx;
            float aby2 = 1 / pby2 - 1 / plby2;
            float b = hl / plby2;

            if (aby2 != 0)
            {
                return (-b + Mathf.Sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
            }
            // both parabolas have same distance to directrix, thus break point is midway
            return (rfocx + lfocx) / 2;
        }