Exemple #1
0
        /// <summary>
        /// Registers the circle event created by the three parabolas as the directrix moves.
        /// </summary>
        /// <param name="shrinking">The parabola which is shrinking. This parabola is assigned the circle event.</param>
        /// <param name="left">The left parabola which will swallow the shrinking parabola</param>
        /// <param name="right">The right parabola which will swallow the shrinking parabola</param>
        private void RegisterCircleEvent(VParabolaNode left, VParabolaNode shrinking, VParabolaNode right)
        {
            if (left == null || right == null)
            {
                throw new ArgumentNullException("left or right");
            }

            // check for conditions at which shrinking wouldn't be swallowed, return if that's the case
            if (shrinking.Site.Y.Within(left.Site.Y, double.Epsilon) &&
                shrinking.Site.Y > right.Site.Y)
            {
                return;
            }

            if (shrinking.Site.Y.Within(right.Site.Y, double.Epsilon) &&
                shrinking.Site.Y > left.Site.Y)
            {
                return;
            }

            // todo: left and right commons are already computed at Parabola Event, can steal from there?
            var leftCommon  = (VEdgeNode)shrinking.GetRightParent();
            var rightCommon = (VEdgeNode)shrinking.GetLeftParent();

            // :: Find the intersection of the two edges which converge on the swallowed parabola
            // :: this will be the center of the circle event.
            var intersection = GeometryUtilities.FindNondegenerateIntersection(
                leftCommon.Site, leftCommon.Direction,
                rightCommon.Site, rightCommon.Direction
                );
            var circleRadius = shrinking.Site.DistanceTo(intersection);
            var newEvent     = new VCircleEvent(intersection, circleRadius, shrinking);

            m_queue.Add(newEvent);
        }
Exemple #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="parabola"></param>
        /// <returns></returns>
        public Vector2D[] CalculateIntersect(VParabolaNode parabola, double directrixY)
        {
            // Get a, b, c coefficients of our parabolas
            var par1 = this.GetEquationCoefficients(directrixY);
            var par2 = parabola.GetEquationCoefficients(directrixY);

            double a = par1.a - par2.a;
            double b = par1.b - par2.b;
            double c = par1.c - par2.c;

            // x = (-b +- sqrt[b^2 - 4ac])/2a
            double sqrtDiscriminant = (double)Math.Sqrt(b * b - 4 * a * c);

            if (double.IsNaN(sqrtDiscriminant) || Math.Abs(a) <= double.Epsilon)
            {
                // The two parabolas don't intersect.
                return(null);
            }
            else
            {
                double x1 = (-b - sqrtDiscriminant) / (2 * a);
                double x2 = (-b + sqrtDiscriminant) / (2 * a);
                return(new Vector2D[]
                {
                    new Vector2D(x1, par1.Calculate(x1)),
                    new Vector2D(x2, par2.Calculate(x2)) // Technically could use par1 because is intersect
                });
            }
        }
Exemple #3
0
        private void AddParabola(Point2D site)
        {
            if (m_root == null)
            {
                m_root = new VParabolaNode(site);
            }
            else if (m_root.EnumerateLeaves().All((leaf) => leaf.Site.Y == site.Y))
            {
                // Degenerate case - all parabola sites on the same y-value.
                // If this happens, then our edge starts between us and our nearest point, and we need
                // to force affected edges (left and right) to recalculate their edge starts.
                var cutParabola = m_root.GetParabolaByX(site.X, site.Y);
                var newParabola = new VParabolaNode(site);
                var middle      = Point2D.Average(cutParabola.Site, newParabola.Site);

                // store old parabola parent so we can append junction node there
                var cutParent = cutParabola.Parent;

                // note: edge ctor appends child parabolas to itself.
                VEdgeNode edge;
                if (cutParabola.Site.X < newParabola.Site.X)
                {
                    edge = new VEdgeNode(middle, Vector2D.UnitXPlus, cutParabola, newParabola);
                }
                else
                {
                    edge = new VEdgeNode(middle, Vector2D.UnitXPlus, newParabola, cutParabola);
                }

                // append edge node to old cutparabola parent
                if (cutParent != null)
                {
                    if (cutParent.Left == null)
                    {
                        cutParent.Left = edge;
                    }
                    else
                    {
                        cutParent.Right = edge;
                    }
                }
            }
            else
            {
                var cutParabola = m_root.GetParabolaByX(site.X, site.Y);

                // store old parabola parent so we can append junction node there
                var cutParent = cutParabola.Parent;

                // :: determine the parabolas left and right of the cut parabola. This is important
                // :: for the "Swallowing" of parabolas by circle events.
                var cutParabolaLeft  = (VParabolaNode)cutParabola.GetLeftLeaf();
                var cutParabolaRight = (VParabolaNode)cutParabola.GetRightLeaf();

                // :: determine the left, middle, and right parabolas formed by this cut.
                var left   = cutParabola;
                var middle = new VParabolaNode(site);
                var right  = cutParabola.Clone();

                // :: determine where the new "middle" parabola meets the "cut" parabola
                var intersection = cutParabola.CalculatePointAtX(site.X, site.Y);

                // create the left and right edges...
                var vCutMiddle     = middle.Site - cutParabola.Site;
                var rightDirection = vCutMiddle.Perp(); // performs 90deg counterclockwise rotation in euclidean space
                var leftDirection  = rightDirection.Flip();
                var leftEdge       = new VEdgeNode(intersection, leftDirection, left, middle);
                var rightEdge      = new VEdgeNode(intersection, leftDirection, leftEdge, right);

                if (cutParent.Left == null)
                {
                    cutParent.Left = rightEdge;
                }
                else
                {
                    cutParent.Right = rightEdge;
                }

                // :: Manage Circle Events
                // Unregister the cut parabola node's event, which is now invalidated.
                if (cutParabola.CircleEvent != null)
                {
                    m_queue.Remove(cutParabola.CircleEvent);
                    cutParabola.CircleEvent = null;
                }

                // Register the left, right, and middle circle events
                if (cutParabolaLeft != null)
                {
                    RegisterCircleEvent(cutParabolaLeft, left, middle);
                }
                if (cutParabolaRight != null)
                {
                    RegisterCircleEvent(middle, right, cutParabolaRight);
                }
                if (cutParabolaLeft != null && cutParabolaRight != null)
                {
                    RegisterCircleEvent(left, middle, right); // wtf? Shouldn't be necessary, right?
                }
            }
        }