A game about forced loneliness, made by TACStudios
1using System;
2using System.Linq;
3using UnityEngine;
4
5namespace UnityEditor.U2D.Common.Path
6{
7 internal class EditablePathController : IEditablePathController
8 {
9 private ISnapping<Vector3> m_Snapping = new Snapping();
10
11 public IEditablePath editablePath { get; set; }
12 public IEditablePath closestEditablePath { get { return editablePath; } }
13
14 public ISnapping<Vector3> snapping
15 {
16 get { return m_Snapping; }
17 set { m_Snapping = value; }
18 }
19
20 public bool enableSnapping { get; }
21
22 public void RegisterUndo(string name)
23 {
24 if (editablePath.undoObject != null)
25 editablePath.undoObject.RegisterUndo(name);
26 }
27
28 public void ClearSelection()
29 {
30 editablePath.selection.Clear();
31 }
32
33 public void SelectPoint(int index, bool select)
34 {
35 editablePath.selection.Select(index, select);
36 }
37
38 public void CreatePoint(int index, Vector3 position)
39 {
40 ClearSelection();
41
42 if (editablePath.shapeType == ShapeType.Polygon)
43 {
44 editablePath.InsertPoint(index + 1, new ControlPoint() { position = position });
45 }
46 else if (editablePath.shapeType == ShapeType.Spline)
47 {
48 var nextIndex = NextIndex(index);
49 var currentPoint = editablePath.GetPoint(index);
50 var nextPoint = editablePath.GetPoint(nextIndex);
51
52 float t;
53 var closestPoint = BezierUtility.ClosestPointOnCurve(
54 position,
55 currentPoint.position,
56 nextPoint.position,
57 GetRightTangentPosition(index),
58 GetLeftTangentPosition(nextIndex),
59 out t);
60
61 Vector3 leftStartPosition;
62 Vector3 leftEndPosition;
63 Vector3 leftStartTangent;
64 Vector3 leftEndTangent;
65
66 Vector3 rightStartPosition;
67 Vector3 rightEndPosition;
68 Vector3 rightStartTangent;
69 Vector3 rightEndTangent;
70
71 BezierUtility.SplitBezier(t, currentPoint.position, nextPoint.position, GetRightTangentPosition(index), GetLeftTangentPosition(nextIndex),
72 out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
73 out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
74
75 var newPointIndex = index + 1;
76 var newPoint = new ControlPoint()
77 {
78 position = closestPoint,
79 leftTangent = leftEndTangent,
80 rightTangent = rightStartTangent,
81 tangentMode = TangentMode.Continuous
82 };
83
84 currentPoint.rightTangent = leftStartTangent;
85 nextPoint.leftTangent = rightEndTangent;
86
87 if (currentPoint.tangentMode == TangentMode.Linear && nextPoint.tangentMode == TangentMode.Linear)
88 {
89 newPoint.tangentMode = TangentMode.Linear;
90 newPoint.localLeftTangent = Vector3.zero;
91 newPoint.localRightTangent = Vector3.zero;
92 currentPoint.localRightTangent = Vector3.zero;
93 nextPoint.localLeftTangent = Vector3.zero;
94 }
95 else
96 {
97 if (currentPoint.tangentMode == TangentMode.Linear)
98 currentPoint.tangentMode = TangentMode.Broken;
99
100 if (nextPoint.tangentMode == TangentMode.Linear)
101 nextPoint.tangentMode = TangentMode.Broken;
102 }
103
104 editablePath.SetPoint(index, currentPoint);
105 editablePath.SetPoint(nextIndex, nextPoint);
106 editablePath.InsertPoint(newPointIndex, newPoint);
107 }
108 }
109
110 public void RemoveSelectedPoints()
111 {
112 var minPointCount = editablePath.isOpenEnded ? 2 : 3;
113
114 if (editablePath.pointCount > minPointCount)
115 {
116 var indices = editablePath.selection.elements.OrderByDescending( i => i);
117
118 foreach (var index in indices)
119 if (editablePath.pointCount > minPointCount)
120 editablePath.RemovePoint(index);
121
122 ClearSelection();
123 }
124 }
125
126 public void MoveSelectedPoints(Vector3 delta)
127 {
128 delta = Vector3.ProjectOnPlane(delta, editablePath.forward);
129
130 for (var i = 0; i < editablePath.pointCount; ++i)
131 {
132 if (editablePath.selection.Contains(i))
133 {
134 var controlPoint = editablePath.GetPoint(i);
135 controlPoint.position += delta;
136 editablePath.SetPoint(i, controlPoint);
137 }
138 }
139 }
140
141 public void MoveEdge(int index, Vector3 delta)
142 {
143 if (editablePath.isOpenEnded && index == editablePath.pointCount - 1)
144 return;
145
146 var controlPoint = editablePath.GetPoint(index);
147 controlPoint.position += delta;
148 editablePath.SetPoint(index, controlPoint);
149 controlPoint = NextControlPoint(index);
150 controlPoint.position += delta;
151 editablePath.SetPoint(NextIndex(index), controlPoint);
152 }
153
154 public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent, TangentMode cachedTangentMode)
155 {
156 var controlPoint = editablePath.GetPoint(index);
157 controlPoint.tangentMode = cachedTangentMode;
158 controlPoint.leftTangent = position;
159 controlPoint.mirrorLeft = false;
160
161 if (setToLinear)
162 {
163 controlPoint.leftTangent = controlPoint.position;
164 controlPoint.rightTangent = cachedRightTangent;
165 }
166 else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
167 {
168 var magnitude = controlPoint.localRightTangent.magnitude;
169
170 if (mirror)
171 magnitude = controlPoint.localLeftTangent.magnitude;
172
173 controlPoint.localRightTangent = magnitude * -controlPoint.localLeftTangent.normalized;
174 }
175
176 editablePath.SetPoint(index, controlPoint);
177 editablePath.UpdateTangentMode(index);
178 }
179
180 public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent, TangentMode cachedTangentMode)
181 {
182 var controlPoint = editablePath.GetPoint(index);
183 controlPoint.tangentMode = cachedTangentMode;
184 controlPoint.rightTangent = position;
185 controlPoint.mirrorLeft = true;
186
187 if (setToLinear)
188 {
189 controlPoint.rightTangent = controlPoint.position;
190 controlPoint.leftTangent = cachedLeftTangent;
191 }
192 else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
193 {
194 var magnitude = controlPoint.localLeftTangent.magnitude;
195
196 if (mirror)
197 magnitude = controlPoint.localRightTangent.magnitude;
198
199 controlPoint.localLeftTangent = magnitude * -controlPoint.localRightTangent.normalized;
200 }
201
202 editablePath.SetPoint(index, controlPoint);
203 editablePath.UpdateTangentMode(index);
204 }
205
206 public void ClearClosestPath() { }
207 public void AddClosestPath(float distance) { }
208
209 private Vector3 GetLeftTangentPosition(int index)
210 {
211 var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localLeftTangent.sqrMagnitude, 0f);
212
213 if (isLinear)
214 {
215 var position = editablePath.GetPoint(index).position;
216 var prevPosition = PrevControlPoint(index).position;
217
218 return (1f / 3f) * (prevPosition - position) + position;
219 }
220
221 return editablePath.GetPoint(index).leftTangent;
222 }
223
224 private Vector3 GetRightTangentPosition(int index)
225 {
226 var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localRightTangent.sqrMagnitude, 0f);
227
228 if (isLinear)
229 {
230 var position = editablePath.GetPoint(index).position;
231 var nextPosition = NextControlPoint(index).position;
232
233 return (1f / 3f) * (nextPosition - position) + position;
234 }
235
236 return editablePath.GetPoint(index).rightTangent;
237 }
238
239 private int NextIndex(int index)
240 {
241 return EditablePathUtility.Mod(index + 1, editablePath.pointCount);
242 }
243
244 private ControlPoint NextControlPoint(int index)
245 {
246 return editablePath.GetPoint(NextIndex(index));
247 }
248
249 private int PrevIndex(int index)
250 {
251 return EditablePathUtility.Mod(index - 1, editablePath.pointCount);
252 }
253
254 private ControlPoint PrevControlPoint(int index)
255 {
256 return editablePath.GetPoint(PrevIndex(index));
257 }
258 }
259}