예제 #1
0
        /* Function: findRightBreakPoint
         * ----------------------------
         * Finds the breakpoint to the right of the new arc in the beach line.
         */
        private float findRightBreakPoint(BeachArc node, float sweepLineY)
        {
            BeachArc rightArc = node.Next;

            if (rightArc)
            {
                return(findLeftBreakPoint(rightArc, sweepLineY));
            }
            return((node.site.y == sweepLineY) ? node.site.x : Mathf.Infinity);
        }
예제 #2
0
        /* Function: deleteCircleEvent
         * ---------------------------
         * Deletes a potential circle event associated with a site event
         * from the event queue (it is no longer correct).
         */
        private void deleteCircleEvent(BeachArc arc)
        {
            CircleEvent ce = arc.circleEvent;

            arc.circleEvent = null;
            if (ce != null && sweepLine.Contains(ce))
            {
                sweepLine.Remove(ce);
                usedCircleEvents.Add(ce);
            }
        }
예제 #3
0
        /* Function: findLeftBreakPoint
         * ----------------------------
         * Finds the breakpoint to the left of the new arc in the beach line.
         */
        private float findLeftBreakPoint(BeachArc node, float sweepLineY)
        {
            // Right site
            Site  site                = node.site;
            float rightFocusX         = site.x;
            float rightFocusY         = site.y;
            float rightFocusSweepDiff = rightFocusY - sweepLineY;

            // Parabola focus on directrix (sweep line)
            if (rightFocusSweepDiff == 0.0f)
            {
                return(rightFocusX);
            }

            // New node, don't do anything
            BeachArc leftArc = node.Prev;

            if (leftArc == null)
            {
                return(-Mathf.Infinity);
            }

            // Left site
            site = leftArc.site;
            float leftFocusX         = site.x;
            float leftFocusY         = site.y;
            float leftFocusSweepDiff = leftFocusY - sweepLineY;

            // Parabola focus on directrix (sweep line)
            if (leftFocusSweepDiff == 0.0f)
            {
                return(leftFocusX);
            }

            // Find intersection
            float xFocusDiff   = leftFocusX - rightFocusX;
            float inverseYDiff = 1 / rightFocusSweepDiff - 1 / leftFocusSweepDiff;
            float b            = xFocusDiff / leftFocusSweepDiff;

            // Like quadratic equation centered at rightfocusX and different coefficients
            if (inverseYDiff != 0)
            {
                return((-b + Mathf.Sqrt(b * b - 2 * inverseYDiff * (xFocusDiff * xFocusDiff / (-2 * leftFocusSweepDiff)
                                                                    - leftFocusY + leftFocusSweepDiff / 2 + rightFocusY - rightFocusSweepDiff / 2)))
                       / inverseYDiff + rightFocusX);
            }

            // Both parabolas are the same distance to sweep line, breakpoint is in middle
            return((rightFocusX + leftFocusX) / 2);
        }
예제 #4
0
        /* Function: findNewArcPosition
         * ----------------------------
         * Finds left and right components of arc above the current one to locate
         * where new site arc should fall and updates the values.
         */
        private void findNewArcPosition(float siteX, float sweepLineY, ref BeachArc leftArc, ref BeachArc rightArc)
        {
            BeachArc node = beachLine.Root;
            float    xLeft, xRight;

            while (node)
            {
                xLeft = findLeftBreakPoint(node, sweepLineY) - siteX;
                // Left node x-value falls somewhere on the left edge of the beachsection
                if (xLeft > EPS)
                {
                    node = node.Left;
                }
                else
                {
                    xRight = siteX - findRightBreakPoint(node, sweepLineY);
                    // Right node x-value falls somewhere after the right edge of the beachsection
                    if (xRight > EPS)
                    {
                        if (node.Right == null)
                        {
                            leftArc = node;
                            break;
                        }
                        node = node.Right; // continue right
                    }
                    else
                    {
                        // Left node x-value falls exactly on the left edge of the beachsection
                        if (xLeft > -EPS)
                        {
                            leftArc  = node.Prev;
                            rightArc = node;
                        }
                        // Right node x-value falls exactly on the right edge of the beachsection
                        else if (xRight > -EPS)
                        {
                            leftArc  = node;
                            rightArc = node.Next;
                        }
                        // Node falls in the middle of the beachsection
                        else
                        {
                            leftArc = rightArc = node;
                        }
                        break;
                    }
                }
            }
        }
