1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package math.geom2d.conic;
28
29 import java.awt.Graphics2D;
30 import java.util.ArrayList;
31 import java.util.Collection;
32
33 import math.geom2d.AffineTransform2D;
34 import math.geom2d.Angle2D;
35 import math.geom2d.Box2D;
36 import math.geom2d.Point2D;
37 import math.geom2d.Shape2D;
38 import math.geom2d.UnboundedShapeException;
39 import math.geom2d.Vector2D;
40 import math.geom2d.curve.AbstractSmoothCurve2D;
41 import math.geom2d.curve.Curve2D;
42 import math.geom2d.curve.Curve2DUtils;
43 import math.geom2d.curve.CurveArray2D;
44 import math.geom2d.curve.CurveSet2D;
45 import math.geom2d.curve.SmoothCurve2D;
46 import math.geom2d.domain.ContinuousBoundary2D;
47 import math.geom2d.domain.Domain2D;
48 import math.geom2d.domain.GenericDomain2D;
49 import math.geom2d.line.LinearShape2D;
50 import math.geom2d.line.StraightLine2D;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class Parabola2D extends AbstractSmoothCurve2D
69 implements ContinuousBoundary2D, Conic2D, Cloneable {
70
71
72 protected double xv = 0, yv = 0;
73
74
75 protected double theta = 0;
76
77
78 protected double a = 1;
79
80 private boolean debug = false;
81
82
83
84
85
86
87
88
89 public final static Parabola2D create(Point2D vertex, Point2D focus) {
90 double p = Point2D.getDistance(vertex, focus);
91 double theta = Angle2D.getHorizontalAngle(vertex, focus)-Math.PI/2;
92 return new Parabola2D(vertex, 1/(4*p), theta);
93 }
94
95 public Parabola2D() {
96 super();
97 }
98
99 public Parabola2D(Point2D vertex, double a, double theta) {
100 this(vertex.getX(), vertex.getY(), a, theta);
101 }
102
103 public Parabola2D(double xv, double yv, double a, double theta) {
104 super();
105 this.xv = xv;
106 this.yv = yv;
107 this.a = a;
108 this.theta = theta;
109 }
110
111
112
113
114
115
116
117 public Point2D getFocus() {
118 double c = 1/a/4.0;
119 return new Point2D(xv-c*Math.sin(theta), yv+c*Math.cos(theta));
120 }
121
122 public double getParameter() {
123 return a;
124 }
125
126 public double getFocusDistance() {
127 return 1.0/(4*a);
128 }
129
130 public Point2D getVertex() {
131 return new Point2D(xv, yv);
132 }
133
134
135
136
137 public Vector2D getVector1() {
138 Vector2D vect = new Vector2D(1, 0);
139 return vect.transform(AffineTransform2D.createRotation(theta));
140 }
141
142
143
144
145 public Vector2D getVector2() {
146 Vector2D vect = new Vector2D(1, 0);
147 return vect
148 .transform(AffineTransform2D.createRotation(theta+Math.PI/2));
149 }
150
151
152
153
154
155 public double getAngle() {
156 return theta;
157 }
158
159
160
161
162 public boolean isDirect() {
163 return a>0;
164 }
165
166
167
168
169
170
171
172
173 private Point2D formatPoint(java.awt.geom.Point2D point) {
174 Point2D p2 = new Point2D(point);
175 p2 = p2.transform(AffineTransform2D.createTranslation(-xv, -yv));
176 p2 = p2.transform(AffineTransform2D.createRotation(-theta));
177 p2 = p2.transform(AffineTransform2D.createScaling(1, 1.0/a));
178 return p2;
179 }
180
181
182
183
184
185
186
187
188 private LinearShape2D formatLine(LinearShape2D line) {
189 line = line.transform(AffineTransform2D.createTranslation(-xv, -yv));
190 line = line.transform(AffineTransform2D.createRotation(-theta));
191 line = line.transform(AffineTransform2D.createScaling(1, 1.0/a));
192 return line;
193 }
194
195
196
197
198 public Conic2D.Type getConicType() {
199 return Conic2D.Type.PARABOLA;
200 }
201
202 public double[] getConicCoefficients() {
203
204
205
206
207
208
209
210
211
212
213
214
215 AffineTransform2D transform =
216 AffineTransform2D.createRotation(theta).chain(
217 AffineTransform2D.createTranslation(xv, yv));
218
219
220 double[][] coefs = transform.invert().getAffineMatrix();
221 double m00 = coefs[0][0];
222 double m01 = coefs[0][1];
223 double m02 = coefs[0][2];
224 double m10 = coefs[1][0];
225 double m11 = coefs[1][1];
226 double m12 = coefs[1][2];
227
228
229
230 double A = a*m00*m00;
231 double B = 2*a*m00*m01;
232 double C = a*m01*m01;
233 double D = 2*a*m00*m02 - m10;
234 double E = 2*a*m01*m02 - m11;
235 double F = a*m02*m02 - m12;
236
237
238 return new double[]{A, B, C, D, E, F};
239 }
240
241
242
243
244 public double getEccentricity() {
245 return 1.0;
246 }
247
248
249
250
251 public Collection<ContinuousBoundary2D> getBoundaryCurves() {
252 ArrayList<ContinuousBoundary2D> list = new ArrayList<ContinuousBoundary2D>(
253 1);
254 list.add(this);
255 return list;
256 }
257
258 public Domain2D getDomain() {
259 return new GenericDomain2D(this);
260 }
261
262
263
264
265 public double getWindingAngle(java.awt.geom.Point2D point) {
266 if (isDirect()) {
267 if (isInside(point))
268 return Math.PI*2;
269 else
270 return 0.0;
271 } else {
272 if (isInside(point))
273 return 0.0;
274 else
275 return -Math.PI*2;
276 }
277 }
278
279 public double getSignedDistance(java.awt.geom.Point2D p) {
280 return getSignedDistance(p.getX(), p.getY());
281 }
282
283 public double getSignedDistance(double x, double y) {
284 if (isInside(new Point2D(x, y)))
285 return -getDistance(x, y);
286 return -getDistance(x, y);
287 }
288
289 public boolean isInside(java.awt.geom.Point2D point) {
290
291
292 Point2D p2 = formatPoint(point);
293
294
295 double x = p2.getX();
296 double y = p2.getY();
297
298
299 return y>x*x^a<0;
300 }
301
302
303
304
305 public Vector2D getTangent(double t) {
306 Vector2D vect = new Vector2D(1, 2.0*a*t);
307 return vect.transform(AffineTransform2D.createRotation(theta));
308 }
309
310
311
312
313 public double getCurvature(double t) {
314 return 2*a/Math.pow(Math.hypot(1, 2*a*t), 3);
315 }
316
317
318
319
320
321
322
323 public boolean isClosed() {
324 return false;
325 }
326
327
328
329
330
331
332
333
334 public double getT0() {
335 return Double.NEGATIVE_INFINITY;
336 }
337
338
339
340
341
342 public double getT1() {
343 return Double.POSITIVE_INFINITY;
344 }
345
346 public Point2D getPoint(double t) {
347 Point2D point = new Point2D(t, a*t*t);
348 point = AffineTransform2D.createRotation(theta).transform(point);
349 point = AffineTransform2D.createTranslation(xv, yv).transform(point);
350 return point;
351 }
352
353
354
355
356
357
358 public double getPosition(java.awt.geom.Point2D point) {
359
360 return formatPoint(point).getX();
361 }
362
363
364
365
366
367
368 public double project(java.awt.geom.Point2D point) {
369
370 return formatPoint(point).getX();
371 }
372
373 public Collection<Point2D> getIntersections(LinearShape2D line) {
374
375 LinearShape2D line2 = this.formatLine(line);
376 double dx = line2.getVector().getX();
377 double dy = line2.getVector().getY();
378
379 ArrayList<Point2D> points = new ArrayList<Point2D>();
380
381
382 if (Math.abs(dx)<Shape2D.ACCURACY) {
383 if (debug)
384 System.out.println("intersect parabola with vertical line ");
385 double x = line2.getOrigin().getX();
386 Point2D point = new Point2D(x, x*x);
387 if (line2.contains(point))
388 points.add(line.getPoint(line2.getPosition(point)));
389 return points;
390 }
391
392
393 Point2D origin = line2.getOrigin();
394 double x0 = origin.getX();
395 double y0 = origin.getY();
396
397
398 double k = dy/dx;
399 double yl = k*x0-y0;
400 double delta = k*k-4*yl;
401
402
403 if (delta<0)
404 return points;
405
406
407
408
409 double x;
410 Point2D point;
411 StraightLine2D support = line2.getSupportingLine();
412
413
414 x = (k-Math.sqrt(delta))*.5;
415 point = new Point2D(x, x*x);
416 if (line2.contains(support.getProjectedPoint(point)))
417 points.add(line.getPoint(line2.getPosition(point)));
418
419
420 x = (k+Math.sqrt(delta))*.5;
421 point = new Point2D(x, x*x);
422 if (line2.contains(support.getProjectedPoint(point)))
423 points.add(line.getPoint(line2.getPosition(point)));
424
425 return points;
426 }
427
428
429
430
431
432 public Parabola2D getReverseCurve() {
433 return new Parabola2D(xv, yv, -a, Angle2D.formatAngle(theta+Math.PI));
434 }
435
436
437
438
439 public ParabolaArc2D getSubCurve(double t0, double t1) {
440 if (debug)
441 System.out.println("theta = "+Math.toDegrees(theta));
442 if (t1<t0)
443 return null;
444 return new ParabolaArc2D(this, t0, t1);
445 }
446
447 public double getDistance(java.awt.geom.Point2D p) {
448 return getDistance(p.getX(), p.getY());
449 }
450
451 public double getDistance(double x, double y) {
452
453
454 return new ParabolaArc2D(this, -100, 100).getDistance(x, y);
455 }
456
457
458
459
460
461 public java.awt.geom.GeneralPath appendPath(
462 java.awt.geom.GeneralPath path) {
463 throw new UnboundedShapeException(this);
464 }
465
466
467 public void fill(Graphics2D g2) {
468 throw new UnboundedShapeException(this);
469 }
470
471
472
473
474
475 public boolean isBounded() {
476 return false;
477 }
478
479
480
481
482 public boolean isEmpty() {
483 return false;
484 }
485
486
487
488
489
490
491
492 public CurveSet2D<ParabolaArc2D> clip(Box2D box) {
493
494 CurveSet2D<SmoothCurve2D> set = Curve2DUtils.clipSmoothCurve(this, box);
495
496
497 CurveArray2D<ParabolaArc2D> result =
498 new CurveArray2D<ParabolaArc2D>(set.getCurveNumber());
499
500
501 for (Curve2D curve : set.getCurves()) {
502 if (curve instanceof ParabolaArc2D)
503 result.addCurve((ParabolaArc2D) curve);
504 }
505 return result;
506 }
507
508 public Box2D getBoundingBox() {
509
510 return new Box2D(
511 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
512 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
513 }
514
515
516
517
518
519
520 public Parabola2D transform(AffineTransform2D trans) {
521
522 Point2D vertex = this.getVertex().transform(trans);
523 Point2D focus = this.getFocus().transform(trans);
524 double a = 1/(4.0*Point2D.getDistance(vertex, focus));
525 double theta = Angle2D.getHorizontalAngle(vertex, focus)-Math.PI/2;
526
527
528 if (this.a<0^trans.isDirect())
529
530 return new Parabola2D(vertex, a, theta);
531 else
532
533 return new Parabola2D(vertex, -a, theta+Math.PI);
534 }
535
536
537
538
539 public boolean contains(double x, double y) {
540
541 Point2D p2 = formatPoint(new Point2D(x, y));
542
543
544 double xp = p2.getX();
545 double yp = p2.getY();
546
547
548 return Math.abs(yp-xp*xp)<Shape2D.ACCURACY;
549 }
550
551 public boolean contains(java.awt.geom.Point2D point) {
552 return contains(point.getX(), point.getY());
553 }
554
555
556
557
558 @Override
559 public String toString() {
560 return String.format("Parabola2D(%f,%f,%f,%f)",
561 xv, yv, a, theta);
562 }
563
564 @Override
565 public boolean equals(Object obj) {
566 if (!(obj instanceof Parabola2D))
567 return false;
568 Parabola2D parabola = (Parabola2D) obj;
569
570 if ((this.xv-parabola.xv)>Shape2D.ACCURACY)
571 return false;
572 if ((this.yv-parabola.yv)>Shape2D.ACCURACY)
573 return false;
574 if ((this.a-parabola.a)>Shape2D.ACCURACY)
575 return false;
576 if (!Angle2D.equals(this.theta, parabola.theta))
577 return false;
578
579 return true;
580 }
581
582 @Override
583 public Parabola2D clone() {
584 return new Parabola2D(xv, yv, a, theta);
585 }
586 }