/// <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); }
/// <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 }); } }
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? } } }