BezierPath.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. Copyright (c) 2021 Omar Duarte
  3. Unauthorized copying of this file, via any medium is strictly prohibited.
  4. Writen by Omar Duarte, 2021.
  5. This class demonstrates the code discussed in these two articles:
  6. http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/
  7. http://devmag.org.za/2011/06/23/bzier-path-algorithms/
  8. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  9. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  10. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  11. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  12. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  13. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  14. THE SOFTWARE.
  15. */
  16. using UnityEngine;
  17. namespace PluginMaster
  18. {
  19. public static class BezierPath
  20. {
  21. private const float MINIMUM_SQR_DISTANCE = 0.01f;
  22. private const float DIVISION_THRESHOLD = -0.99f;
  23. public static Vector3[] GetBezierPoints(Vector3[] segmentPoints, float[] scales)
  24. {
  25. var controlPoints = Interpolate(segmentPoints, scales);
  26. var curveCount = (controlPoints.Length - 1) / 3;
  27. var pathPoints = GetDrawingPoints(controlPoints, curveCount);
  28. return pathPoints;
  29. }
  30. private static Vector3[] Interpolate(Vector3[] segmentPoints, float[] scales)
  31. {
  32. var controlPoints = new System.Collections.Generic.List<Vector3>();
  33. if (segmentPoints.Length < 2) return segmentPoints;
  34. for (int i = 0; i < segmentPoints.Length; i++)
  35. {
  36. if (i == 0)
  37. {
  38. var p1 = segmentPoints[i];
  39. var p2 = segmentPoints[i + 1];
  40. var tangent = (p2 - p1);
  41. var q1 = p1 + scales[i] * tangent;
  42. controlPoints.Add(p1);
  43. controlPoints.Add(q1);
  44. }
  45. else if (i == segmentPoints.Length - 1)
  46. {
  47. var p0 = segmentPoints[i - 1];
  48. var p1 = segmentPoints[i];
  49. var tangent = (p1 - p0);
  50. var q0 = p1 - scales[i] * tangent;
  51. controlPoints.Add(q0);
  52. controlPoints.Add(p1);
  53. }
  54. else
  55. {
  56. var p0 = segmentPoints[i - 1];
  57. var p1 = segmentPoints[i];
  58. var p2 = segmentPoints[i + 1];
  59. var tangent = (p2 - p0).normalized;
  60. var q0 = p1 - scales[i] * tangent * (p1 - p0).magnitude;
  61. var q1 = p1 + scales[i] * tangent * (p2 - p1).magnitude;
  62. controlPoints.Add(q0);
  63. controlPoints.Add(p1);
  64. controlPoints.Add(q1);
  65. }
  66. }
  67. return controlPoints.ToArray();
  68. }
  69. private static Vector3 CalculateBezierPoint(int curveIndex, Vector3[] controlPoints, float t)
  70. {
  71. var nodeIndex = curveIndex * 3;
  72. var p0 = controlPoints[nodeIndex];
  73. var p1 = controlPoints[nodeIndex + 1];
  74. var p2 = controlPoints[nodeIndex + 2];
  75. var p3 = controlPoints[nodeIndex + 3];
  76. return CalculateBezierPoint(t, p0, p1, p2, p3);
  77. }
  78. public static Vector3[] GetDrawingPoints(Vector3[] controlPoints, int curveCount)
  79. {
  80. var drawingPoints = new System.Collections.Generic.List<Vector3>();
  81. for (int curveIndex = 0; curveIndex < curveCount; ++curveIndex)
  82. {
  83. var bezierCurveDrawingPoints = FindDrawingPoints(curveIndex, controlPoints);
  84. if (curveIndex != 0) bezierCurveDrawingPoints.RemoveAt(0);
  85. drawingPoints.AddRange(bezierCurveDrawingPoints);
  86. }
  87. return drawingPoints.ToArray();
  88. }
  89. private static System.Collections.Generic.List<Vector3> FindDrawingPoints(int curveIndex, Vector3[] controlPoints)
  90. {
  91. var pointList = new System.Collections.Generic.List<Vector3>();
  92. var left = CalculateBezierPoint(curveIndex, controlPoints, 0);
  93. var right = CalculateBezierPoint(curveIndex, controlPoints, 1);
  94. pointList.Add(left);
  95. pointList.Add(right);
  96. FindDrawingPoints(curveIndex, 0, 1, pointList, controlPoints, 1);
  97. return pointList;
  98. }
  99. private static int FindDrawingPoints(int curveIndex, float t0, float t1,
  100. System.Collections.Generic.List<Vector3> pointList, Vector3[] controlPoints, int insertionIndex)
  101. {
  102. var left = CalculateBezierPoint(curveIndex, controlPoints, t0);
  103. var right = CalculateBezierPoint(curveIndex, controlPoints, t1);
  104. if ((left - right).sqrMagnitude < MINIMUM_SQR_DISTANCE) return 0;
  105. var tMid = (t0 + t1) / 2;
  106. var mid = CalculateBezierPoint(curveIndex, controlPoints, tMid);
  107. var leftDirection = (left - mid).normalized;
  108. var rightDirection = (right - mid).normalized;
  109. if (Vector3.Dot(leftDirection, rightDirection) < DIVISION_THRESHOLD && Mathf.Abs(tMid - 0.5f) > 0.0001f) return 0;
  110. int pointsAddedCount = 0;
  111. pointsAddedCount += FindDrawingPoints(curveIndex, t0, tMid, pointList, controlPoints, insertionIndex);
  112. pointList.Insert(insertionIndex + pointsAddedCount, mid);
  113. pointsAddedCount++;
  114. pointsAddedCount += FindDrawingPoints(curveIndex, tMid, t1, pointList,
  115. controlPoints, insertionIndex + pointsAddedCount);
  116. return pointsAddedCount;
  117. }
  118. private static Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
  119. {
  120. var u = 1 - t;
  121. var tt = t * t;
  122. var uu = u * u;
  123. var uuu = uu * u;
  124. var ttt = tt * t;
  125. var p = uuu * p0; //first term
  126. p += 3 * uu * t * p1; //second term
  127. p += 3 * u * tt * p2; //third term
  128. p += ttt * p3; //fourth term
  129. return p;
  130. }
  131. }
  132. }