示例#1
0
        /// <summary>
        /// Creates a list of CubicBezierKnots automatically generating control points so the path is C1 and C2 continous,
        /// (meaning the tangent and acceleration at the knots in the path are the same). This method uses the
        /// Kochanek-Bartels algorithm.
        /// </summary>
        /// <returns>The a list of bezier knots.</returns>
        /// <param name="looped">If set to <c>true</c> the curve will be looped.</param>
        /// <param name="points">The positions os the knots, in order.</param>
        /// <param name="tension">
        /// A tension of 0 indicates smooth corners. A tension of 1 indicates sharp corners. Values larger than
        /// this range can cause the curves to overshoot. Values smaller than 0 will cause the curves to fold into
        /// themselves.
        /// </param>
        /// <summary>
        /// Bias affects how automatic control points should be generated. For a given knot, a positive bias means there
        /// will be a strong bend at the start of the outgoing segment, and a negative bias means there will be a strong
        /// bend at the end of the incomming segment. The default bias of 0 gives a neutral weighting.
        /// </summary>
        /// <summary>
        /// Continuity affects how automatic control points should be generated. If this value is 0, control points will
        /// be C1 continious, (no instant changes in velocity, unless tension is >= 1). This can be though to change how
        /// sharp the shape is.
        /// </summary>
        /// <param name="relativeControlPoints">
        /// Should control points be calculated as offsets from the knot position, or as absolute values in the path's
        /// coordinate system.
        /// </param>
        public static List <CubicBezierKnot> GeneratePathKnots(bool looped, IEnumerable <Vector3> points,
                                                               float tension = 0f, float bias = 0f, float continuity = 0f, bool relativeControlPoints = false)
        {
            Vector3[] pointsArray = points.ToArray();

            List <CubicBezierKnot> knots = new List <CubicBezierKnot>();

            // Special case, for if the path is one point or less.
            if (pointsArray.Length < 2)
            {
                foreach (var point in points)
                {
                    var knot = new CubicBezierKnot();
                    knot.InControlPoint  = point;
                    knot.OutControlPoint = point;
                    knot.Position        = point;
                    knot.Type            = CubicBezierKnotType.Singular;
                }
                return(knots);
            }

            for (int i = 0; i < pointsArray.Length; ++i)
            {
                int prevIndex = i - 1;
                // Wrap the prevIndex around if the path is looped. Otherwise clamp it to zero.
                if (prevIndex < 0)
                {
                    prevIndex = looped ? prevIndex + pointsArray.Length : 0;
                }
                // Wrap the nextIndex around if the path is looped. Otherwise clamp it.
                int     nextIndex    = looped ? (i + 1) % pointsArray.Length : Mathf.Min(i + 1, pointsArray.Length - 1);
                Vector3 prevPoint    = pointsArray[prevIndex];
                Vector3 currentPoint = pointsArray[i % pointsArray.Length];
                Vector3 nextPoint    = pointsArray[nextIndex];

                // Explanation of how we generate the midpoints can be found here:
                // https://en.wikipedia.org/wiki/Kochanek%E2%80%93Bartels_spline

                var inControlPoint = -(
                    0.5f * (1f - tension) * (1f + bias) * (1f - continuity) * (currentPoint - prevPoint) +
                    0.5f * (1f - tension) * (1f - bias) * (1f + continuity) * (nextPoint - currentPoint));
                var outControlPoint =
                    0.5f * (1f - tension) * (1f + bias) * (1f + continuity) * (currentPoint - prevPoint) +
                    0.5f * (1f - tension) * (1f - bias) * (1f - continuity) * (nextPoint - currentPoint);

                if (!relativeControlPoints)
                {
                    inControlPoint  += currentPoint;
                    outControlPoint += currentPoint;
                }

                var knot = new CubicBezierKnot();
                knot.Position        = currentPoint;
                knot.InControlPoint  = inControlPoint;
                knot.OutControlPoint = outControlPoint;
                knot.Type            = CubicBezierKnotType.Continuous;
                knots.Add(knot);
            }
            return(knots);
        }
