1
2
3
4
5 package math.geom2d.conic;
6
7 import math.geom2d.AffineTransform2D;
8 import math.geom2d.Angle2D;
9 import math.geom2d.Point2D;
10 import math.geom2d.Shape2D;
11 import math.geom2d.domain.BoundarySet2D;
12 import math.geom2d.line.StraightLine2D;
13
14
15
16
17
18
19
20 public class Conic2DUtils {
21
22 public final static Conic2D reduceConic(double[] coefs) {
23 if (coefs.length<6) {
24 System.err.println(
25 "Conic2DUtils.reduceConic: must provide 6 coefficients");
26 return null;
27 }
28 boolean debug = false;
29
30
31 double eps = Shape2D.ACCURACY;
32
33
34 double a = coefs[0];
35 double b = coefs[1];
36 double c = coefs[2];
37 double d = coefs[3];
38 double e = coefs[4];
39 double f = coefs[5];
40
41
42
43
44
45
46 double a1, b1, c1, d1, e1, f1;
47
48 double theta0 = 0;
49
50 if (Math.abs(b)<eps) {
51
52 a1 = a;
53 b1 = b;
54 c1 = c;
55 d1 = d;
56 e1 = e;
57 f1 = f;
58 theta0 = 0;
59 } else {
60
61 if (Math.abs(a-c)<eps)
62 theta0 = Math.PI/4;
63 else
64 theta0 = Angle2D.formatAngle(Math.atan2(b, a-c)/2);
65
66 if(debug)
67 System.out.println("conic main angle: " +
68 Math.toDegrees(theta0));
69
70
71 double cot = Math.cos(theta0);
72 double sit = Math.sin(theta0);
73 double co2t = Math.cos(2*theta0);
74 double si2t = Math.sin(2*theta0);
75 double cot2 = cot*cot;
76 double sit2 = sit*sit;
77
78
79 a1 = a*cot2+b*sit*cot+c*sit2;
80 b1 = si2t*(c-a)+b*co2t;
81 c1 = a*sit2-b*sit*cot+c*cot2;
82 d1 = d*cot+e*sit;
83 e1 = -d*sit+e*cot;
84 f1 = f;
85 }
86
87
88 if (Math.abs(b1)>eps) {
89 System.err.println(
90 "Conic2DUtils.reduceConic: " +
91 "conic was not correctly transformed");
92 return null;
93 }
94
95
96 if (Math.abs(a)<eps&&Math.abs(c)<eps) {
97 if (Math.abs(d)>eps||Math.abs(e)>eps)
98 return new ConicStraightLine2D(d, e, f);
99 else
100 return null;
101 }
102
103
104 if (Math.abs(a1)<eps) {
105
106 if (debug)
107 System.out.println("horizontal parabola");
108
109
110 if (Math.abs(d1)<eps) {
111 double delta = e1*e1-4*c1*f1;
112 if (delta>=0) {
113
114 double ys = -e1/2.0/c1;
115 double dist = Math.sqrt(delta)/2.0/c1;
116 Point2D center = new Point2D(0, ys).transform(
117 AffineTransform2D.createRotation(theta0));
118 return new ConicTwoLines2D(center, dist, theta0);
119 } else
120 return null;
121 }
122
123
124 double c2 = -c1/d1;
125 double e2 = -e1/d1;
126 double f2 = -f1/d1;
127
128
129 double xs = -(e2*e2-4*c2*f2)/(4*c2);
130 double ys = -e2*.5/c2;
131
132
133 return new Parabola2D(xs, ys, c2, theta0-Math.PI/2);
134
135 } else if (Math.abs(c1)<eps) {
136
137 if (debug)
138 System.out.println("vertical parabola");
139
140
141 if (Math.abs(e1)<eps) {
142 double delta = d1*d1-4*a1*f1;
143 if (delta>=0) {
144
145 double xs = -d1/2.0/a1;
146 double dist = Math.sqrt(delta)/2.0/a1;
147 Point2D center = new Point2D(0, xs).transform(
148 AffineTransform2D.createRotation(theta0));
149 return new ConicTwoLines2D(center, dist, theta0);
150 } else
151 return null;
152 }
153
154
155 double a2 = -a1/e1;
156 double d2 = -d1/e1;
157 double f2 = -f1/e1;
158
159
160 double xs = -d2*.5/a2;
161 double ys = -(d2*d2-4*a2*f2)/(4*a2);
162
163
164 return new Parabola2D(xs, ys, a2, theta0);
165 }
166
167
168
169
170 Point2D center = new Point2D(-d1/(2*a1), -e1/(2*c1));
171 center = center.transform(AffineTransform2D.createRotation(theta0));
172
173
174 double num = (c1*d1*d1+a1*e1*e1-4*a1*c1*f1)/(4*a1*c1);
175 double at = num/a1;
176 double bt = num/c1;
177
178 if (at<0&&bt<0) {
179 System.err.println(
180 "Conic2DUtils.reduceConic(): found A<0 and C<0");
181 return null;
182 }
183
184
185 if (at>0&&bt>0) {
186 if (debug)
187 System.out.println("ellipse");
188 if (at>bt)
189 return new Ellipse2D(center, Math.sqrt(at), Math.sqrt(bt),
190 theta0);
191 else
192 return new Ellipse2D(center, Math.sqrt(bt), Math.sqrt(at),
193 Angle2D.formatAngle(theta0+Math.PI/2));
194 }
195
196
197
198
199 if (at>0) {
200 if (debug)
201 System.out.println("east-west hyperbola");
202 return new Hyperbola2D(center, Math.sqrt(at), Math.sqrt(-bt),
203 theta0);
204 } else {
205 if (debug)
206 System.out.println("north-south hyperbola");
207 return new Hyperbola2D(center, Math.sqrt(bt), Math.sqrt(-at),
208 theta0+Math.PI/2);
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222 public final static double[] transformCentered(double[] coefs,
223 AffineTransform2D trans) {
224
225 double[][] mat = trans.getAffineMatrix();
226 double a = mat[0][0];
227 double b = mat[1][0];
228 double c = mat[0][1];
229 double d = mat[1][1];
230
231
232 double A = coefs[0];
233 double B = coefs[1];
234 double C = coefs[2];
235
236
237 double delta = a*d-b*c;
238 delta = delta*delta;
239
240 double A2 = (A*d*d+C*b*b-B*b*d)/delta;
241 double B2 = (B*(a*d+b*c)-2*(A*c*d+C*a*b))/delta;
242 double C2 = (A*c*c+C*a*a-B*a*c)/delta;
243
244
245 if (coefs.length==3)
246 return new double[] { A2, B2, C2 };
247
248
249 double D = coefs[3];
250 double E = coefs[4];
251 double F = coefs[5];
252 double D2 = D*d-E*b;
253 double E2 = E*a-D*c;
254 return new double[] { A2, B2, C2, D2, E2, F };
255 }
256
257
258
259
260
261
262
263
264 public final static double[] transform(double[] coefs,
265 AffineTransform2D trans) {
266
267 double[][] mat = trans.invert().getAffineMatrix();
268 double a = mat[0][0];
269 double b = mat[1][0];
270 double c = mat[0][1];
271 double d = mat[1][1];
272 double e = mat[0][2];
273 double f = mat[1][2];
274
275
276 double A = coefs[0];
277 double B = coefs[1];
278 double C = coefs[2];
279 double D = coefs[3];
280 double E = coefs[4];
281 double F = coefs[5];
282
283
284 double A2 = A*a*a+B*a*b+C*b*b;
285 double B2 = 2*(A*a*c+C*b*d)+B*(a*d+b*c);
286 double C2 = A*c*c+B*c*d+C*d*d;
287 double D2 = 2*(A*a*e+C*b*f)+B*(a*f+b*e)+D*a+E*b;
288 double E2 = 2*(A*c*e+C*d*f)+B*(c*f+d*e)+D*c+E*d;
289 double F2 = A*e*e+B*e*f+C*f*f+D*e+E*f+F;
290
291
292 return new double[] { A2, B2, C2, D2, E2, F2 };
293 }
294
295
296
297
298 static class ConicStraightLine2D extends StraightLine2D implements Conic2D {
299
300 double[] coefs = new double[] { 0, 0, 0, 1, 0, 0 };
301
302 public ConicStraightLine2D(StraightLine2D line) {
303 super(line);
304 coefs = new double[] { 0, 0, 0, dy, -dx, dx*y0-dy*x0 };
305 }
306
307 public ConicStraightLine2D(double a, double b, double c) {
308 super(StraightLine2D.createCartesian(a, b, c));
309 coefs = new double[] { 0, 0, 0, a, b, c };
310 }
311
312 public double[] getConicCoefficients() {
313 return coefs;
314 }
315
316 public Type getConicType() {
317 return Conic2D.Type.STRAIGHT_LINE;
318 }
319
320
321 public double getEccentricity() {
322 return Double.NaN;
323 }
324
325 @Override
326 public ConicStraightLine2D getReverseCurve() {
327 return new ConicStraightLine2D(super.getReverseCurve());
328 }
329
330 @Override
331 public ConicStraightLine2D transform(AffineTransform2D trans) {
332 return new ConicStraightLine2D(super.transform(trans));
333 }
334 }
335
336 static class ConicTwoLines2D extends BoundarySet2D<StraightLine2D>
337 implements Conic2D {
338
339 double xc = 0, yc = 0, d = 1, theta = 0;
340
341 public ConicTwoLines2D(Point2D point, double d, double theta) {
342 this(point.x, point.y, d, theta);
343 }
344
345 public ConicTwoLines2D(double xc, double yc, double d, double theta) {
346 super();
347
348 this.xc = xc;
349 this.yc = yc;
350 this.d = d;
351 this.theta = theta;
352
353 StraightLine2D baseLine = StraightLine2D.create(
354 new Point2D(xc, yc), theta);
355 this.addCurve(baseLine.getParallel(d));
356 this.addCurve(baseLine.getParallel(-d).getReverseCurve());
357 }
358
359 public double[] getConicCoefficients() {
360 double[] coefs = { 0, 0, 1, 0, 0, -1 };
361 AffineTransform2D sca = AffineTransform2D.createScaling(0, d), rot = AffineTransform2D
362 .createRotation(theta), tra = AffineTransform2D
363 .createTranslation(xc, yc);
364
365 AffineTransform2D trans = sca.chain(rot).chain(tra);
366 return Conic2DUtils.transform(coefs, trans);
367 }
368
369 public Type getConicType() {
370 return Conic2D.Type.TWO_LINES;
371 }
372
373 public double getEccentricity() {
374 return Double.NaN;
375 }
376
377 @Override
378 public ConicTwoLines2D transform(AffineTransform2D trans) {
379 Point2D center = new Point2D(xc, yc).transform(trans);
380 StraightLine2D line = this.getFirstCurve().transform(trans);
381
382 return new ConicTwoLines2D(center, line.getDistance(center), line
383 .getHorizontalAngle());
384 }
385
386 @Override
387 public ConicTwoLines2D getReverseCurve() {
388 return new ConicTwoLines2D(xc, yc, -d, theta);
389 }
390 }
391
392
393 }