/// <summary>
        /// Calculates the completion values of all the red anchors along the path.
        /// </summary>
        /// <param name="sliderPath"></param>
        /// <returns></returns>
        public static IEnumerable <double> GetRedAnchorCompletions(SliderPath sliderPath)
        {
            int    start       = 0;
            int    end         = 0;
            double totalLength = 0;
            var    anchors     = sliderPath.ControlPoints;

            for (int i = 0; i < anchors.Count; i++)
            {
                end++;

                if (i == anchors.Count - 1 || anchors[i] != anchors[i + 1])
                {
                    continue;
                }

                var cpSpan      = anchors.GetRange(start, end - start);
                var subdivision = new BezierSubdivision(cpSpan);
                totalLength += subdivision.SubdividedApproximationLength();

                yield return(totalLength / sliderPath.Distance);

                start = end;
            }
        }
Exemple #2
0
        public BezierSubdivision Prev() // Previous index at current level
        {
            var next = new BezierSubdivision(new List <Vector2>(Points), Level, Index - 1);

            next.ScaleRight(-1);
            next.Reverse();
            return(next);
        }
Exemple #3
0
        public BezierSubdivision Next() // Next index at current level
        {
            var next = new BezierSubdivision(new List <Vector2>(Points), Level, Index + 1);

            next.ScaleLeft(2);
            next.Reverse();
            return(next);
        }
 public static IEnumerable <BezierSubdivision> ChopAnchorsLinear(List <Vector2> anchors)
 {
     for (int i = 1; i < anchors.Count; i++)
     {
         var subdivision = new BezierSubdivision(new List <Vector2> {
             anchors[i - 1], anchors[i]
         });
         yield return(subdivision);
     }
 }
Exemple #5
0
        public double LengthToT(double length, double precision = 0.1, double tolerance = 0.25) // approximate bezier progress t for a desired path length, t can be > 1
        {
            if (Length() == 0)
            {
                return(double.NaN);
            }
            if (length <= 0)
            {
                return(0);
            }

            BezierSubdivision baseSubdivision          = null;
            LinkedListNode <BezierSubdivision> current = null;
            double l     = 0;
            double lnext = 0;

            while (length > lnext)
            {
                current = current?.Next;
                if (current == null)
                {
                    baseSubdivision = baseSubdivision == null ? this : baseSubdivision.Next();
                    var pathApproximation = new LinkedList <BezierSubdivision>();
                    pathApproximation.AddLast(baseSubdivision);
                    Subdivide(ref pathApproximation, tolerance);
                    current = pathApproximation.First;
                }
                l      = lnext;
                lnext += current.Value.ApproximationLength();
            }

            var curr = current.Value;

            while (curr.ApproximationLength() > precision)
            {
                curr.Children(out var left, out var right);
                lnext = l + left.ApproximationLength();
                if (length > lnext)
                {
                    curr = right;
                    l    = lnext;
                }
                else
                {
                    curr = left;
                }
            }

            return((curr.Index + (length - l) / curr.ApproximationLength()) / (1 << curr.Level));
        }
Exemple #6
0
        public BezierSubdivision Parent() // Parent subdivision (inverse of BezierSubdivide)
        {
            var parent = new BezierSubdivision(new List <Vector2>(Points), Level - 1, Index >> 1);

            if ((Index & 1) == 0)
            {
                parent.ScaleRight(2);
            }
            else
            {
                parent.ScaleLeft(-1);
            }
            return(parent);
        }
