EditablePathExtensions.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace UnityEditor.Experimental.Rendering.Universal.Path2D
  6. {
  7. internal static class EditablePathExtensions
  8. {
  9. public static Polygon ToPolygon(this IEditablePath path)
  10. {
  11. var polygon = new Polygon()
  12. {
  13. isOpenEnded = path.isOpenEnded,
  14. points = new Vector3[path.pointCount]
  15. };
  16. for (var i = 0; i < path.pointCount; ++i)
  17. polygon.points[i] = path.GetPoint(i).position;
  18. return polygon;
  19. }
  20. public static Spline ToSpline(this IEditablePath path)
  21. {
  22. var count = path.pointCount * 3;
  23. if (path.isOpenEnded)
  24. count -= 2;
  25. var spline = new Spline()
  26. {
  27. isOpenEnded = path.isOpenEnded,
  28. points = new Vector3[count]
  29. };
  30. for (var i = 0; i < path.pointCount; ++i)
  31. {
  32. var point = path.GetPoint(i);
  33. spline.points[i*3] = point.position;
  34. if (i * 3 + 1 < count)
  35. {
  36. var nextIndex = EditablePathUtility.Mod(i+1, path.pointCount);
  37. spline.points[i*3 + 1] = path.CalculateRightTangent(i);
  38. spline.points[i*3 + 2] = path.CalculateLeftTangent(nextIndex);
  39. }
  40. }
  41. return spline;
  42. }
  43. public static Vector3 CalculateLocalLeftTangent(this IEditablePath path, int index)
  44. {
  45. return path.CalculateLeftTangent(index) - path.GetPoint(index).position;
  46. }
  47. public static Vector3 CalculateLeftTangent(this IEditablePath path, int index)
  48. {
  49. var point = path.GetPoint(index);
  50. var isTangentLinear = point.localLeftTangent == Vector3.zero;
  51. var isEndpoint = path.isOpenEnded && index == 0;
  52. var tangent = point.leftTangent;
  53. if (isEndpoint)
  54. return point.position;
  55. if (isTangentLinear)
  56. {
  57. var prevPoint = path.GetPrevPoint(index);
  58. var v = prevPoint.position - point.position;
  59. tangent = point.position + v.normalized * (v.magnitude / 3f);
  60. }
  61. return tangent;
  62. }
  63. public static Vector3 CalculateLocalRightTangent(this IEditablePath path, int index)
  64. {
  65. return path.CalculateRightTangent(index) - path.GetPoint(index).position;
  66. }
  67. public static Vector3 CalculateRightTangent(this IEditablePath path, int index)
  68. {
  69. var point = path.GetPoint(index);
  70. var isTangentLinear = point.localRightTangent == Vector3.zero;
  71. var isEndpoint = path.isOpenEnded && index == path.pointCount - 1;
  72. var tangent = point.rightTangent;
  73. if (isEndpoint)
  74. return point.position;
  75. if (isTangentLinear)
  76. {
  77. var nextPoint = path.GetNextPoint(index);
  78. var v = nextPoint.position - point.position;
  79. tangent = point.position + v.normalized * (v.magnitude / 3f);
  80. }
  81. return tangent;
  82. }
  83. public static ControlPoint GetPrevPoint(this IEditablePath path, int index)
  84. {
  85. return path.GetPoint(EditablePathUtility.Mod(index - 1, path.pointCount));
  86. }
  87. public static ControlPoint GetNextPoint(this IEditablePath path, int index)
  88. {
  89. return path.GetPoint(EditablePathUtility.Mod(index + 1, path.pointCount));
  90. }
  91. public static void UpdateTangentMode(this IEditablePath path, int index)
  92. {
  93. var localToWorldMatrix = path.localToWorldMatrix;
  94. path.localToWorldMatrix = Matrix4x4.identity;
  95. var controlPoint = path.GetPoint(index);
  96. var isLeftTangentLinear = controlPoint.localLeftTangent == Vector3.zero;
  97. var isRightTangentLinear = controlPoint.localRightTangent == Vector3.zero;
  98. if (isLeftTangentLinear && isRightTangentLinear)
  99. controlPoint.tangentMode = TangentMode.Linear;
  100. else if (isLeftTangentLinear || isRightTangentLinear)
  101. controlPoint.tangentMode = TangentMode.Broken;
  102. else if (controlPoint.tangentMode != TangentMode.Continuous)
  103. controlPoint.tangentMode = TangentMode.Broken;
  104. controlPoint.StoreTangents();
  105. path.SetPoint(index, controlPoint);
  106. path.localToWorldMatrix = localToWorldMatrix;
  107. }
  108. public static void UpdateTangentsFromMode(this IEditablePath path)
  109. {
  110. const float kEpsilon = 0.001f;
  111. var localToWorldMatrix = path.localToWorldMatrix;
  112. path.localToWorldMatrix = Matrix4x4.identity;
  113. for (var i = 0; i < path.pointCount; ++i)
  114. {
  115. var controlPoint = path.GetPoint(i);
  116. if (controlPoint.tangentMode == TangentMode.Linear)
  117. {
  118. controlPoint.localLeftTangent = Vector3.zero;
  119. controlPoint.localRightTangent = Vector3.zero;
  120. }
  121. else if (controlPoint.tangentMode == TangentMode.Broken)
  122. {
  123. var isLeftEndpoint = path.isOpenEnded && i == 0;
  124. var prevPoint = path.GetPrevPoint(i);
  125. var nextPoint = path.GetNextPoint(i);
  126. var liniarLeftPosition = (prevPoint.position - controlPoint.position) / 3f;
  127. var isLeftTangentLinear = isLeftEndpoint || (controlPoint.localLeftTangent - liniarLeftPosition).sqrMagnitude < kEpsilon;
  128. if (isLeftTangentLinear)
  129. controlPoint.localLeftTangent = Vector3.zero;
  130. var isRightEndpoint = path.isOpenEnded && i == path.pointCount-1;
  131. var liniarRightPosition = (nextPoint.position - controlPoint.position) / 3f;
  132. var isRightTangentLinear = isRightEndpoint || (controlPoint.localRightTangent - liniarRightPosition).sqrMagnitude < kEpsilon;
  133. if (isRightTangentLinear)
  134. controlPoint.localRightTangent = Vector3.zero;
  135. if (isLeftTangentLinear && isRightTangentLinear)
  136. controlPoint.tangentMode = TangentMode.Linear;
  137. }
  138. else if (controlPoint.tangentMode == TangentMode.Continuous)
  139. {
  140. //TODO: ensure tangent continuity
  141. }
  142. controlPoint.StoreTangents();
  143. path.SetPoint(i, controlPoint);
  144. }
  145. path.localToWorldMatrix = localToWorldMatrix;
  146. }
  147. public static void SetTangentMode(this IEditablePath path, int index, TangentMode tangentMode)
  148. {
  149. var localToWorldMatrix = path.localToWorldMatrix;
  150. path.localToWorldMatrix = Matrix4x4.identity;
  151. var controlPoint = path.GetPoint(index);
  152. var isEndpoint = path.isOpenEnded && (index == 0 || index == path.pointCount - 1);
  153. var oldTangentMode = controlPoint.tangentMode;
  154. controlPoint.tangentMode = tangentMode;
  155. controlPoint.RestoreTangents();
  156. if (tangentMode == TangentMode.Linear)
  157. {
  158. controlPoint.localLeftTangent = Vector3.zero;
  159. controlPoint.localRightTangent = Vector3.zero;
  160. }
  161. else if (tangentMode == TangentMode.Continuous && !isEndpoint)
  162. {
  163. var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
  164. var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
  165. var tangentDotProduct = Vector3.Dot(controlPoint.localLeftTangent.normalized, controlPoint.localRightTangent.normalized);
  166. var isContinous = tangentDotProduct < 0f && (tangentDotProduct + 1) < 0.001f;
  167. var isLinear = isLeftLinear && isRightLinear;
  168. if ((isLinear || oldTangentMode == TangentMode.Broken) && !isContinous)
  169. {
  170. var prevPoint = path.GetPrevPoint(index);
  171. var nextPoint = path.GetNextPoint(index);
  172. var vLeft = prevPoint.position - controlPoint.position;
  173. var vRight = nextPoint.position - controlPoint.position;
  174. var rightDirection = Vector3.Cross(Vector3.Cross(vLeft, vRight), vLeft.normalized + vRight.normalized).normalized;
  175. var scale = 1f / 3f;
  176. if (isLeftLinear)
  177. controlPoint.localLeftTangent = vLeft.magnitude * scale * -rightDirection;
  178. else
  179. controlPoint.localLeftTangent = controlPoint.localLeftTangent.magnitude * -rightDirection;
  180. if (isRightLinear)
  181. controlPoint.localRightTangent = vRight.magnitude * scale * rightDirection;
  182. else
  183. controlPoint.localRightTangent = controlPoint.localRightTangent.magnitude * rightDirection;
  184. }
  185. }
  186. else
  187. {
  188. var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
  189. var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
  190. if (isLeftLinear || isRightLinear)
  191. {
  192. if (isLeftLinear)
  193. controlPoint.localLeftTangent = path.CalculateLocalLeftTangent(index);
  194. if (isRightLinear)
  195. controlPoint.localRightTangent = path.CalculateLocalRightTangent(index);
  196. }
  197. }
  198. controlPoint.StoreTangents();
  199. path.SetPoint(index, controlPoint);
  200. path.localToWorldMatrix = localToWorldMatrix;
  201. }
  202. public static void MirrorTangent(this IEditablePath path, int index)
  203. {
  204. var localToWorldMatrix = path.localToWorldMatrix;
  205. path.localToWorldMatrix = Matrix4x4.identity;
  206. var controlPoint = path.GetPoint(index);
  207. if (controlPoint.tangentMode == TangentMode.Linear)
  208. return;
  209. if (!Mathf.Approximately((controlPoint.localLeftTangent + controlPoint.localRightTangent).sqrMagnitude, 0f))
  210. {
  211. if (controlPoint.mirrorLeft)
  212. controlPoint.localLeftTangent = -controlPoint.localRightTangent;
  213. else
  214. controlPoint.localRightTangent = -controlPoint.localLeftTangent;
  215. controlPoint.StoreTangents();
  216. path.SetPoint(index, controlPoint);
  217. }
  218. path.localToWorldMatrix = localToWorldMatrix;
  219. }
  220. }
  221. }