예제 #5
0
 /* Method: reset
  * -------------
  * Resets to compute a new diagram.
  */
 public void reset()
 {
     // Leftover beach sections to the used list
     if (beachLine.Root)
     {
         BeachArc arc = beachLine.GetFirst(beachLine.Root);
         while (arc)
         {
             usedBeachArcs.Add(arc);
             arc = arc.Next;
         }
     }
     beachLine.Root = null;
     // Reset sweep line (should be empty)
     sweepLine.Clear();
 }
예제 #6
0
        /* Function: createNewBeachArc
         * ---------------------------
         * Creates new beach arc from provided site. Attempts to find
         * 'used' one to minimize memory costs.
         */
        private BeachArc createNewBeachArc(Site site)
        {
            // See if used arc available for repurposing
            BeachArc arc = (usedBeachArcs.Count > 0) ? usedBeachArcs.Last() : null;

            if (arc)
            {
                usedBeachArcs.Remove(arc);
                arc.site = site;
            }
            // Otherwise create new one
            else
            {
                arc = new BeachArc(site);
            }
            return(arc);
        }
예제 #7
0
        /* Function: getTripleArcCircleEvent
         * ---------------------------------
         * Checks sites of three arcs for convergence of breakpoints, i.e. beach will
         * collapse giving rise to a circle event. If true, gets a circle event and
         * updates its fields on the receiving arc and event queue.
         *
         * Note: Calculation of circle event based on Jeremie St-Amand's:
         * https://github.com/jesta88/Unity-Voronoi/blob/master/Assets/Voronoi/FortuneVoronoi.cs
         */
        private void getTripleArcCircleEvent(Site left, ref BeachArc center, Site right)
        {
            float cx          = center.site.x;
            float cy          = center.site.y;
            float lx          = left.x - cx;
            float ly          = left.y - cy;
            float rx          = right.x - cx;
            float ry          = right.y - cy;
            float orientation = 2 * (lx * ry - ly * rx);

            // Points are CW, beach section does not collapse so no event
            if (orientation >= -2e-12)
            {
                return;
            }

            float lLengthSquared = lx * lx + ly * ly;
            float rLengthSquared = rx * rx + ry * ry;
            float x       = (ry * lLengthSquared - ly * rLengthSquared) / orientation;
            float y       = (lx * rLengthSquared - rx * lLengthSquared) / orientation;
            float yCenter = y + cy; // at or below sweepline

            // Get circle event to setup
            CircleEvent circleEvent = null;

            if (usedCircleEvents.Count > 0)
            {
                circleEvent = usedCircleEvents.Last();
                usedCircleEvents.Remove(circleEvent);
            }
            else
            {
                circleEvent = new CircleEvent();
            }

            // Update fields
            circleEvent.site = center.site;
            circleEvent.arc  = center;
            circleEvent.setPosition(x + cx, yCenter + Mathf.Sqrt(x * x + y * y), yCenter);
            center.circleEvent = circleEvent;

            // Add to event queue
            sweepLine.Enqueue(circleEvent, circleEvent);
        }
예제 #8
0
        /* Function: findPotentialCircleEvent
         * ----------------------------------
         * Checks for a potential circle event after a site event and adds it
         * to the event queue and references it on the arc if found. Done by checking
         * if triple of consecutive arcs (including new one) have breakpoints that
         * converge.
         */
        private void findPotentialCircleEvent(BeachArc arc)
        {
            BeachArc leftArc  = arc.Prev;
            BeachArc rightArc = arc.Next;

            // Handle missing edge (shouldn't happen)
            if (leftArc == null || rightArc == null)
            {
                //Debug.LogError("Error attaching potential circle event, a surrounding arc is null.");
                return;
            }

            // No convergence, arc sides same
            if (leftArc.site == rightArc.site)
            {
                return;
            }

            // If left, center, right site locations are CW no event, else event returned
            getTripleArcCircleEvent(leftArc.site, ref arc, rightArc.site);
        }