示例#2
0
        /// <summary>
        /// Creates an automatic bezier path between the given list of points. If tension, bias and continuity are all 0,
        /// This path will be a catmull-rom spline.
        /// </summary>
        /// <param name="tension">
        /// A tension of 0 indicates smooth corners. A tension of 1 indicates sharp corners. Values larger than
        /// this range can cause the curves to overshoot. Values smaller than 0 will cause the curves to fold into
        /// themselves.
        /// </param>
        /// <summary>
        /// Bias affects how automatic control points should be generated. For a given knot, a positive bias means there
        /// will be a strong bend at the start of the outgoing segment, and a negative bias means there will be a strong
        /// bend at the end of the incomming segment. The default bias of 0 gives a neutral weighting.
        /// </summary>
        /// <summary>
        /// Continuity affects how automatic control points should be generated. If this value is 0, control points will
        /// be C1 continious, (no instant changes in velocity, unless tension is >= 1). This can be though to change how
        /// sharp the shape is.
        /// </summary>
        /// <param name="looped">
        /// Whether the path should be looped or not.
        /// </param>
        /// <param name="overshootMode">
        /// How path overshooting should be handled.
        /// </param>
        /// <param name="points">The points the bezier should map through.</param>
        public AutoBezierPath(float tension, float bias, float continuity, bool looped, PathOvershootMode overshootMode,
                              IEnumerable <Vector3> points)
        {
            List <CubicBezierKnot> knots = CubicBezierKnotUtils.GeneratePathKnots(looped, points, tension, bias, continuity);
            var paths = new List <IPath>();

            int numberOfPoints = points.Count();
            int count          = looped ? numberOfPoints : numberOfPoints - 1;

            for (int i = 0; i < count; ++i)
            {
                int             index     = i;
                int             nextIndex = (i + 1) % numberOfPoints;
                CubicBezierKnot firstKnot = knots[index];
                CubicBezierKnot nextKnot  = knots[nextIndex];

                var path = new NormalizedBezierPath(firstKnot.Position, firstKnot.OutControlPoint, nextKnot.InControlPoint,
                                                    nextKnot.Position);
                paths.Add(path);
            }
            _path = new ChainedPath(overshootMode, paths);
        }
示例#3
0
        /// <summary>
        /// Takes an existing knot, and forces it's control points to be recalculated based on the CubicBezierKnotType.
        /// </summary>
        /// <param name="knot">The knot to recalculate.</param>
        /// <param name="inControlFixed">
        /// If set to <c>true</c>, the in control point will be considered fixed and the out control point will be
        /// recalculated. Otherwise the out control point will be considered fixed and the int control point will be
        /// recalculated.
        /// </param>
        /// <param name="relativeControlPoints">
        /// Should control points be calculated as offsets from the knot position, or as absolute values in the path's
        /// coordinate system.
        /// </param>
        public static void RecalculateControlPoint(this CubicBezierKnot knot, bool inControlFixed = true,
                                                   bool relativeControlPoints = false)
        {
            Vector3 fixedControlPoint = inControlFixed ? knot.InControlPoint : knot.OutControlPoint;
            Vector3 freeControlPoint  = inControlFixed ? knot.OutControlPoint : knot.InControlPoint;

            if (!relativeControlPoints)
            {
                fixedControlPoint -= knot.Position;
                freeControlPoint  -= knot.Position;
            }

            switch (knot.Type)
            {
            case CubicBezierKnotType.Continuous:
                freeControlPoint = -fixedControlPoint;
                break;

            case CubicBezierKnotType.Aligned:
                Vector3 tangent   = -fixedControlPoint.normalized;
                float   magnitude = freeControlPoint.magnitude;
                freeControlPoint = tangent * magnitude;
                break;

            case CubicBezierKnotType.Singular:
                fixedControlPoint = Vector3.zero;
                freeControlPoint  = Vector3.zero;
                break;
            }

            if (!relativeControlPoints)
            {
                fixedControlPoint += knot.Position;
                freeControlPoint  += knot.Position;
            }

            knot.InControlPoint  = inControlFixed ? fixedControlPoint : freeControlPoint;
            knot.OutControlPoint = inControlFixed ? freeControlPoint : fixedControlPoint;
        }
示例#4
0
        /// <summary>
        /// Gets the path of a segment. A segment is any curve connecting two knots in the path.
        /// </summary>
        /// <returns>The path segment.</returns>
        /// <param name="segmentIndex">
        /// The index of the path segment. Must be less than NumberOfSegments.
        /// </param>
        public IPath GetSegmentPath(int segmentIndex)
        {
            Assert.IsTrue((Looped && segmentIndex <= _knots.Count) || (!Looped && segmentIndex < _knots.Count));

            int nextIndex = (segmentIndex + 1) % _knots.Count;

            CubicBezierKnot firstPoint  = _knots[segmentIndex];
            CubicBezierKnot secondPoint = _knots[nextIndex];

            var startPoint        = firstPoint.Position;
            var startControlPoint = firstPoint.OutControlPoint + firstPoint.Position;
            var endControlPoint   = secondPoint.InControlPoint + secondPoint.Position;
            var endPoint          = secondPoint.Position;

            if (Normalized)
            {
                return(new NormalizedBezierPath(startPoint, startControlPoint, endControlPoint, endPoint));
            }
            else
            {
                return(new BezierPath(startPoint, startControlPoint, endControlPoint, endPoint));
            }
        }