예제 #1
0
        /// <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());
        }
예제 #2
0
        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);
        }