1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package math.geom2d.spline;
25
26 import java.awt.geom.CubicCurve2D;
27 import java.util.Collection;
28
29 import math.geom2d.AffineTransform2D;
30 import math.geom2d.Box2D;
31 import math.geom2d.Point2D;
32 import math.geom2d.Shape2D;
33 import math.geom2d.Vector2D;
34 import math.geom2d.curve.AbstractSmoothCurve2D;
35 import math.geom2d.curve.Curve2D;
36 import math.geom2d.curve.Curve2DUtils;
37 import math.geom2d.curve.CurveArray2D;
38 import math.geom2d.curve.CurveSet2D;
39 import math.geom2d.curve.SmoothCurve2D;
40 import math.geom2d.domain.ContinuousOrientedCurve2D;
41 import math.geom2d.line.LinearShape2D;
42
43
44
45
46
47
48
49
50
51 public class CubicBezierCurve2D extends AbstractSmoothCurve2D
52 implements SmoothCurve2D, ContinuousOrientedCurve2D, Cloneable {
53
54 protected double x1, y1;
55 protected double ctrlx1, ctrly1;
56 protected double ctrlx2, ctrly2;
57 protected double x2, y2;
58
59
60
61
62
63 public CubicBezierCurve2D() {
64 this(0, 0, 0, 0, 0, 0, 0, 0);
65 }
66
67
68
69
70
71
72
73 public CubicBezierCurve2D(double[][] coefs) {
74 this(coefs[0][0], coefs[1][0], coefs[0][0]+coefs[0][1]/3.0, coefs[1][0]
75 +coefs[1][1]/3.0,
76 coefs[0][0]+2*coefs[0][1]/3.0+coefs[0][2]/3.0, coefs[1][0]+2
77 *coefs[1][1]/3.0+coefs[1][2]/3.0, coefs[0][0]
78 +coefs[0][1]+coefs[0][2]+coefs[0][3], coefs[1][0]
79 +coefs[1][1]+coefs[1][2]+coefs[1][3]);
80 }
81
82
83
84
85
86
87
88
89
90
91
92 public CubicBezierCurve2D(java.awt.geom.Point2D p1, java.awt.geom.Point2D ctrl1,
93 java.awt.geom.Point2D ctrl2, java.awt.geom.Point2D p2) {
94 this(p1.getX(), p1.getY(), ctrl1.getX(), ctrl1.getY(), ctrl2.getX(),
95 ctrl2.getY(), p2.getX(), p2.getY());
96 }
97
98
99
100
101
102
103
104
105
106
107 public CubicBezierCurve2D(
108 java.awt.geom.Point2D p1, Vector2D v1,
109 java.awt.geom.Point2D p2, Vector2D v2) {
110 this( p1.getX(), p1.getY(),
111 p1.getX()+v1.getX()/3, p1.getY()+v1.getY()/3,
112 p2.getX()-v2.getX()/3, p2.getY()-v2.getY()/3,
113 p2.getX(), p2.getY());
114 }
115
116
117
118
119
120
121 public CubicBezierCurve2D(double x1, double y1, double xctrl1, double yctrl1,
122 double xctrl2, double yctrl2, double x2, double y2) {
123 this.x1 = x1;
124 this.y1 = y1;
125 this.ctrlx1 = xctrl1;
126 this.ctrly1 = yctrl1;
127 this.ctrlx2 = xctrl2;
128 this.ctrly2 = yctrl2;
129 this.x2 = x2;
130 this.y2 = y2;
131 }
132
133
134
135
136
137 public final static CubicBezierCurve2D create (
138 Point2D p1, Point2D c1, Point2D c2, Point2D p2) {
139 return new CubicBezierCurve2D(p1, c1, c2, p2);
140 }
141
142 public final static CubicBezierCurve2D create (
143 Point2D p1, Vector2D v1, Point2D p2, Vector2D v2) {
144 return new CubicBezierCurve2D(p1, v1, p2, v2);
145 }
146
147
148
149
150
151 public Point2D getControl1() {
152 return new Point2D(ctrlx1, ctrly1);
153 }
154
155 public Point2D getControl2() {
156 return new Point2D(ctrlx2, ctrly2);
157 }
158
159 public Point2D getP1() {
160 return this.getFirstPoint();
161 }
162
163 public Point2D getP2() {
164 return this.getLastPoint();
165 }
166
167 public Point2D getCtrlP1() {
168 return this.getControl1();
169 }
170
171 public Point2D getCtrlP2() {
172 return this.getControl2();
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186 public double[][] getParametric() {
187 double[][] tab = new double[2][4];
188 tab[0][0] = x1;
189 tab[0][1] = 3*ctrlx1-3*x1;
190 tab[0][2] = 3*x1-6*ctrlx1+3*ctrlx2;
191 tab[0][3] = x2-3*ctrlx2+3*ctrlx1-x1;
192
193 tab[1][0] = y1;
194 tab[1][1] = 3*ctrly1-3*y1;
195 tab[1][2] = 3*y1-6*ctrly1+3*ctrly2;
196 tab[1][3] = y2-3*ctrly2+3*ctrly1-y1;
197 return tab;
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 public double getWindingAngle(java.awt.geom.Point2D point) {
234 return this.getAsPolyline(100).getWindingAngle(point);
235 }
236
237
238
239
240
241
242
243
244 public boolean isInside(java.awt.geom.Point2D pt) {
245 return this.getAsPolyline(100).isInside(pt);
246 }
247
248 public double getSignedDistance(java.awt.geom.Point2D point) {
249 if (isInside(point))
250 return -getDistance(point.getX(), point.getY());
251 else
252 return getDistance(point.getX(), point.getY());
253 }
254
255
256
257
258 public double getSignedDistance(double x, double y) {
259 if (isInside(new Point2D(x, y)))
260 return -getDistance(x, y);
261 else
262 return getDistance(x, y);
263 }
264
265
266
267
268 public Vector2D getTangent(double t) {
269 double[][] c = getParametric();
270 double dx = c[0][1]+(2*c[0][2]+3*c[0][3]*t)*t;
271 double dy = c[1][1]+(2*c[1][2]+3*c[1][3]*t)*t;
272 return new Vector2D(dx, dy);
273 }
274
275
276
277
278 public double getCurvature(double t) {
279 double[][] c = getParametric();
280 double xp = c[0][1]+(2*c[0][2]+3*c[0][3]*t)*t;
281 double yp = c[1][1]+(2*c[1][2]+3*c[1][3]*t)*t;
282 double xs = 2*c[0][2]+6*c[0][3]*t;
283 double ys = 2*c[1][2]+6*c[1][3]*t;
284
285 return (xp*ys-yp*xs)/Math.pow(Math.hypot(xp, yp), 3);
286 }
287
288
289
290
291
292
293
294 public boolean isClosed() {
295 return false;
296 }
297
298
299
300
301
302
303
304 public double getT0() {
305 return 0;
306 }
307
308
309
310
311 public double getT1() {
312 return 1;
313 }
314
315
316
317
318
319
320 public Collection<Point2D> getIntersections(LinearShape2D line) {
321 return this.getAsPolyline(100).getIntersections(line);
322 }
323
324
325
326
327 public Point2D getPoint(double t) {
328 t = Math.min(Math.max(t, 0), 1);
329 double[][] c = getParametric();
330 double x = c[0][0]+(c[0][1]+(c[0][2]+c[0][3]*t)*t)*t;
331 double y = c[1][0]+(c[1][1]+(c[1][2]+c[1][3]*t)*t)*t;
332 return new Point2D(x, y);
333 }
334
335
336
337
338
339
340 @Override
341 public Point2D getFirstPoint() {
342 return new Point2D(this.x1, this.y1);
343 }
344
345
346
347
348
349
350 @Override
351 public Point2D getLastPoint() {
352 return new Point2D(this.x2, this.y2);
353 }
354
355
356
357
358 public double getPosition(java.awt.geom.Point2D point) {
359 int N = 100;
360 return this.getAsPolyline(N).getPosition(point)/(N);
361 }
362
363
364
365
366 public double project(java.awt.geom.Point2D point) {
367 int N = 100;
368 return this.getAsPolyline(N).project(point)/(N);
369 }
370
371
372
373
374
375 public CubicBezierCurve2D getReverseCurve() {
376 return new CubicBezierCurve2D(
377 this.getLastPoint(), this.getControl1(),
378 this.getControl2(), this.getFirstPoint());
379 }
380
381
382
383
384 public CubicBezierCurve2D getSubCurve(double t0, double t1) {
385 t0 = Math.max(t0, 0);
386 t1 = Math.min(t1, 1);
387 if (t0>t1)
388 return null;
389
390 double dt = t1-t0;
391 Vector2D v0 = getTangent(t0).times(dt);
392 Vector2D v1 = getTangent(t1).times(dt);
393 return new CubicBezierCurve2D(getPoint(t0), v0, getPoint(t1), v1);
394 }
395
396
397
398
399
400
401
402 public boolean contains(double x, double y) {
403 return new CubicCurve2D.Double(
404 x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2).contains(x, y);
405 }
406
407
408
409
410 public boolean contains(java.awt.geom.Point2D p) {
411 return this.contains(p.getX(), p.getY());
412 }
413
414
415
416
417 public double getDistance(java.awt.geom.Point2D p) {
418 return this.getDistance(p.getX(), p.getY());
419 }
420
421
422
423
424
425
426 public double getDistance(double x, double y) {
427 return this.getAsPolyline(100).getDistance(x, y);
428 }
429
430
431
432
433 public boolean isBounded() {
434 return true;
435 }
436
437 public boolean isEmpty() {
438 return false;
439 }
440
441
442
443
444 public CurveSet2D<? extends CubicBezierCurve2D> clip(Box2D box) {
445
446 CurveSet2D<SmoothCurve2D> set =
447 Curve2DUtils.clipSmoothCurve(this, box);
448
449
450 CurveArray2D<CubicBezierCurve2D> result =
451 new CurveArray2D<CubicBezierCurve2D>(set.getCurveNumber());
452
453
454 for (Curve2D curve : set.getCurves()) {
455 if (curve instanceof CubicBezierCurve2D)
456 result.addCurve((CubicBezierCurve2D) curve);
457 }
458 return result;
459 }
460
461 public Box2D getBoundingBox() {
462 Point2D p1 = this.getFirstPoint();
463 Point2D p2 = this.getControl1();
464 Point2D p3 = this.getControl2();
465 Point2D p4 = this.getLastPoint();
466 double xmin = Math.min(Math.min(p1.getX(), p2.getX()),
467 Math.min(p3.getX(), p4.getX()));
468 double xmax = Math.max(Math.max(p1.getX(), p2.getX()),
469 Math.max(p3.getX(), p4.getX()));
470 double ymin = Math.min(Math.min(p1.getY(), p2.getY()),
471 Math.min(p3.getY(), p4.getY()));
472 double ymax = Math.max(Math.max(p1.getY(), p2.getY()),
473 Math.max(p3.getY(), p4.getY()));
474 return new Box2D(xmin, xmax, ymin, ymax);
475 }
476
477
478
479
480
481 public CubicBezierCurve2D transform(AffineTransform2D trans) {
482 return new CubicBezierCurve2D(
483 trans.transform(this.getFirstPoint()),
484 trans.transform(this.getControl1()),
485 trans.transform(this.getControl2()),
486 trans.transform(this.getLastPoint()));
487 }
488
489 public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
490 Point2D p2 = this.getControl1();
491 Point2D p3 = this.getControl2();
492 Point2D p4 = this.getLastPoint();
493 path.curveTo(
494 p2.getX(), p2.getY(),
495 p3.getX(), p3.getY(),
496 p4.getX(), p4.getY());
497 return path;
498 }
499
500 public java.awt.geom.GeneralPath getGeneralPath() {
501 java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
502 Point2D p1 = this.getFirstPoint();
503 Point2D p2 = this.getControl1();
504 Point2D p3 = this.getControl2();
505 Point2D p4 = this.getLastPoint();
506 path.moveTo(p1.getX(), p1.getY());
507 path.curveTo(
508 p2.getX(), p2.getY(),
509 p3.getX(), p3.getY(),
510 p4.getX(), p4.getY());
511 return path;
512 }
513
514 @Override
515 public boolean equals(Object obj) {
516 if(!(obj instanceof CubicBezierCurve2D))
517 return false;
518
519
520 CubicBezierCurve2D bezier = (CubicBezierCurve2D) obj;
521
522
523 if(Math.abs(this.x1-bezier.x1)>Shape2D.ACCURACY) return false;
524 if(Math.abs(this.y1-bezier.y1)>Shape2D.ACCURACY) return false;
525 if(Math.abs(this.ctrlx1-bezier.ctrlx1)>Shape2D.ACCURACY) return false;
526 if(Math.abs(this.ctrly1-bezier.ctrly1)>Shape2D.ACCURACY) return false;
527 if(Math.abs(this.ctrlx2-bezier.ctrlx2)>Shape2D.ACCURACY) return false;
528 if(Math.abs(this.ctrly2-bezier.ctrly2)>Shape2D.ACCURACY) return false;
529 if(Math.abs(this.x2-bezier.x2)>Shape2D.ACCURACY) return false;
530 if(Math.abs(this.y2-bezier.y2)>Shape2D.ACCURACY) return false;
531
532 return true;
533 }
534
535 @Override
536 public CubicBezierCurve2D clone() {
537 return new CubicBezierCurve2D(
538 x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
539 }
540 }