using System.Collections.Generic;
using UnityEngine;
namespace FairyGUI
{
///
///
///
[System.Serializable]
public struct GPathPoint
{
///
///
///
public Vector3 pos;
///
///
///
public Vector3 control1;
///
///
///
public Vector3 control2;
///
///
///
public CurveType curveType;
///
///
///
public bool smooth;
///
///
///
public enum CurveType
{
CRSpline,
Bezier,
CubicBezier,
Straight
}
///
///
///
///
public GPathPoint(Vector3 pos)
{
this.pos = pos;
this.control1 = Vector3.zero;
this.control2 = Vector3.zero;
this.curveType = CurveType.CRSpline;
this.smooth = true;
}
///
///
///
///
///
public GPathPoint(Vector3 pos, Vector3 control)
{
this.pos = pos;
this.control1 = control;
this.control2 = Vector3.zero;
this.curveType = CurveType.Bezier;
this.smooth = true;
}
///
///
///
///
///
///
public GPathPoint(Vector3 pos, Vector3 control1, Vector3 control2)
{
this.pos = pos;
this.control1 = control1;
this.control2 = control2;
this.curveType = CurveType.CubicBezier;
this.smooth = true;
}
///
///
///
///
///
public GPathPoint(Vector3 pos, CurveType curveType)
{
this.pos = pos;
this.control1 = Vector3.zero;
this.control2 = Vector3.zero;
this.curveType = curveType;
this.smooth = true;
}
}
///
///
///
public class GPath
{
protected struct Segment
{
public GPathPoint.CurveType type;
public float length;
public int ptStart;
public int ptCount;
}
protected List _segments;
protected List _points;
protected float _fullLength;
static List helperList = new List();
static List splinePoints = new List();
public GPath()
{
_segments = new List();
_points = new List();
}
///
///
///
public float length
{
get { return _fullLength; }
}
///
///
///
///
///
public void Create(GPathPoint pt1, GPathPoint pt2)
{
helperList.Clear();
helperList.Add(pt1);
helperList.Add(pt2);
Create(helperList);
}
///
///
///
///
///
///
public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3)
{
helperList.Clear();
helperList.Add(pt1);
helperList.Add(pt2);
helperList.Add(pt3);
Create(helperList);
}
///
///
///
///
///
///
///
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);
}
///
///
///
///
public void Create(IEnumerable 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();
}
///
///
///
public void Clear()
{
_segments.Clear();
_points.Clear();
}
///
///
///
///
///
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;
}
///
///
///
public int segmentCount
{
get { return _segments.Count; }
}
///
///
///
///
///
public float GetSegmentLength(int segmentIndex)
{
return _segments[segmentIndex].length;
}
///
///
///
///
///
///
///
///
public void GetPointsInSegment(int segmentIndex, float t0, float t1, List points, List ts = null, float pointDensity = 0.1f)
{
if (points == null)
points = new List();
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);
}
///
///
///
///
public void GetAllPoints(List points, float pointDensity = 0.1f)
{
int cnt = _segments.Count;
for (int i = 0; i < cnt; i++)
GetPointsInSegment(i, 0, 1, points, null, pointDensity);
}
///
/// 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
///
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;
}
}
}