/// <summary> /// Builds a list of <see cref="Bezier"/> from a list of <see cref="Point"/> for /// the specified error tolerance. /// </summary> /// <param name="points"> /// A list of <see cref="Point"/> to build from. /// </param> /// <param name="tolerance"> /// The error tolerance value between 4 to 20 inclusive. /// </param> /// <returns> /// A list of <see cref="Bezier"/> from a list of <see cref="Point"/> for /// the specified error tolerance. /// </returns> public static ReadOnlyCollection <Bezier> Build( IEnumerable <Point> points, double tolerance = DefaultError) { if (points == null) { throw new ArgumentNullException("points"); } if (!tolerance.IsBetween(MinimumError, MaximumError)) { throw new ArgumentOutOfRangeException( string.Format( "The error tolerance must be between {0} and {1} inclusive.", MinimumError, MaximumError), "tolerance"); } List <Point> list = points.Filter(MinimumDistance); if (list.Count < 2) { throw new ArgumentException("There must be at least two distinct points in the list.", "points"); } List <Bezier> bezierList = new List <Bezier>(); ReadOnlyCollection <Point> bezierPoints = list.AsReadOnly(); Vector leftTangent = bezierPoints.LeftTangent(); Vector rightTangent = bezierPoints.RightTangent(); BezierBuilder bezierBuilder = new BezierBuilder( bezierPoints, leftTangent, rightTangent, tolerance); bezierBuilder.Build(bezierList); return(bezierList.AsReadOnly()); }
private void Build(List <Bezier> bezierList) { // Use heuristic if region only has two points in it if (points.Count == 2) { Point p0 = points.First(); Point p3 = points.Last(); double distance = p0.DistanceTo(p3) / 3.0; Point p1 = p0 + leftTangent * distance; Point p2 = p3 + rightTangent * distance; bezierList.Add(new Bezier(p0, p1, p2, p3)); return; } // Parameterize points, and attempt to fit curve parameterizedLength = points.Parameterize(); Bezier bezier = Bezier.Create(this); // Find max deviation of points to fitted curve int index; double maximumDistanceError = bezier.CalculateMaximumDistance(this, out index); if (maximumDistanceError < tolerance) { bezierList.Add(bezier); return; } // If error not too large, try some reparameterization and iteration double iterationError = tolerance * tolerance; if (maximumDistanceError < iterationError) { for (int i = 0; i < MaximumIterationCount; i++) { parameterizedLength = bezier.Reparameterize(this); bezier = Bezier.Create(this); maximumDistanceError = bezier.CalculateMaximumDistance(this, out index); if (maximumDistanceError < tolerance) { bezierList.Add(bezier); return; } } } // Fitting failed -- split at max error point and fit recursively Vector centerTangent = points.CenterTangent(index); BezierBuilder builder = new BezierBuilder( points .Take(index + 1) .ToList() .AsReadOnly(), leftTangent, centerTangent, tolerance); builder.Build(bezierList); centerTangent.Negate(); builder = new BezierBuilder( points .Skip(index + 1) .ToList() .AsReadOnly(), centerTangent, rightTangent, tolerance); builder.Build(bezierList); }