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 package math.geom2d.conic;
27
28 import java.awt.Graphics2D;
29 import java.util.ArrayList;
30 import java.util.Collection;
31
32 import math.geom2d.AffineTransform2D;
33 import math.geom2d.Angle2D;
34 import math.geom2d.Point2D;
35 import math.geom2d.Shape2D;
36 import math.geom2d.UnboundedShapeException;
37 import math.geom2d.Vector2D;
38 import math.geom2d.domain.BoundarySet2D;
39 import math.geom2d.line.LinearShape2D;
40 import math.geom2d.line.StraightLine2D;
41
42
43
44
45
46
47
48 public class Hyperbola2D extends BoundarySet2D<HyperbolaBranch2D>
49 implements Conic2D, Cloneable {
50
51
52
53
54
55
56
57
58 protected double xc = 0;
59 protected double yc = 0;
60
61
62 protected double a = 1;
63
64
65 protected double b = 1;
66
67
68 protected double theta = 0;
69
70
71 protected boolean direct = true;
72
73
74 protected HyperbolaBranch2D branch1 = null;
75
76
77 protected HyperbolaBranch2D branch2 = null;
78
79
80
81
82
83
84
85
86 public Hyperbola2D() {
87 this(0, 0, 1, 1, 0, true);
88 }
89
90 public Hyperbola2D(Point2D center, double a, double b, double theta) {
91 this(center.getX(), center.getY(), a, b, theta, true);
92 }
93
94 public Hyperbola2D(Point2D center, double a, double b, double theta,
95 boolean d) {
96 this(center.getX(), center.getY(), a, b, theta, d);
97 }
98
99 public Hyperbola2D(double xc, double yc, double a, double b, double theta) {
100 this(xc, yc, a, b, theta, true);
101 }
102
103
104 public Hyperbola2D(double xc, double yc, double a, double b, double theta,
105 boolean d) {
106 this.xc = xc;
107 this.yc = yc;
108 this.a = a;
109 this.b = b;
110 this.theta = theta;
111 this.direct = d;
112
113 branch1 = new HyperbolaBranch2D(this, false);
114 branch2 = new HyperbolaBranch2D(this, true);
115 this.addCurve(branch1);
116 this.addCurve(branch2);
117 }
118
119
120
121
122
123 public static Hyperbola2D create(Point2D center, double a, double b,
124 double theta) {
125 return new Hyperbola2D(center.getX(), center.getY(), a, b, theta, true);
126 }
127
128 public static Hyperbola2D create(Point2D center, double a, double b,
129 double theta, boolean d) {
130 return new Hyperbola2D(center.getX(), center.getY(), a, b, theta, d);
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 public static Hyperbola2D reduceCentered(double[] coefs) {
147 double A = coefs[0];
148 double B = coefs[1];
149 double C = coefs[2];
150
151
152 double theta;
153 if (Math.abs(A-C)<Shape2D.ACCURACY) {
154 theta = Math.PI/4;
155 } else {
156 theta = Math.atan2(B, (A-C))/2.0;
157 if (B<0)
158 theta -= Math.PI;
159 theta = Angle2D.formatAngle(theta);
160 }
161
162
163 double[] coefs2 = Conic2DUtils.transformCentered(coefs,
164 AffineTransform2D.createRotation(-theta));
165
166
167 double f = 1;
168 if (coefs2.length>5)
169 f = Math.abs(coefs[5]);
170
171 assert Math.abs(coefs2[1]/f)<Shape2D.ACCURACY :
172 "Second conic coefficient should be zero";
173
174 assert coefs2[0]*coefs2[2]<0:
175 "Transformed conic is not an Hyperbola";
176
177
178
179 double r1, r2;
180 if (coefs2[0]>0) {
181
182 r1 = Math.sqrt(f/coefs2[0]);
183 r2 = Math.sqrt(-f/coefs2[2]);
184 } else {
185
186 r1 = Math.sqrt(f/coefs2[2]);
187 r2 = Math.sqrt(-f/coefs2[0]);
188 theta = Angle2D.formatAngle(theta+Math.PI/2);
189 theta = Math.min(theta, Angle2D.formatAngle(theta+Math.PI));
190 }
191
192
193 return new Hyperbola2D(0, 0, r1, r2, theta, true);
194 }
195
196
197
198
199
200
201
202
203
204 public static Hyperbola2D transformCentered(Hyperbola2D hyper,
205 AffineTransform2D trans) {
206
207 double a = hyper.a;
208 double b = hyper.b;
209 double theta = hyper.theta;
210
211
212 double aSq = a*a;
213 double bSq = b*b;
214 double cot = Math.cos(theta);
215 double sit = Math.sin(theta);
216 double cotSq = cot*cot;
217 double sitSq = sit*sit;
218
219
220 double A = cotSq/aSq-sitSq/bSq;
221 double B = 2*cot*sit*(1/aSq+1/bSq);
222 double C = sitSq/aSq-cotSq/bSq;
223 double[] coefs = new double[] { A, B, C };
224
225
226 double[] coefs2 = Conic2DUtils.transformCentered(coefs, trans);
227
228
229 return Hyperbola2D.reduceCentered(coefs2);
230 }
231
232
233
234
235
236
237
238
239
240 public Point2D toGlobal(Point2D point) {
241 point = point.transform(AffineTransform2D.createScaling(a, b));
242 point = point.transform(AffineTransform2D.createRotation(theta));
243 point = point.transform(AffineTransform2D.createTranslation(xc, yc));
244 return point;
245 }
246
247 public Point2D toLocal(Point2D point) {
248 point = point.transform(AffineTransform2D.createTranslation(-xc, -yc));
249 point = point.transform(AffineTransform2D.createRotation(-theta));
250 point = point.transform(AffineTransform2D.createScaling(1/a, 1/b));
251 return point;
252 }
253
254
255
256
257
258
259
260
261 private LinearShape2D formatLine(LinearShape2D line) {
262 line = line.transform(AffineTransform2D.createTranslation(-xc, -yc));
263 line = line.transform(AffineTransform2D.createRotation(-theta));
264 line = line.transform(AffineTransform2D.createScaling(1.0/a, 1.0/b));
265 return line;
266 }
267
268
269
270
271
272
273 public Point2D getCenter() {
274 return new Point2D(xc, yc);
275 }
276
277
278
279
280
281 public double getAngle() {
282 return theta;
283 }
284
285
286 public double getLength1() {
287 return a;
288 }
289
290
291 public double getLength2() {
292 return b;
293 }
294
295 public boolean isDirect() {
296 return direct;
297 }
298
299 public Vector2D getVector1() {
300 return new Vector2D(Math.cos(theta), Math.sin(theta));
301 }
302
303 public Vector2D getVector2() {
304 return new Vector2D(-Math.sin(theta), Math.cos(theta));
305 }
306
307
308
309
310
311 public Point2D getFocus1() {
312 double c = Math.hypot(a, b);
313 return new Point2D(xc+c*Math.cos(theta), yc+c*Math.sin(theta));
314 }
315
316
317
318
319
320 public Point2D getFocus2() {
321 double c = Math.hypot(a, b);
322 return new Point2D(xc-c*Math.cos(theta), yc-c*Math.sin(theta));
323 }
324
325 public HyperbolaBranch2D getPositiveBranch() {
326 return branch2;
327 }
328
329 public HyperbolaBranch2D getNegativeBranch() {
330 return branch1;
331 }
332
333 public Collection<HyperbolaBranch2D> getBranches() {
334 ArrayList<HyperbolaBranch2D> array =
335 new ArrayList<HyperbolaBranch2D>(2);
336 array.add(branch1);
337 array.add(branch2);
338 return array;
339 }
340
341
342
343
344 public Collection<StraightLine2D> getAsymptotes() {
345
346 Vector2D v1 = new Vector2D(a, b);
347 Vector2D v2 = new Vector2D(a, -b);
348
349
350 AffineTransform2D rot = AffineTransform2D.createRotation(this.theta);
351 v1 = v1.transform(rot);
352 v2 = v2.transform(rot);
353
354
355 ArrayList<StraightLine2D> array = new ArrayList<StraightLine2D>(2);
356
357
358 Point2D center = this.getCenter();
359 array.add(new StraightLine2D(center, v1));
360 array.add(new StraightLine2D(center, v2));
361
362
363 return array;
364 }
365
366
367
368
369 public double[] getConicCoefficients() {
370
371 double aSq = this.a*this.a;
372 double bSq = this.b*this.b;
373 double aSqInv = 1.0/aSq;
374 double bSqInv = 1.0/bSq;
375
376
377 double sint = Math.sin(this.theta);
378 double cost = Math.cos(this.theta);
379 double sin2t = 2.0*sint*cost;
380 double sintSq = sint*sint;
381 double costSq = cost*cost;
382
383
384 double xcSq = xc*xc;
385 double ycSq = yc*yc;
386
387
388
389
390
391
392 double a = costSq/aSq-sintSq/bSq;
393 double b = (bSq+aSq)*sin2t/(aSq*bSq);
394 double c = sintSq/aSq-costSq/bSq;
395 double d = -yc*b-2*xc*a;
396 double e = -xc*b-2*yc*c;
397 double f = -1.0+(xcSq+ycSq)*(aSqInv-bSqInv)/2.0+(costSq-sintSq)
398 *(xcSq-ycSq)*(aSqInv+bSqInv)/2.0+xc*yc*(aSqInv+bSqInv)*sin2t;
399
400
401
402
403
404 return new double[] { a, b, c, d, e, f };
405 }
406
407 public Conic2D.Type getConicType() {
408 return Conic2D.Type.HYPERBOLA;
409 }
410
411 public double getEccentricity() {
412 return Math.hypot(1, b*b/a/a);
413 }
414
415
416
417
418
419 @Override
420 public Hyperbola2D getReverseCurve() {
421 return new Hyperbola2D(this.xc, this.yc, this.a, this.b, this.theta,
422 !this.direct);
423 }
424
425 @Override
426 public Collection<Point2D> getIntersections(LinearShape2D line) {
427
428 Collection<Point2D> points = new ArrayList<Point2D>();
429
430
431 LinearShape2D line2 = formatLine(line);
432
433
434 Point2D origin = line2.getOrigin();
435 double dx = line2.getVector().getX();
436 double dy = line2.getVector().getY();
437
438
439
440
441 if (Math.abs(dx)>Math.abs(dy)) {
442
443
444
445 double k = dy/dx;
446 double yi = origin.getY()-k*origin.getX();
447
448
449 double a = 1-k*k;
450 double b = -2*k*yi;
451 double c = -yi*yi-1;
452
453 double delta = b*b-4*a*c;
454 if (delta<=0) {
455 System.out.println(
456 "Intersection with horizontal line should alays give positive delta");
457 return points;
458 }
459
460
461 double x1 = (-b-Math.sqrt(delta))/(2*a);
462 double x2 = (-b+Math.sqrt(delta))/(2*a);
463
464
465 StraightLine2D support = line2.getSupportingLine();
466
467
468 double pos1 = support.project(new Point2D(x1, k*x1+yi));
469 if (line2.contains(support.getPoint(pos1)))
470 points.add(line.getPoint(pos1));
471
472
473 double pos2 = support.project(new Point2D(x2, k*x2+yi));
474 if (line2.contains(support.getPoint(pos2)))
475 points.add(line.getPoint(pos2));
476
477 } else {
478
479
480
481 double k = dx/dy;
482 double xi = origin.getX()-k*origin.getY();
483
484
485 double a = k*k-1;
486 double b = 2*k*xi;
487 double c = xi*xi-1;
488
489 double delta = b*b-4*a*c;
490 if (delta<=0) {
491
492 return points;
493 }
494
495
496 double y1 = (-b-Math.sqrt(delta))/(2*a);
497 double y2 = (-b+Math.sqrt(delta))/(2*a);
498
499
500 StraightLine2D support = line2.getSupportingLine();
501
502
503 double pos1 = support.project(new Point2D(k*y1+xi, y1));
504 if (line2.contains(support.getPoint(pos1)))
505 points.add(line.getPoint(pos1));
506
507
508 double pos2 = support.project(new Point2D(k*y2+xi, y2));
509 if (line2.contains(support.getPoint(pos2)))
510 points.add(line.getPoint(pos2));
511 }
512
513 return points;
514 }
515
516
517
518
519 @Override
520 public boolean contains(java.awt.geom.Point2D point) {
521 return this.contains(point.getX(), point.getY());
522 }
523
524 @Override
525 public boolean contains(double x, double y) {
526 Point2D point = toLocal(new Point2D(x, y));
527 double xa = point.getX()/a;
528 double yb = point.getY()/b;
529 double res = xa*xa-yb*yb-1;
530
531 return Math.abs(res)<1e-6;
532 }
533
534
535
536
537 @Override
538 public Hyperbola2D transform(AffineTransform2D trans) {
539 Hyperbola2D result = Hyperbola2D.transformCentered(this, trans);
540 Point2D center = this.getCenter().transform(trans);
541 result.xc = center.getX();
542 result.yc = center.getY();
543
544 result.direct = this.direct^!trans.isDirect();
545 return result;
546 }
547
548
549 @Override
550 public void draw(Graphics2D g) {
551 throw new UnboundedShapeException(this);
552 }
553
554
555
556
557 @Override
558 public boolean equals(Object obj) {
559 if (!(obj instanceof Hyperbola2D))
560 return false;
561
562
563 Hyperbola2D that = (Hyperbola2D) obj;
564
565
566 double eps = 1e-6;
567 if (Math.abs(that.xc-this.xc)>eps)
568 return false;
569 if (Math.abs(that.yc-this.yc)>eps)
570 return false;
571 if (Math.abs(that.a-this.a)>eps)
572 return false;
573 if (Math.abs(that.b-this.b)>eps)
574 return false;
575 if (Math.abs(that.theta-this.theta)>eps)
576 return false;
577 if (this.direct!=that.direct)
578 return false;
579
580
581 return true;
582 }
583
584 @Override
585 public Hyperbola2D clone() {
586 return new Hyperbola2D(xc, yc, a, b, theta, direct);
587 }
588 }