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