Example #1
0
 /// <summary>
 /// Returns the velocity of this bezier curve at <paramref name="t"/>
 /// </summary>
 /// <param name="t">Clamped to [0..1]</param>
 public Vector3 GetVelocity(float t)
 {
     t = Mathf.Clamp01(t);
     return(transform.TransformPoint(BezierMath.CubicGetFirstDerivative(points[0], points[1], points[2], points[3], t)) -
            transform.position); // Because it produces a velocity vector and not a point,
     // it should not be affected by the position of the curve, so we subtract that after transforming.
 }
Example #2
0
        /// <summary>
        /// Returns point on this bezier curve at <paramref name="t"/>
        /// </summary>
        public Vector3 GetPoint(float t)
        {
            int i;

            if (t >= 1f)
            {
                t = 1f;
                i = points.Length - 4;
            }
            else
            {
                // To get to the actual points, we have to multiply the curve index by three
                t  = Mathf.Clamp01(t) * CurveCount;
                i  = (int)t;
                t -= i;
                i *= 3;
            }
            return(transform.TransformPoint(BezierMath.CubicGetPoint(points[i], points[i + 1], points[i + 2], points[i + 3], t)));
        }
Example #3
0
        /// <summary>
        /// Returns velocity on this bezier curve at <paramref name="t"/>
        /// </summary>
        public Vector3 GetVelocity(float t)
        {
            int i;

            if (t >= 1f)
            {
                t = 1f;
                i = points.Length - 4;
            }
            else
            {
                t  = Mathf.Clamp01(t) * CurveCount;
                i  = (int)t;
                t -= i;
                i *= 3;
            }
            return(transform.TransformPoint(BezierMath.CubicGetFirstDerivative(points[i], points[i + 1], points[i + 2], points[i + 3], t)) -
                   transform.position); // Because it produces a velocity vector and not a point,
            // it should not be affected by the position of the curve, so we subtract that after transforming.
        }
Example #4
0
 /// <summary>
 /// Return point on this bezier curve at <paramref name="t"/>
 /// </summary>
 /// <param name="t">Clamped to [0..1]</param>
 public Vector3 GetPoint(float t)
 {
     t = Mathf.Clamp01(t);
     return(transform.TransformPoint(BezierMath.CubicGetPoint(points[0], points[1], points[2], points[3], t)));
 }
