| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 | using System.Collections.Generic;using UnityEngine;namespace FairyGUI{    /// <summary>    ///     /// </summary>    [System.Serializable]    public struct GPathPoint    {        /// <summary>        ///         /// </summary>        public Vector3 pos;        /// <summary>        ///         /// </summary>        public Vector3 control1;        /// <summary>        ///         /// </summary>        public Vector3 control2;        /// <summary>        ///         /// </summary>        public CurveType curveType;        /// <summary>        ///         /// </summary>        public bool smooth;        /// <summary>        ///         /// </summary>        public enum CurveType        {            CRSpline,            Bezier,            CubicBezier,            Straight        }        /// <summary>        ///         /// </summary>        /// <param name="pos"></param>        public GPathPoint(Vector3 pos)        {            this.pos = pos;            this.control1 = Vector3.zero;            this.control2 = Vector3.zero;            this.curveType = CurveType.CRSpline;            this.smooth = true;        }        /// <summary>        ///         /// </summary>        /// <param name="pos"></param>        /// <param name="control"></param>        public GPathPoint(Vector3 pos, Vector3 control)        {            this.pos = pos;            this.control1 = control;            this.control2 = Vector3.zero;            this.curveType = CurveType.Bezier;            this.smooth = true;        }        /// <summary>        ///         /// </summary>        /// <param name="pos"></param>        /// <param name="control1"></param>        /// <param name="control2"></param>        public GPathPoint(Vector3 pos, Vector3 control1, Vector3 control2)        {            this.pos = pos;            this.control1 = control1;            this.control2 = control2;            this.curveType = CurveType.CubicBezier;            this.smooth = true;        }        /// <summary>        ///         /// </summary>        /// <param name="pos"></param>        /// <param name="curveType"></param>        public GPathPoint(Vector3 pos, CurveType curveType)        {            this.pos = pos;            this.control1 = Vector3.zero;            this.control2 = Vector3.zero;            this.curveType = curveType;            this.smooth = true;        }    }    /// <summary>    ///     /// </summary>    public class GPath    {        protected struct Segment        {            public GPathPoint.CurveType type;            public float length;            public int ptStart;            public int ptCount;        }        protected List<Segment> _segments;        protected List<Vector3> _points;        protected float _fullLength;        static List<GPathPoint> helperList = new List<GPathPoint>();        static List<Vector3> splinePoints = new List<Vector3>();        public GPath()        {            _segments = new List<Segment>();            _points = new List<Vector3>();        }        /// <summary>        ///         /// </summary>        public float length        {            get { return _fullLength; }        }        /// <summary>        ///         /// </summary>        /// <param name="pt1"></param>        /// <param name="pt2"></param>        public void Create(GPathPoint pt1, GPathPoint pt2)        {            helperList.Clear();            helperList.Add(pt1);            helperList.Add(pt2);            Create(helperList);        }        /// <summary>        ///         /// </summary>        /// <param name="pt1"></param>        /// <param name="pt2"></param>        /// <param name="pt3"></param>        public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3)        {            helperList.Clear();            helperList.Add(pt1);            helperList.Add(pt2);            helperList.Add(pt3);            Create(helperList);        }        /// <summary>        ///         /// </summary>        /// <param name="pt1"></param>        /// <param name="pt2"></param>        /// <param name="pt3"></param>        /// <param name="pt4"></param>        public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3, GPathPoint pt4)        {            helperList.Clear();            helperList.Add(pt1);            helperList.Add(pt2);            helperList.Add(pt3);            helperList.Add(pt4);            Create(helperList);        }        /// <summary>        ///         /// </summary>        /// <param name="points"></param>        public void Create(IEnumerable<GPathPoint> points)        {            _segments.Clear();            _points.Clear();            splinePoints.Clear();            _fullLength = 0;            var et = points.GetEnumerator();            if (!et.MoveNext())                return;            GPathPoint prev = et.Current;            if (prev.curveType == GPathPoint.CurveType.CRSpline)                splinePoints.Add(prev.pos);            while (et.MoveNext())            {                GPathPoint current = et.Current;                if (prev.curveType != GPathPoint.CurveType.CRSpline)                {                    Segment seg = new Segment();                    seg.type = prev.curveType;                    seg.ptStart = _points.Count;                    if (prev.curveType == GPathPoint.CurveType.Straight)                    {                        seg.ptCount = 2;                        _points.Add(prev.pos);                        _points.Add(current.pos);                    }                    else if (prev.curveType == GPathPoint.CurveType.Bezier)                    {                        seg.ptCount = 3;                        _points.Add(prev.pos);                        _points.Add(current.pos);                        _points.Add(prev.control1);                    }                    else if (prev.curveType == GPathPoint.CurveType.CubicBezier)                    {                        seg.ptCount = 4;                        _points.Add(prev.pos);                        _points.Add(current.pos);                        _points.Add(prev.control1);                        _points.Add(prev.control2);                    }                    seg.length = Vector3.Distance(prev.pos, current.pos);                    _fullLength += seg.length;                    _segments.Add(seg);                }                if (current.curveType != GPathPoint.CurveType.CRSpline)                {                    if (splinePoints.Count > 0)                    {                        splinePoints.Add(current.pos);                        CreateSplineSegment();                    }                }                else                    splinePoints.Add(current.pos);                prev = current;            }            if (splinePoints.Count > 1)                CreateSplineSegment();        }        void CreateSplineSegment()        {            int cnt = splinePoints.Count;            splinePoints.Insert(0, splinePoints[0]);            splinePoints.Add(splinePoints[cnt]);            splinePoints.Add(splinePoints[cnt]);            cnt += 3;            Segment seg = new Segment();            seg.type = GPathPoint.CurveType.CRSpline;            seg.ptStart = _points.Count;            seg.ptCount = cnt;            _points.AddRange(splinePoints);            seg.length = 0;            for (int i = 1; i < cnt; i++)                seg.length += Vector3.Distance(splinePoints[i - 1], splinePoints[i]);            _fullLength += seg.length;            _segments.Add(seg);            splinePoints.Clear();        }        /// <summary>        ///         /// </summary>        public void Clear()        {            _segments.Clear();            _points.Clear();        }        /// <summary>        ///         /// </summary>        /// <param name="t"></param>        /// <returns></returns>        public Vector3 GetPointAt(float t)        {            t = Mathf.Clamp01(t);            int cnt = _segments.Count;            if (cnt == 0)                return Vector3.zero;            Segment seg;            if (t == 1)            {                seg = _segments[cnt - 1];                if (seg.type == GPathPoint.CurveType.Straight)                    return Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t);                else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)                    return onBezierCurve(seg.ptStart, seg.ptCount, t);                else                    return onCRSplineCurve(seg.ptStart, seg.ptCount, t);            }            float len = t * _fullLength;            Vector3 pt = new Vector3();            for (int i = 0; i < cnt; i++)            {                seg = _segments[i];                len -= seg.length;                if (len < 0)                {                    t = 1 + len / seg.length;                    if (seg.type == GPathPoint.CurveType.Straight)                        pt = Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t);                    else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)                        pt = onBezierCurve(seg.ptStart, seg.ptCount, t);                    else                        pt = onCRSplineCurve(seg.ptStart, seg.ptCount, t);                    break;                }            }            return pt;        }        /// <summary>        ///         /// </summary>        public int segmentCount        {            get { return _segments.Count; }        }        /// <summary>        ///         /// </summary>        /// <param name="segmentIndex"></param>        /// <returns></returns>        public float GetSegmentLength(int segmentIndex)        {            return _segments[segmentIndex].length;        }        /// <summary>        ///         /// </summary>        /// <param name="segmentIndex"></param>        /// <param name="t0"></param>        /// <param name="t1"></param>        /// <param name="points"></param>        /// <param name="ts"></param>        public void GetPointsInSegment(int segmentIndex, float t0, float t1, List<Vector3> points, List<float> ts = null, float pointDensity = 0.1f)        {            if (points == null)                points = new List<Vector3>();            if (ts != null)                ts.Add(t0);            Segment seg = _segments[segmentIndex];            if (seg.type == GPathPoint.CurveType.Straight)            {                points.Add(Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t0));                points.Add(Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t1));            }            else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)            {                points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t0));                int SmoothAmount = (int)Mathf.Min(seg.length * pointDensity, 50);                for (int j = 0; j <= SmoothAmount; j++)                {                    float t = (float)j / SmoothAmount;                    if (t > t0 && t < t1)                    {                        points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t));                        if (ts != null)                            ts.Add(t);                    }                }                points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t1));            }            else            {                points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t0));                int SmoothAmount = (int)Mathf.Min(seg.length * pointDensity, 50);                for (int j = 0; j <= SmoothAmount; j++)                {                    float t = (float)j / SmoothAmount;                    if (t > t0 && t < t1)                    {                        points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t));                        if (ts != null)                            ts.Add(t);                    }                }                points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t1));            }            if (ts != null)                ts.Add(t1);        }        /// <summary>        ///         /// </summary>        /// <param name="points"></param>        public void GetAllPoints(List<Vector3> points, float pointDensity = 0.1f)        {            int cnt = _segments.Count;            for (int i = 0; i < cnt; i++)                GetPointsInSegment(i, 0, 1, points, null, pointDensity);        }        /// <summary>        /// Catmull rom spline implementation        /// by Stéphane Drouot, laei - http://games.laei.org        ///         /// Actual translation of math gebrish to C# credit is due to         /// Boon Cotter - http://www.booncotter.com/waypoints-catmull-rom-splines/        ///         /// This takes a list of vector3 (or an array) and gives a function called .onCurve(t)        /// returning a value on a Catmull-Rom spline for 0 <= t <= 1        /// </summary>        Vector3 onCRSplineCurve(int ptStart, int ptCount, float t)        {            int adjustedIndex = Mathf.FloorToInt(t * (ptCount - 4)) + ptStart; //Since the equation works with 4 points, we adjust the starting point depending on t to return a point on the specific segment            Vector3 result = new Vector3();            Vector3 p0 = _points[adjustedIndex];            Vector3 p1 = _points[adjustedIndex + 1];            Vector3 p2 = _points[adjustedIndex + 2];            Vector3 p3 = _points[adjustedIndex + 3];            float adjustedT = (t == 1f) ? 1f : Mathf.Repeat(t * (ptCount - 4), 1f); // Then we adjust t to be that value on that new piece of segment... for t == 1f don't use repeat (that would return 0f);            float t0 = ((-adjustedT + 2f) * adjustedT - 1f) * adjustedT * 0.5f;            float t1 = (((3f * adjustedT - 5f) * adjustedT) * adjustedT + 2f) * 0.5f;            float t2 = ((-3f * adjustedT + 4f) * adjustedT + 1f) * adjustedT * 0.5f;            float t3 = ((adjustedT - 1f) * adjustedT * adjustedT) * 0.5f;            result.x = p0.x * t0 + p1.x * t1 + p2.x * t2 + p3.x * t3;            result.y = p0.y * t0 + p1.y * t1 + p2.y * t2 + p3.y * t3;            result.z = p0.z * t0 + p1.z * t1 + p2.z * t2 + p3.z * t3;            return result;        }        Vector3 onBezierCurve(int ptStart, int ptCount, float t)        {            float t2 = 1f - t;            Vector3 p0 = _points[ptStart];            Vector3 p1 = _points[ptStart + 1];            Vector3 cp0 = _points[ptStart + 2];            if (ptCount == 4)            {                Vector3 cp1 = _points[ptStart + 3];                return t2 * t2 * t2 * p0 + 3f * t2 * t2 * t * cp0 + 3f * t2 * t * t * cp1 + t * t * t * p1;            }            else                return t2 * t2 * p0 + 2f * t2 * t * cp0 + t * t * p1;        }    }}
 |