Exemple #7
0
        public void Children(out BezierSubdivision leftChild, out BezierSubdivision rightChild) // Child subdivisions (BezierSubdivide)
        {
            var left  = new List <Vector2>(Points);
            var right = new List <Vector2>(Points);

            for (int j = 0; j < Order; j++)
            {
                for (int i = Order; i > j; i--)
                {
                    left[i]          = (left[i] + left[i - 1]) / 2;
                    right[Order - i] = (right[Order - i] + right[Order - i + 1]) / 2;
                }
            }
            leftChild  = new BezierSubdivision(left, Level + 1, Index << 1);
            rightChild = new BezierSubdivision(right, Level + 1, Index << 1 | 1);
        }
        public static IEnumerable <BezierSubdivision> ChopAnchors(List <Vector2> anchors)
        {
            int start = 0;
            int end   = 0;

            for (int i = 0; i < anchors.Count; i++)
            {
                end++;

                if (i != anchors.Count - 1 && anchors[i] != anchors[i + 1] || i == anchors.Count - 2)
                {
                    continue;
                }

                var cpSpan      = anchors.GetRange(start, end - start);
                var subdivision = new BezierSubdivision(cpSpan);

                yield return(subdivision);

                start = end;
            }
        }
        public static double CalculatePathLength(List <Vector2> anchors)
        {
            double length = 0;

            int start = 0;
            int end   = 0;

            for (int i = 0; i < anchors.Length(); ++i)
            {
                end++;

                if (i == anchors.Length() - 1 || anchors[i] == anchors[i + 1])
                {
                    List <Vector2> cpSpan = anchors.GetRange(start, end - start);

                    length += new BezierSubdivision(cpSpan).SubdividedApproximationLength();

                    start = end;
                }
            }

            return(length);
        }
        public static List <Vector2> MoveAnchorsToLength(List <Vector2> anchors, PathType pathType, double newLength, out PathType newPathType)
        {
            var newAnchors    = new List <Vector2>();
            var sliderPath    = new SliderPath(pathType, anchors.ToArray(), newLength);
            var maxSliderPath = new SliderPath(pathType, anchors.ToArray());

            if (newLength > maxSliderPath.Distance)
            {
                // Extend linearly
                switch (pathType)
                {
                case PathType.Bezier:
                    newPathType = PathType.Bezier;
                    newAnchors.AddRange(anchors);

                    if (newAnchors.Count > 1 && newAnchors[newAnchors.Count - 2] == newAnchors[newAnchors.Count - 1])
                    {
                        newAnchors[newAnchors.Count - 2] = newAnchors[newAnchors.Count - 2] + Vector2.UnitX;
                    }

                    newAnchors.Add(anchors.Last());
                    newAnchors.Add(sliderPath.PositionAt(1));
                    break;

                case PathType.Catmull:
                case PathType.PerfectCurve:
                    // Convert to bezier and then extend
                    newPathType = PathType.Bezier;
                    newAnchors  = BezierConverter.ConvertToBezier(sliderPath).ControlPoints;
                    newAnchors.Add(anchors.Last());
                    newAnchors.Add(sliderPath.PositionAt(1));
                    break;

                default:
                    newPathType = pathType;
                    newAnchors.AddRange(anchors);
                    newAnchors[newAnchors.Count - 1] = sliderPath.PositionAt(1);
                    break;
                }
            }
            else
            {
                switch (sliderPath.Type)
                {
                case PathType.Catmull:
                case PathType.Bezier:
                    newPathType = PathType.Bezier;

                    // Convert in case the path type is catmull
                    var convert = BezierConverter.ConvertToBezier(sliderPath).ControlPoints;

                    // Find the last bezier segment and the pixel length at that part
                    BezierSubdivision subdivision = null;
                    double            totalLength = 0;

                    foreach (var bezierSubdivision in ChopAnchors(convert))
                    {
                        subdivision = bezierSubdivision;
                        var length = bezierSubdivision.SubdividedApproximationLength();

                        if (totalLength + length > newLength)
                        {
                            break;
                        }

                        totalLength += length;
                        newAnchors.AddRange(bezierSubdivision.Points);
                    }

                    if (subdivision == null)
                    {
                        break;
                    }

                    // Find T for the remaining pixel length
                    var t = subdivision.LengthToT(newLength - totalLength);

                    // ScaleRight the BezierSubdivision so the anchors end at T
                    subdivision.ScaleRight(t);

                    // Add the scaled anchors
                    newAnchors.AddRange(subdivision.Points);
                    break;

                case PathType.PerfectCurve:
                    newPathType = PathType.PerfectCurve;
                    newAnchors.AddRange(anchors);
                    newAnchors[1] = sliderPath.PositionAt(0.5);
                    newAnchors[2] = sliderPath.PositionAt(1);
                    break;

                default:
                    newPathType = pathType;
                    if (anchors.Count > 2)
                    {
                        // Find the section of the linear slider which contains the slider end
                        totalLength = 0;
                        foreach (var bezierSubdivision in ChopAnchorsLinear(anchors))
                        {
                            newAnchors.Add(bezierSubdivision.Points[0]);
                            var length = bezierSubdivision.Length();

                            if (totalLength + length > newLength)
                            {
                                break;
                            }

                            totalLength += length;
                        }
                        newAnchors.Add(sliderPath.PositionAt(1));
                    }
                    else
                    {
                        newAnchors.AddRange(anchors);
                        newAnchors[newAnchors.Count - 1] = sliderPath.PositionAt(1);
                    }
                    break;
                }
            }

            return(newAnchors);
        }