예제 #9
0
        /* Function: handleCircleEvent
         * ---------------------------
         * Handles circle event.
         */
        private void handleCircleEvent(BeachArc arc)
        {
            CircleEvent circleEvent = arc.circleEvent;
            Vertex      center      = new Vertex(circleEvent.x, circleEvent.yCircleCenter);
            BeachArc    prev        = arc.Prev;
            BeachArc    next        = arc.Next;

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

            disappearingTransitions.AddLast(arc);

            deleteBeachArc(arc);

            // Handle collapse left
            BeachArc leftArc = prev;

            while (leftArc.circleEvent != null && Mathf.Abs(center.x - leftArc.circleEvent.x) < EPS &&
                   Mathf.Abs(center.y - leftArc.circleEvent.yCircleCenter) < EPS)
            {
                prev = leftArc.Prev;
                disappearingTransitions.AddFirst(leftArc);
                deleteBeachArc(leftArc);
                leftArc = prev;
            }

            // New left arc not dissappearing, but used in edge updates
            disappearingTransitions.AddFirst(leftArc);
            deleteCircleEvent(leftArc);

            // Hanlde collapse right
            BeachArc rightArc = next;

            while (rightArc.circleEvent != null && Mathf.Abs(center.x - rightArc.circleEvent.x) < EPS &&
                   Mathf.Abs(center.y - rightArc.circleEvent.yCircleCenter) < EPS)
            {
                next = rightArc.Next;
                disappearingTransitions.AddLast(rightArc);
                deleteBeachArc(rightArc);
                rightArc = next;
            }

            // New right arc not dissappearing, but used in edge updates
            disappearingTransitions.AddLast(rightArc);
            deleteCircleEvent(rightArc);

            // Link existing edges at start point
            int nArcs = disappearingTransitions.Count;

            for (int i = 1; i < nArcs; i++)
            {
                rightArc = disappearingTransitions.ElementAt(i);
                leftArc  = disappearingTransitions.ElementAt(i - 1);
                rightArc.edge.setStartVertex(leftArc.site, rightArc.site, center);
            }

            // Create new edge between previously non-adjacent arcs
            // New vertex defines end point relative to site on the left
            leftArc       = disappearingTransitions.ElementAt(0);
            rightArc      = disappearingTransitions.ElementAt(nArcs - 1);
            rightArc.edge = createNewEdge(leftArc.site, rightArc.site, null, center);

            // Update circle events for arcs
            findPotentialCircleEvent(leftArc);
            findPotentialCircleEvent(rightArc);
        }
예제 #10
0
 /* Function: deleteBeachArc
  * ------------------------
  * Deletes an arc and associated circle event from the beach line.
  */
 private void deleteBeachArc(BeachArc arc)
 {
     deleteCircleEvent(arc);
     beachLine.Remove(arc);
     usedBeachArcs.Add(arc);
 }
예제 #11
0
        /* Function: handleSiteEvent
         * -------------------------
         * Handles site event.
         */
        private void handleSiteEvent(Site site)
        {
            // Locate arc above new site (binary search tree on x-coord)
            BeachArc leftArc  = null;
            BeachArc rightArc = null;

            findNewArcPosition(site.x, site.y, ref leftArc, ref rightArc);

            // Create a new site event for the site and add it to RB-tree
            BeachArc newArc = createNewBeachArc(site);

            beachLine.Insert(leftArc, newArc); // if leftArc null, newArc set as root

            // First element in beach line, do nothing
            if (!leftArc && !rightArc)
            {
                return;
            }
            // Node falls in middle of existing beach section arc
            else if (leftArc == rightArc)
            {
                // Delete potential circle event of now split section
                deleteCircleEvent(leftArc);

                // Split beach section in two
                rightArc = createNewBeachArc(leftArc.site);
                beachLine.Insert(newArc, rightArc);

                // New edge between new site and the one that created split old arc (from new breakpoint)
                newArc.edge = rightArc.edge = createNewEdge(leftArc.site, newArc.site);

                //  Check for potential circle events and add to the event queue
                findPotentialCircleEvent(leftArc);
                findPotentialCircleEvent(rightArc);
            }
            // Node falls last on beach line to the right
            else if (leftArc && rightArc == null)
            {
                newArc.edge = createNewEdge(leftArc.site, newArc.site);
            }
            // Only occurs if no nodes in tree yet (no left curve yet), handled above
            else if (leftArc == null && rightArc)
            {
                Debug.LogError("Error inserting first element into beachline tree.");
            }
            // Node falls inbetween two other beach section arcs
            else if (leftArc != rightArc)
            {
                deleteCircleEvent(leftArc);
                deleteCircleEvent(rightArc);

                // Find new cell vertex
                Vertex v = findNewVertex(site, leftArc.site, rightArc.site);

                // Connect vertex to arcs
                rightArc.edge.setStartVertex(leftArc.site, rightArc.site, v);
                newArc.edge   = createNewEdge(leftArc.site, site, null, v);
                rightArc.edge = createNewEdge(site, rightArc.site, null, v);

                //  Check for potential circle events and add to the event queue
                findPotentialCircleEvent(leftArc);
                findPotentialCircleEvent(rightArc);
            }
        }