Tanoda
BezierPath.cs
Go to the documentation of this file.
1
17using System.Collections.Generic;
18
20{
25 public class BezierPath
26 {
27 public int SegmentsPerCurve = 10;
28 public float MINIMUM_SQR_DISTANCE = 0.01f;
29
30 // This corresponds to about 172 degrees, 8 degrees from a straight line
31 public float DIVISION_THRESHOLD = -0.99f;
32
33 private List<Vector2> controlPoints;
34
35 private int curveCount; //how many bezier curves in this path?
36
41 public BezierPath()
42 {
43 controlPoints = new List<Vector2>();
44 }
45
51 public void SetControlPoints(List<Vector2> newControlPoints)
52 {
53 controlPoints.Clear();
54 controlPoints.AddRange(newControlPoints);
55 curveCount = (controlPoints.Count - 1) / 3;
56 }
57
58 public void SetControlPoints(Vector2[] newControlPoints)
59 {
60 controlPoints.Clear();
61 controlPoints.AddRange(newControlPoints);
62 curveCount = (controlPoints.Count - 1) / 3;
63 }
64
68 public List<Vector2> GetControlPoints()
69 {
70 return controlPoints;
71 }
72
73
77 public void Interpolate(List<Vector2> segmentPoints, float scale)
78 {
79 controlPoints.Clear();
80
81 if (segmentPoints.Count < 2)
82 {
83 return;
84 }
85
86 for (int i = 0; i < segmentPoints.Count; i++)
87 {
88 if (i == 0) // is first
89 {
90 Vector2 p1 = segmentPoints[i];
91 Vector2 p2 = segmentPoints[i + 1];
92
93 Vector2 tangent = (p2 - p1);
94 Vector2 q1 = p1 + scale * tangent;
95
96 controlPoints.Add(p1);
97 controlPoints.Add(q1);
98 }
99 else if (i == segmentPoints.Count - 1) //last
100 {
101 Vector2 p0 = segmentPoints[i - 1];
102 Vector2 p1 = segmentPoints[i];
103 Vector2 tangent = (p1 - p0);
104 Vector2 q0 = p1 - scale * tangent;
105
106 controlPoints.Add(q0);
107 controlPoints.Add(p1);
108 }
109 else
110 {
111 Vector2 p0 = segmentPoints[i - 1];
112 Vector2 p1 = segmentPoints[i];
113 Vector2 p2 = segmentPoints[i + 1];
114 Vector2 tangent = (p2 - p0).normalized;
115 Vector2 q0 = p1 - scale * tangent * (p1 - p0).magnitude;
116 Vector2 q1 = p1 + scale * tangent * (p2 - p1).magnitude;
117
118 controlPoints.Add(q0);
119 controlPoints.Add(p1);
120 controlPoints.Add(q1);
121 }
122 }
123
124 curveCount = (controlPoints.Count - 1) / 3;
125 }
126
130 public void SamplePoints(List<Vector2> sourcePoints, float minSqrDistance, float maxSqrDistance, float scale)
131 {
132 if (sourcePoints.Count < 2)
133 {
134 return;
135 }
136
137 Stack<Vector2> samplePoints = new Stack<Vector2>();
138
139 samplePoints.Push(sourcePoints[0]);
140
141 Vector2 potentialSamplePoint = sourcePoints[1];
142
143 int i = 2;
144
145 for (i = 2; i < sourcePoints.Count; i++)
146 {
147 if (
148 ((potentialSamplePoint - sourcePoints[i]).sqrMagnitude > minSqrDistance) &&
149 ((samplePoints.Peek() - sourcePoints[i]).sqrMagnitude > maxSqrDistance))
150 {
151 samplePoints.Push(potentialSamplePoint);
152 }
153
154 potentialSamplePoint = sourcePoints[i];
155 }
156
157 //now handle last bit of curve
158 Vector2 p1 = samplePoints.Pop(); //last sample point
159 Vector2 p0 = samplePoints.Peek(); //second last sample point
160 Vector2 tangent = (p0 - potentialSamplePoint).normalized;
161 float d2 = (potentialSamplePoint - p1).magnitude;
162 float d1 = (p1 - p0).magnitude;
163 p1 = p1 + tangent * ((d1 - d2) / 2);
164
165 samplePoints.Push(p1);
166 samplePoints.Push(potentialSamplePoint);
167
168
169 Interpolate(new List<Vector2>(samplePoints), scale);
170 }
171
181 public Vector2 CalculateBezierPoint(int curveIndex, float t)
182 {
183 int nodeIndex = curveIndex * 3;
184
185 Vector2 p0 = controlPoints[nodeIndex];
186 Vector2 p1 = controlPoints[nodeIndex + 1];
187 Vector2 p2 = controlPoints[nodeIndex + 2];
188 Vector2 p3 = controlPoints[nodeIndex + 3];
189
190 return CalculateBezierPoint(t, p0, p1, p2, p3);
191 }
192
197 public List<Vector2> GetDrawingPoints0()
198 {
199 List<Vector2> drawingPoints = new List<Vector2>();
200
201 for (int curveIndex = 0; curveIndex < curveCount; curveIndex++)
202 {
203 if (curveIndex == 0) //Only do this for the first end point.
204 //When i != 0, this coincides with the
205 //end point of the previous segment,
206 {
207 drawingPoints.Add(CalculateBezierPoint(curveIndex, 0));
208 }
209
210 for (int j = 1; j <= SegmentsPerCurve; j++)
211 {
212 float t = j / (float)SegmentsPerCurve;
213 drawingPoints.Add(CalculateBezierPoint(curveIndex, t));
214 }
215 }
216
217 return drawingPoints;
218 }
219
226 public List<Vector2> GetDrawingPoints1()
227 {
228 List<Vector2> drawingPoints = new List<Vector2>();
229
230 for (int i = 0; i < controlPoints.Count - 3; i += 3)
231 {
232 Vector2 p0 = controlPoints[i];
233 Vector2 p1 = controlPoints[i + 1];
234 Vector2 p2 = controlPoints[i + 2];
235 Vector2 p3 = controlPoints[i + 3];
236
237 if (i == 0) //only do this for the first end point. When i != 0, this coincides with the end point of the previous segment,
238 {
239 drawingPoints.Add(CalculateBezierPoint(0, p0, p1, p2, p3));
240 }
241
242 for (int j = 1; j <= SegmentsPerCurve; j++)
243 {
244 float t = j / (float)SegmentsPerCurve;
245 drawingPoints.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
246 }
247 }
248
249 return drawingPoints;
250 }
251
256 public List<Vector2> GetDrawingPoints2()
257 {
258 List<Vector2> drawingPoints = new List<Vector2>();
259
260 for (int curveIndex = 0; curveIndex < curveCount; curveIndex++)
261 {
262 List<Vector2> bezierCurveDrawingPoints = FindDrawingPoints(curveIndex);
263
264 if (curveIndex != 0)
265 {
266 //remove the fist point, as it coincides with the last point of the previous Bezier curve.
267 bezierCurveDrawingPoints.RemoveAt(0);
268 }
269
270 drawingPoints.AddRange(bezierCurveDrawingPoints);
271 }
272
273 return drawingPoints;
274 }
275
276 List<Vector2> FindDrawingPoints(int curveIndex)
277 {
278 List<Vector2> pointList = new List<Vector2>();
279
280 Vector2 left = CalculateBezierPoint(curveIndex, 0);
281 Vector2 right = CalculateBezierPoint(curveIndex, 1);
282
283 pointList.Add(left);
284 pointList.Add(right);
285
286 FindDrawingPoints(curveIndex, 0, 1, pointList, 1);
287
288 return pointList;
289 }
290
291
295 int FindDrawingPoints(int curveIndex, float t0, float t1,
296 List<Vector2> pointList, int insertionIndex)
297 {
298 Vector2 left = CalculateBezierPoint(curveIndex, t0);
299 Vector2 right = CalculateBezierPoint(curveIndex, t1);
300
301 if ((left - right).sqrMagnitude < MINIMUM_SQR_DISTANCE)
302 {
303 return 0;
304 }
305
306 float tMid = (t0 + t1) / 2;
307 Vector2 mid = CalculateBezierPoint(curveIndex, tMid);
308
309 Vector2 leftDirection = (left - mid).normalized;
310 Vector2 rightDirection = (right - mid).normalized;
311
312 if (Vector2.Dot(leftDirection, rightDirection) > DIVISION_THRESHOLD || Mathf.Abs(tMid - 0.5f) < 0.0001f)
313 {
314 int pointsAddedCount = 0;
315
316 pointsAddedCount += FindDrawingPoints(curveIndex, t0, tMid, pointList, insertionIndex);
317 pointList.Insert(insertionIndex + pointsAddedCount, mid);
318 pointsAddedCount++;
319 pointsAddedCount += FindDrawingPoints(curveIndex, tMid, t1, pointList, insertionIndex + pointsAddedCount);
320
321 return pointsAddedCount;
322 }
323
324 return 0;
325 }
326
327
328
332 private Vector2 CalculateBezierPoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3)
333 {
334 float u = 1 - t;
335 float tt = t * t;
336 float uu = u * u;
337 float uuu = uu * u;
338 float ttt = tt * t;
339
340 Vector2 p = uuu * p0; //first term
341
342 p += 3 * uu * t * p1; //second term
343 p += 3 * u * tt * p2; //third term
344 p += ttt * p3; //fourth term
345
346 return p;
347
348 }
349 }
350}
List< Vector2 > GetControlPoints()
Definition: BezierPath.cs:68
Vector2 CalculateBezierPoint(int curveIndex, float t)
Definition: BezierPath.cs:181
void SetControlPoints(List< Vector2 > newControlPoints)
Definition: BezierPath.cs:51
void SamplePoints(List< Vector2 > sourcePoints, float minSqrDistance, float maxSqrDistance, float scale)
Definition: BezierPath.cs:130
void Interpolate(List< Vector2 > segmentPoints, float scale)
Definition: BezierPath.cs:77
void SetControlPoints(Vector2[] newControlPoints)
Definition: BezierPath.cs:58
Credit Erdener Gonenc - @PixelEnvision.