GPath.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. namespace FairyGUI
  4. {
  5. /// <summary>
  6. ///
  7. /// </summary>
  8. [System.Serializable]
  9. public struct GPathPoint
  10. {
  11. /// <summary>
  12. ///
  13. /// </summary>
  14. public Vector3 pos;
  15. /// <summary>
  16. ///
  17. /// </summary>
  18. public Vector3 control1;
  19. /// <summary>
  20. ///
  21. /// </summary>
  22. public Vector3 control2;
  23. /// <summary>
  24. ///
  25. /// </summary>
  26. public CurveType curveType;
  27. /// <summary>
  28. ///
  29. /// </summary>
  30. public bool smooth;
  31. /// <summary>
  32. ///
  33. /// </summary>
  34. public enum CurveType
  35. {
  36. CRSpline,
  37. Bezier,
  38. CubicBezier,
  39. Straight
  40. }
  41. /// <summary>
  42. ///
  43. /// </summary>
  44. /// <param name="pos"></param>
  45. public GPathPoint(Vector3 pos)
  46. {
  47. this.pos = pos;
  48. this.control1 = Vector3.zero;
  49. this.control2 = Vector3.zero;
  50. this.curveType = CurveType.CRSpline;
  51. this.smooth = true;
  52. }
  53. /// <summary>
  54. ///
  55. /// </summary>
  56. /// <param name="pos"></param>
  57. /// <param name="control"></param>
  58. public GPathPoint(Vector3 pos, Vector3 control)
  59. {
  60. this.pos = pos;
  61. this.control1 = control;
  62. this.control2 = Vector3.zero;
  63. this.curveType = CurveType.Bezier;
  64. this.smooth = true;
  65. }
  66. /// <summary>
  67. ///
  68. /// </summary>
  69. /// <param name="pos"></param>
  70. /// <param name="control1"></param>
  71. /// <param name="control2"></param>
  72. public GPathPoint(Vector3 pos, Vector3 control1, Vector3 control2)
  73. {
  74. this.pos = pos;
  75. this.control1 = control1;
  76. this.control2 = control2;
  77. this.curveType = CurveType.CubicBezier;
  78. this.smooth = true;
  79. }
  80. /// <summary>
  81. ///
  82. /// </summary>
  83. /// <param name="pos"></param>
  84. /// <param name="curveType"></param>
  85. public GPathPoint(Vector3 pos, CurveType curveType)
  86. {
  87. this.pos = pos;
  88. this.control1 = Vector3.zero;
  89. this.control2 = Vector3.zero;
  90. this.curveType = curveType;
  91. this.smooth = true;
  92. }
  93. }
  94. /// <summary>
  95. ///
  96. /// </summary>
  97. public class GPath
  98. {
  99. protected struct Segment
  100. {
  101. public GPathPoint.CurveType type;
  102. public float length;
  103. public int ptStart;
  104. public int ptCount;
  105. }
  106. protected List<Segment> _segments;
  107. protected List<Vector3> _points;
  108. protected float _fullLength;
  109. static List<GPathPoint> helperList = new List<GPathPoint>();
  110. static List<Vector3> splinePoints = new List<Vector3>();
  111. public GPath()
  112. {
  113. _segments = new List<Segment>();
  114. _points = new List<Vector3>();
  115. }
  116. /// <summary>
  117. ///
  118. /// </summary>
  119. public float length
  120. {
  121. get { return _fullLength; }
  122. }
  123. /// <summary>
  124. ///
  125. /// </summary>
  126. /// <param name="pt1"></param>
  127. /// <param name="pt2"></param>
  128. public void Create(GPathPoint pt1, GPathPoint pt2)
  129. {
  130. helperList.Clear();
  131. helperList.Add(pt1);
  132. helperList.Add(pt2);
  133. Create(helperList);
  134. }
  135. /// <summary>
  136. ///
  137. /// </summary>
  138. /// <param name="pt1"></param>
  139. /// <param name="pt2"></param>
  140. /// <param name="pt3"></param>
  141. public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3)
  142. {
  143. helperList.Clear();
  144. helperList.Add(pt1);
  145. helperList.Add(pt2);
  146. helperList.Add(pt3);
  147. Create(helperList);
  148. }
  149. /// <summary>
  150. ///
  151. /// </summary>
  152. /// <param name="pt1"></param>
  153. /// <param name="pt2"></param>
  154. /// <param name="pt3"></param>
  155. /// <param name="pt4"></param>
  156. public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3, GPathPoint pt4)
  157. {
  158. helperList.Clear();
  159. helperList.Add(pt1);
  160. helperList.Add(pt2);
  161. helperList.Add(pt3);
  162. helperList.Add(pt4);
  163. Create(helperList);
  164. }
  165. /// <summary>
  166. ///
  167. /// </summary>
  168. /// <param name="points"></param>
  169. public void Create(IEnumerable<GPathPoint> points)
  170. {
  171. _segments.Clear();
  172. _points.Clear();
  173. splinePoints.Clear();
  174. _fullLength = 0;
  175. var et = points.GetEnumerator();
  176. if (!et.MoveNext())
  177. return;
  178. GPathPoint prev = et.Current;
  179. if (prev.curveType == GPathPoint.CurveType.CRSpline)
  180. splinePoints.Add(prev.pos);
  181. while (et.MoveNext())
  182. {
  183. GPathPoint current = et.Current;
  184. if (prev.curveType != GPathPoint.CurveType.CRSpline)
  185. {
  186. Segment seg = new Segment();
  187. seg.type = prev.curveType;
  188. seg.ptStart = _points.Count;
  189. if (prev.curveType == GPathPoint.CurveType.Straight)
  190. {
  191. seg.ptCount = 2;
  192. _points.Add(prev.pos);
  193. _points.Add(current.pos);
  194. }
  195. else if (prev.curveType == GPathPoint.CurveType.Bezier)
  196. {
  197. seg.ptCount = 3;
  198. _points.Add(prev.pos);
  199. _points.Add(current.pos);
  200. _points.Add(prev.control1);
  201. }
  202. else if (prev.curveType == GPathPoint.CurveType.CubicBezier)
  203. {
  204. seg.ptCount = 4;
  205. _points.Add(prev.pos);
  206. _points.Add(current.pos);
  207. _points.Add(prev.control1);
  208. _points.Add(prev.control2);
  209. }
  210. seg.length = Vector3.Distance(prev.pos, current.pos);
  211. _fullLength += seg.length;
  212. _segments.Add(seg);
  213. }
  214. if (current.curveType != GPathPoint.CurveType.CRSpline)
  215. {
  216. if (splinePoints.Count > 0)
  217. {
  218. splinePoints.Add(current.pos);
  219. CreateSplineSegment();
  220. }
  221. }
  222. else
  223. splinePoints.Add(current.pos);
  224. prev = current;
  225. }
  226. if (splinePoints.Count > 1)
  227. CreateSplineSegment();
  228. }
  229. void CreateSplineSegment()
  230. {
  231. int cnt = splinePoints.Count;
  232. splinePoints.Insert(0, splinePoints[0]);
  233. splinePoints.Add(splinePoints[cnt]);
  234. splinePoints.Add(splinePoints[cnt]);
  235. cnt += 3;
  236. Segment seg = new Segment();
  237. seg.type = GPathPoint.CurveType.CRSpline;
  238. seg.ptStart = _points.Count;
  239. seg.ptCount = cnt;
  240. _points.AddRange(splinePoints);
  241. seg.length = 0;
  242. for (int i = 1; i < cnt; i++)
  243. seg.length += Vector3.Distance(splinePoints[i - 1], splinePoints[i]);
  244. _fullLength += seg.length;
  245. _segments.Add(seg);
  246. splinePoints.Clear();
  247. }
  248. /// <summary>
  249. ///
  250. /// </summary>
  251. public void Clear()
  252. {
  253. _segments.Clear();
  254. _points.Clear();
  255. }
  256. /// <summary>
  257. ///
  258. /// </summary>
  259. /// <param name="t"></param>
  260. /// <returns></returns>
  261. public Vector3 GetPointAt(float t)
  262. {
  263. t = Mathf.Clamp01(t);
  264. int cnt = _segments.Count;
  265. if (cnt == 0)
  266. return Vector3.zero;
  267. Segment seg;
  268. if (t == 1)
  269. {
  270. seg = _segments[cnt - 1];
  271. if (seg.type == GPathPoint.CurveType.Straight)
  272. return Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t);
  273. else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)
  274. return onBezierCurve(seg.ptStart, seg.ptCount, t);
  275. else
  276. return onCRSplineCurve(seg.ptStart, seg.ptCount, t);
  277. }
  278. float len = t * _fullLength;
  279. Vector3 pt = new Vector3();
  280. for (int i = 0; i < cnt; i++)
  281. {
  282. seg = _segments[i];
  283. len -= seg.length;
  284. if (len < 0)
  285. {
  286. t = 1 + len / seg.length;
  287. if (seg.type == GPathPoint.CurveType.Straight)
  288. pt = Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t);
  289. else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)
  290. pt = onBezierCurve(seg.ptStart, seg.ptCount, t);
  291. else
  292. pt = onCRSplineCurve(seg.ptStart, seg.ptCount, t);
  293. break;
  294. }
  295. }
  296. return pt;
  297. }
  298. /// <summary>
  299. ///
  300. /// </summary>
  301. public int segmentCount
  302. {
  303. get { return _segments.Count; }
  304. }
  305. /// <summary>
  306. ///
  307. /// </summary>
  308. /// <param name="segmentIndex"></param>
  309. /// <returns></returns>
  310. public float GetSegmentLength(int segmentIndex)
  311. {
  312. return _segments[segmentIndex].length;
  313. }
  314. /// <summary>
  315. ///
  316. /// </summary>
  317. /// <param name="segmentIndex"></param>
  318. /// <param name="t0"></param>
  319. /// <param name="t1"></param>
  320. /// <param name="points"></param>
  321. /// <param name="ts"></param>
  322. public void GetPointsInSegment(int segmentIndex, float t0, float t1, List<Vector3> points, List<float> ts = null, float pointDensity = 0.1f)
  323. {
  324. if (points == null)
  325. points = new List<Vector3>();
  326. if (ts != null)
  327. ts.Add(t0);
  328. Segment seg = _segments[segmentIndex];
  329. if (seg.type == GPathPoint.CurveType.Straight)
  330. {
  331. points.Add(Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t0));
  332. points.Add(Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t1));
  333. }
  334. else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)
  335. {
  336. points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t0));
  337. int SmoothAmount = (int)Mathf.Min(seg.length * pointDensity, 50);
  338. for (int j = 0; j <= SmoothAmount; j++)
  339. {
  340. float t = (float)j / SmoothAmount;
  341. if (t > t0 && t < t1)
  342. {
  343. points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t));
  344. if (ts != null)
  345. ts.Add(t);
  346. }
  347. }
  348. points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t1));
  349. }
  350. else
  351. {
  352. points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t0));
  353. int SmoothAmount = (int)Mathf.Min(seg.length * pointDensity, 50);
  354. for (int j = 0; j <= SmoothAmount; j++)
  355. {
  356. float t = (float)j / SmoothAmount;
  357. if (t > t0 && t < t1)
  358. {
  359. points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t));
  360. if (ts != null)
  361. ts.Add(t);
  362. }
  363. }
  364. points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t1));
  365. }
  366. if (ts != null)
  367. ts.Add(t1);
  368. }
  369. /// <summary>
  370. ///
  371. /// </summary>
  372. /// <param name="points"></param>
  373. public void GetAllPoints(List<Vector3> points, float pointDensity = 0.1f)
  374. {
  375. int cnt = _segments.Count;
  376. for (int i = 0; i < cnt; i++)
  377. GetPointsInSegment(i, 0, 1, points, null, pointDensity);
  378. }
  379. /// <summary>
  380. /// Catmull rom spline implementation
  381. /// by Stéphane Drouot, laei - http://games.laei.org
  382. ///
  383. /// Actual translation of math gebrish to C# credit is due to
  384. /// Boon Cotter - http://www.booncotter.com/waypoints-catmull-rom-splines/
  385. ///
  386. /// This takes a list of vector3 (or an array) and gives a function called .onCurve(t)
  387. /// returning a value on a Catmull-Rom spline for 0 <= t <= 1
  388. /// </summary>
  389. Vector3 onCRSplineCurve(int ptStart, int ptCount, float t)
  390. {
  391. 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
  392. Vector3 result = new Vector3();
  393. Vector3 p0 = _points[adjustedIndex];
  394. Vector3 p1 = _points[adjustedIndex + 1];
  395. Vector3 p2 = _points[adjustedIndex + 2];
  396. Vector3 p3 = _points[adjustedIndex + 3];
  397. 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);
  398. float t0 = ((-adjustedT + 2f) * adjustedT - 1f) * adjustedT * 0.5f;
  399. float t1 = (((3f * adjustedT - 5f) * adjustedT) * adjustedT + 2f) * 0.5f;
  400. float t2 = ((-3f * adjustedT + 4f) * adjustedT + 1f) * adjustedT * 0.5f;
  401. float t3 = ((adjustedT - 1f) * adjustedT * adjustedT) * 0.5f;
  402. result.x = p0.x * t0 + p1.x * t1 + p2.x * t2 + p3.x * t3;
  403. result.y = p0.y * t0 + p1.y * t1 + p2.y * t2 + p3.y * t3;
  404. result.z = p0.z * t0 + p1.z * t1 + p2.z * t2 + p3.z * t3;
  405. return result;
  406. }
  407. Vector3 onBezierCurve(int ptStart, int ptCount, float t)
  408. {
  409. float t2 = 1f - t;
  410. Vector3 p0 = _points[ptStart];
  411. Vector3 p1 = _points[ptStart + 1];
  412. Vector3 cp0 = _points[ptStart + 2];
  413. if (ptCount == 4)
  414. {
  415. Vector3 cp1 = _points[ptStart + 3];
  416. return t2 * t2 * t2 * p0 + 3f * t2 * t2 * t * cp0 + 3f * t2 * t * t * cp1 + t * t * t * p1;
  417. }
  418. else
  419. return t2 * t2 * p0 + 2f * t2 * t * cp0 + t * t * p1;
  420. }
  421. }
  422. }