/// <summary> /// Approximate the given Bezier curve as lines and arcs. /// </summary> internal static IPrimitive[] AsSimplePrimitives(PrimitiveBezier bezier) { var simplePrimitives = new List <IPrimitive>(); var intervals = new Queue <Tuple <double, double, int> >(); intervals.Enqueue(Tuple.Create(0.0, 1.0, 0)); while (intervals.Count > 0) { var interval = intervals.Dequeue(); var intervalStart = interval.Item1; var intervalEnd = interval.Item2; var currentSplitCount = interval.Item3; var intervalSpread = intervalEnd - intervalStart; var intervalMid = intervalStart + (intervalSpread * 0.5); var pStart = bezier.ComputeParameterizedPoint(intervalStart); var pMid = bezier.ComputeParameterizedPoint(intervalMid); var pEnd = bezier.ComputeParameterizedPoint(intervalEnd); var intervalLine = new PrimitiveLine(pStart, pEnd); var candidatePrimitive = intervalLine.IsPointOnPrimitive(pMid, MathHelper.BezierEpsilon) ? (IPrimitive)intervalLine : PrimitiveEllipse.ThreePointArc(pStart, pMid, pEnd, idealNormal: Vector.ZAxis); candidatePrimitive = candidatePrimitive ?? intervalLine; // ensure it has a value var intervalFirstQuarter = intervalStart + (intervalSpread * 0.25); var intervalThirdQuarter = intervalStart + (intervalSpread * 0.75); var pFirstQuarter = bezier.ComputeParameterizedPoint(intervalFirstQuarter); var pThirdQuarter = bezier.ComputeParameterizedPoint(intervalThirdQuarter); if (currentSplitCount >= MaximumPrimitiveSplitCount || (candidatePrimitive.IsPointOnPrimitive(pFirstQuarter, MathHelper.BezierEpsilon) && candidatePrimitive.IsPointOnPrimitive(pThirdQuarter, MathHelper.BezierEpsilon))) { simplePrimitives.Add(candidatePrimitive); } else { intervals.Enqueue(Tuple.Create(intervalStart, intervalMid, currentSplitCount + 1)); intervals.Enqueue(Tuple.Create(intervalMid, intervalEnd, currentSplitCount + 1)); } } return(simplePrimitives.ToArray()); }
/// <summary> /// Creates a circle that passes through the three specified points. Null if the points are co-linear /// </summary> /// <param name="a">The first point.</param> /// <param name="b">The second point.</param> /// <param name="c">The third point.</param> /// <param name="idealNormal">The ideal normal to normalize to if specified.</param> /// <returns>The resultant circle or null.</returns> public static PrimitiveEllipse ThreePointCircle(Point a, Point b, Point c, Optional <Vector> idealNormal = default(Optional <Vector>)) { var v1 = a - b; var v2 = c - b; var normal = v1.Cross(v2); if (normal.IsZeroVector) { return(null); } normal = normal.Normalize(); var m1 = v1.Cross(normal); var m2 = v2.Cross(normal); var b1a = (a + b) / 2.0; var b2a = (c + b) / 2.0; var b1 = new PrimitiveLine(b1a, b1a + m1); var b2 = new PrimitiveLine(b2a, b2a + m2); var center = b1.IntersectionPoint(b2, false); if (center == null) { return(null); } if (idealNormal.HasValue && idealNormal.Value == normal * -1.0) { normal = idealNormal.Value; } return(new PrimitiveEllipse(center.GetValueOrDefault(), (a - center.GetValueOrDefault()).Length, normal)); }