Example #5
0
        /// <summary>
        /// Shifts the spline with <paramref name="amount"/> distance and returns a new spline
        /// </summary>
        /// <param name="safeDist">
        /// The max distance between the middle of the curves and their extreme points.
        /// If your shifted curve has too many sharp edges try pumping this number up.
        /// But beware because it causes it to have more points and thus decreasing performance.
        /// </param>
        /// <param name="newSpline">If you want the returned spline to be put into an already existing spline assign this parameter.</param>
        public BezierSpline GetShiftedSpline(float amount, float safeDist = 0.3f, BezierSpline newSpline = null)
        {
            BezierSpline _shiftedSpline;

            if (newSpline != null)
            {
                _shiftedSpline = newSpline;
            }
            else
            {
                _shiftedSpline = new GameObject().AddComponent <BezierSpline>();
            }
            _shiftedSpline.Clear();
            _shiftedSpline.name = "Lane" + amount;

            // So, you cannot offset a Bézier curve perfectly with another Bézier curve, no matter how high-order you make that
            // other Bézier curve. However, we can chop up a curve into "safe" sub-curves (where safe means that all the control
            // points are always on a single side of the baseline, and the midpoint of the curve at t=0.5 is roughly in the centre
            // of the polygon defined by the curve coordinates) and then point-scale each sub-curve with respect to its scaling
            // origin (which is the intersection of the point normals at the start and end points).

            // A good way to do this reduction is to first find the curve's extreme points, and use these as initial splitting points.
            // After this initial split, we can check each individual segment to see if it's "safe enough" based on where the
            // center of the curve is.If the on-curve point for t = 0.5 is too far off from the center, we simply split the
            // segment down the middle. Generally this is more than enough to end up with safe segments.

            Vector3          _p0 = points[0];
            Vector3          _p1, _p2, _p3;
            List <Vector3[]> _newPointsSpline = new List <Vector3[]>();

            for (int i = 3; i < PointCount; i += 3)
            {
                _p1 = points[i - 2];
                _p2 = points[i - 1];
                _p3 = points[i];

                // roots of our cubic bezier curve to find extremes
                SortedSet <float> _solutions = new SortedSet <float>();
                {
                    float?_sol1, _sol2;

                    BezierMath.CubicGetRoots(_p0.x, _p1.x, _p2.x, _p3.x, out _sol1, out _sol2);
                    if (_sol1.HasValue && _sol1.Value < 1 && _sol1.Value > 0)
                    {
                        _solutions.Add(_sol1.Value);
                    }
                    if (_sol2.HasValue && _sol2.Value < 1 && _sol2.Value > 0)
                    {
                        _solutions.Add(_sol2.Value);
                    }

                    BezierMath.CubicGetRoots(_p0.y, _p1.y, _p2.y, _p3.y, out _sol1, out _sol2);
                    if (_sol1.HasValue && _sol1.Value < 1 && _sol1.Value > 0)
                    {
                        _solutions.Add(_sol1.Value);
                    }
                    if (_sol2.HasValue && _sol2.Value < 1 && _sol2.Value > 0)
                    {
                        _solutions.Add(_sol2.Value);
                    }

                    BezierMath.CubicGetRoots(_p0.z, _p1.z, _p2.z, _p3.z, out _sol1, out _sol2);
                    if (_sol1.HasValue && _sol1.Value < 1 && _sol1.Value > 0)
                    {
                        _solutions.Add(_sol1.Value);
                    }
                    if (_sol2.HasValue && _sol2.Value < 1 && _sol2.Value > 0)
                    {
                        _solutions.Add(_sol2.Value);
                    }
                }

                // the list of new points of the offset curve
                List <Vector3[]> _newPointsCurve = new List <Vector3[]>();

                // So we're gonna need to split the starting curve at potentially more points
                // We do that by splitting in order and always taking the second split curve as the next
                // starting curve
                {
                    Vector3[] _first, _second = new Vector3[] { _p0, _p1, _p2, _p3 };
                    float     _secondSize     = 1f; // we need this to know how big the second curve is compared to the very first starting curve
                                                    // so we can split at the right point
                    foreach (float sol in _solutions)
                    {
                        BezierMath.CubicSplitCurve(_second[0], _second[1], _second[2], _second[3], _secondSize * sol, out _first, out _second);

                        _secondSize *= (1f - sol);
                        _newPointsCurve.Add(_first);
                    }
                    _newPointsCurve.Add(_second); // no more calculations with the second curve, so we can add it to the list
                }

                // check whether each bezier curve is safe or not if not split it
                {
                    int _at = _newPointsCurve.Count - 1;

                    while (_at >= 0)
                    {
                        Vector3[] _split1, _split2;
                        // where the curv t = 0.5f
                        Vector3 _zeroDotFive = BezierMath.CubicGetPoint(_newPointsCurve[_at][0], _newPointsCurve[_at][1],
                                                                        _newPointsCurve[_at][2], _newPointsCurve[_at][3], 0.5f);
                        // the center of the curve's 4 points
                        Vector3 _center = (_newPointsCurve[_at][0] + _newPointsCurve[_at][1] + _newPointsCurve[_at][2] + _newPointsCurve[_at][3]) / 4f;

                        // if they are too far away
                        if (Vector3.Distance(_zeroDotFive, _center) > safeDist)
                        {
                            // split curva at 0.5f
                            BezierMath.CubicSplitCurve(_newPointsCurve[_at][0], _newPointsCurve[_at][1], _newPointsCurve[_at][2], _newPointsCurve[_at][3], 0.5f,
                                                       out _split1, out _split2);

                            // overwrite current curve
                            _newPointsCurve[_at] = _split2;
                            // add one before it
                            _newPointsCurve.Insert(_at, _split1);
                            // we need to check the _split too again as well, so add one to at
                            _at++;
                        }
                        else
                        {
                            // no problem with this curve, move on
                            _at--;
                        }
                    }
                }

                // scale
                {
                    Vector3 _pivot;

                    for (int k = 0; k < _newPointsCurve.Count; k++)
                    {
                        // get pivot -> the intersection of the point normals at the start and end points
                        bool _doIntersect = Math3D.LineLineIntersection(
                            out _pivot,
                            _newPointsCurve[k][0],
                            (Quaternion.LookRotation(Vector3.right) * BezierMath.CubicGetFirstDerivative(_newPointsCurve[k][0], _newPointsCurve[k][1], _newPointsCurve[k][2], _newPointsCurve[k][3], 0.01f)).normalized,
                            _newPointsCurve[k][3],
                            (Quaternion.LookRotation(Vector3.right) * BezierMath.CubicGetFirstDerivative(_newPointsCurve[k][0], _newPointsCurve[k][1], _newPointsCurve[k][2], _newPointsCurve[k][3], 0.99f)).normalized
                            );

                        // scaling
                        if (!_doIntersect)   // it's essentially a line
                        // which way the right is from the line
                        {
                            Vector3 _whichWay = Vector3.Cross(_newPointsCurve[k][3] - _newPointsCurve[k][0], Vector3.up).normalized;

                            for (int j = 0; j < _newPointsCurve[k].Length; j++)
                            {
                                _newPointsCurve[k][j] -= _whichWay * amount;
                            }
                        }
                        else
                        {
                            // cache this
                            Vector3 _middleDerivative = BezierMath.CubicGetFirstDerivative(
                                _newPointsCurve[k][0],
                                _newPointsCurve[k][1],
                                _newPointsCurve[k][2],
                                _newPointsCurve[k][3], 0.5f);

                            // which way is the middle point's right
                            Vector3 _curveMiddleRight = (Quaternion.LookRotation(Vector3.right) * _middleDerivative).normalized;
                            // which way is the middle point's left
                            Vector3 _curveMiddleLeft = (Quaternion.LookRotation(Vector3.left) * _middleDerivative).normalized;

                            for (int j = 0; j < _newPointsCurve[k].Length; j++)
                            {
                                Vector3 _scaleDir = (_pivot - _newPointsCurve[k][j]).normalized;

                                // pivot is to the right
                                bool _isPivotToRight = Vector3.Angle(_scaleDir, _curveMiddleRight) <
                                                       Vector3.Angle(_scaleDir, _curveMiddleLeft);

                                // move to position based on pivot direction
                                if (_isPivotToRight)
                                {
                                    _newPointsCurve[k][j] += _scaleDir * amount;
                                }
                                else
                                {
                                    _newPointsCurve[k][j] -= _scaleDir * amount;
                                }
                            }
                        }
                    }
                }

                _newPointsSpline.AddRange(_newPointsCurve);

                _p0 = _p3;
            }

            _shiftedSpline.points    = new Vector3[_newPointsSpline.Count * 3 + 1];
            _shiftedSpline.points[0] = _newPointsSpline[0][0];

            _shiftedSpline.modes    = new BezierControlPointMode[_newPointsSpline.Count + 1];
            _shiftedSpline.modes[0] = BezierControlPointMode.Free;

            for (int i = 0; i < _newPointsSpline.Count; i++)
            {
                _shiftedSpline.modes[i + 1] = BezierControlPointMode.Free;

                for (int k = 1; k <= 3; k++)
                {
                    _shiftedSpline.points[i * 3 + k] = _newPointsSpline[i][k];
                }
            }

            return(_shiftedSpline);
        }