1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.geometry;
17
18 import com.irurueta.algebra.AlgebraException;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.algebra.SingularValueDecomposer;
21 import com.irurueta.algebra.WrongSizeException;
22
23 import java.io.Serializable;
24
25
26
27
28 @SuppressWarnings("DuplicatedCode")
29 public class DualQuadric extends BaseQuadric implements Serializable {
30
31
32
33
34 public DualQuadric() {
35 super();
36 }
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public DualQuadric(
54 final double a, final double b, final double c, final double d, final double e, final double f,
55 final double g, final double h, final double i, final double j) {
56 super(a, b, c, d, e, f, g, h, i, j);
57 }
58
59
60
61
62
63
64
65
66
67
68
69 public DualQuadric(final Matrix m) throws NonSymmetricMatrixException {
70 super(m);
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 public DualQuadric(
92 final Plane plane1, final Plane plane2, final Plane plane3, final Plane plane4, final Plane plane5,
93 final Plane plane6, final Plane plane7, final Plane plane8, final Plane plane9)
94 throws CoincidentPlanesException {
95 setParametersFromPlanes(plane1, plane2, plane3, plane4, plane5, plane6, plane7, plane8, plane9);
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 public boolean isLocus(final Plane plane, final double threshold) {
113 if (threshold < MIN_THRESHOLD) {
114 throw new IllegalArgumentException();
115 }
116
117 try {
118 normalize();
119 final var dualQ = asMatrix();
120 final var homPlane = new Matrix(Plane.PLANE_NUMBER_PARAMS, 1);
121 plane.normalize();
122 homPlane.setElementAt(0, 0, plane.getA());
123 homPlane.setElementAt(1, 0, plane.getB());
124 homPlane.setElementAt(2, 0, plane.getC());
125 homPlane.setElementAt(3, 0, plane.getD());
126 final var locusMatrix = homPlane.transposeAndReturnNew();
127 locusMatrix.multiply(dualQ);
128 locusMatrix.multiply(homPlane);
129
130 return Math.abs(locusMatrix.getElementAt(0, 0)) < threshold;
131 } catch (final WrongSizeException ignore) {
132 return false;
133 }
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147 public boolean isLocus(final Plane plane) {
148 return isLocus(plane, DEFAULT_LOCUS_THRESHOLD);
149 }
150
151
152
153
154
155
156
157
158
159
160
161 public double angleBetweenPlanes(final Plane planeA, final Plane planeB) {
162 try {
163
164 normalize();
165 final var dualQ = asMatrix();
166 final var transHomPlaneA = new Matrix(1, Plane.PLANE_NUMBER_PARAMS);
167 planeA.normalize();
168 transHomPlaneA.setElementAt(0, 0, planeA.getA());
169 transHomPlaneA.setElementAt(0, 1, planeA.getB());
170 transHomPlaneA.setElementAt(0, 2, planeA.getC());
171 transHomPlaneA.setElementAt(0, 3, planeA.getD());
172
173 final var tmp = transHomPlaneA.multiplyAndReturnNew(dualQ);
174 tmp.multiply(transHomPlaneA.transposeAndReturnNew());
175
176
177 final var normA = tmp.getElementAt(0, 0);
178
179 final var homPlaneB = new Matrix(Plane.PLANE_NUMBER_PARAMS, 1);
180 planeB.normalize();
181 homPlaneB.setElementAt(0, 0, planeB.getA());
182 homPlaneB.setElementAt(1, 0, planeB.getB());
183 homPlaneB.setElementAt(2, 0, planeB.getC());
184 homPlaneB.setElementAt(3, 0, planeB.getD());
185
186 homPlaneB.transpose(tmp);
187 tmp.multiply(dualQ);
188 tmp.multiply(homPlaneB);
189
190 final var normB = tmp.getElementAt(0, 0);
191
192 transHomPlaneA.multiply(dualQ);
193 transHomPlaneA.multiply(homPlaneB);
194
195
196 final var angleNumerator = transHomPlaneA.getElementAt(0, 0);
197
198 final var cosTheta = angleNumerator / Math.sqrt(normA * normB);
199 return Math.acos(cosTheta);
200 } catch (final WrongSizeException ignore) {
201
202 return 0.0;
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 public boolean arePerpendicularPlanes(final Plane planeA, final Plane planeB, final double threshold) {
221 try {
222
223 final var transHomPlaneA = new Matrix(1, Plane.PLANE_NUMBER_PARAMS);
224 planeA.normalize();
225 transHomPlaneA.setElementAt(0, 0, planeA.getA());
226 transHomPlaneA.setElementAt(0, 1, planeA.getB());
227 transHomPlaneA.setElementAt(0, 2, planeA.getC());
228 transHomPlaneA.setElementAt(0, 3, planeA.getD());
229
230 final var homPlaneB = new Matrix(Point3D.POINT3D_HOMOGENEOUS_COORDINATES_LENGTH, 1);
231 planeB.normalize();
232 homPlaneB.setElementAt(0, 0, planeB.getA());
233 homPlaneB.setElementAt(1, 0, planeB.getB());
234 homPlaneB.setElementAt(2, 0, planeB.getC());
235 homPlaneB.setElementAt(3, 0, planeB.getD());
236
237 normalize();
238 final var dualQ = asMatrix();
239 transHomPlaneA.multiply(dualQ);
240 transHomPlaneA.multiply(homPlaneB);
241
242
243 final var perpend = transHomPlaneA.getElementAt(0, 0);
244
245 return Math.abs(perpend) < threshold;
246 } catch (final WrongSizeException ignore) {
247
248 return false;
249 }
250 }
251
252
253
254
255
256
257
258
259
260 public boolean arePerpendicularPlanes(final Plane planeA, final Plane planeB) {
261 return arePerpendicularPlanes(planeA, planeB, DEFAULT_PERPENDICULAR_THRESHOLD);
262 }
263
264
265
266
267
268
269
270
271
272 public Quadric getQuadric() throws QuadricNotAvailableException {
273 final var q = new Quadric();
274 quadric(q);
275 return q;
276 }
277
278
279
280
281
282
283
284
285
286
287 public void quadric(final Quadric quadric) throws QuadricNotAvailableException {
288 final var dualQuadricMatrix = asMatrix();
289 try {
290 final var invMatrix = com.irurueta.algebra.Utils.inverse(dualQuadricMatrix);
291
292 final var a = invMatrix.getElementAt(0, 0);
293 final var b = invMatrix.getElementAt(1, 1);
294 final var c = invMatrix.getElementAt(2, 2);
295 final var d = 0.5 * (invMatrix.getElementAt(0, 1) + invMatrix.getElementAt(1, 0));
296 final var e = 0.5 * (invMatrix.getElementAt(2, 1) + invMatrix.getElementAt(1, 2));
297 final var f = 0.5 * (invMatrix.getElementAt(2, 0) + invMatrix.getElementAt(0, 2));
298 final var g = 0.5 * (invMatrix.getElementAt(3, 0) + invMatrix.getElementAt(0, 3));
299 final double h = 0.5 * (invMatrix.getElementAt(3, 1)
300 + invMatrix.getElementAt(1, 3));
301 final var i = 0.5 * (invMatrix.getElementAt(3, 2) + invMatrix.getElementAt(2, 3));
302 final var j = invMatrix.getElementAt(3, 3);
303 quadric.setParameters(a, b, c, d, e, f, g, h, i, j);
304 } catch (final AlgebraException e) {
305 throw new QuadricNotAvailableException(e);
306 }
307 }
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325 public final void setParametersFromPlanes(
326 final Plane plane1, final Plane plane2, final Plane plane3, final Plane plane4, final Plane plane5,
327 final Plane plane6, final Plane plane7, final Plane plane8, final Plane plane9)
328 throws CoincidentPlanesException {
329
330
331 plane1.normalize();
332 plane2.normalize();
333 plane3.normalize();
334 plane4.normalize();
335 plane5.normalize();
336 plane6.normalize();
337 plane7.normalize();
338 plane8.normalize();
339 plane9.normalize();
340
341 try {
342
343
344
345
346
347 final var m = new Matrix(9, 10);
348 var pA = plane1.getA();
349 var pB = plane1.getB();
350 var pC = plane1.getC();
351 var pD = plane1.getD();
352 m.setElementAt(0, 0, pA * pA);
353 m.setElementAt(0, 1, pB * pB);
354 m.setElementAt(0, 2, pC * pC);
355 m.setElementAt(0, 3, 2.0 * pA * pB);
356 m.setElementAt(0, 4, 2.0 * pA * pC);
357 m.setElementAt(0, 5, 2.0 * pB * pC);
358 m.setElementAt(0, 6, 2.0 * pA * pD);
359 m.setElementAt(0, 7, 2.0 * pB * pD);
360 m.setElementAt(0, 8, 2.0 * pC * pD);
361 m.setElementAt(0, 9, pD * pD);
362 pA = plane2.getA();
363 pB = plane2.getB();
364 pC = plane2.getC();
365 pD = plane2.getD();
366 m.setElementAt(1, 0, pA * pA);
367 m.setElementAt(1, 1, pB * pB);
368 m.setElementAt(1, 2, pC * pC);
369 m.setElementAt(1, 3, 2.0 * pA * pB);
370 m.setElementAt(1, 4, 2.0 * pA * pC);
371 m.setElementAt(1, 5, 2.0 * pB * pC);
372 m.setElementAt(1, 6, 2.0 * pA * pD);
373 m.setElementAt(1, 7, 2.0 * pB * pD);
374 m.setElementAt(1, 8, 2.0 * pC * pD);
375 m.setElementAt(1, 9, pD * pD);
376 pA = plane3.getA();
377 pB = plane3.getB();
378 pC = plane3.getC();
379 pD = plane3.getD();
380 m.setElementAt(2, 0, pA * pA);
381 m.setElementAt(2, 1, pB * pB);
382 m.setElementAt(2, 2, pC * pC);
383 m.setElementAt(2, 3, 2.0 * pA * pB);
384 m.setElementAt(2, 4, 2.0 * pA * pC);
385 m.setElementAt(2, 5, 2.0 * pB * pC);
386 m.setElementAt(2, 6, 2.0 * pA * pD);
387 m.setElementAt(2, 7, 2.0 * pB * pD);
388 m.setElementAt(2, 8, 2.0 * pC * pD);
389 m.setElementAt(2, 9, pD * pD);
390 pA = plane4.getA();
391 pB = plane4.getB();
392 pC = plane4.getC();
393 pD = plane4.getD();
394 m.setElementAt(3, 0, pA * pA);
395 m.setElementAt(3, 1, pB * pB);
396 m.setElementAt(3, 2, pC * pC);
397 m.setElementAt(3, 3, 2.0 * pA * pB);
398 m.setElementAt(3, 4, 2.0 * pA * pC);
399 m.setElementAt(3, 5, 2.0 * pB * pC);
400 m.setElementAt(3, 6, 2.0 * pA * pD);
401 m.setElementAt(3, 7, 2.0 * pB * pD);
402 m.setElementAt(3, 8, 2.0 * pC * pD);
403 m.setElementAt(3, 9, pD * pD);
404 pA = plane5.getA();
405 pB = plane5.getB();
406 pC = plane5.getC();
407 pD = plane5.getD();
408 m.setElementAt(4, 0, pA * pA);
409 m.setElementAt(4, 1, pB * pB);
410 m.setElementAt(4, 2, pC * pC);
411 m.setElementAt(4, 3, 2.0 * pA * pB);
412 m.setElementAt(4, 4, 2.0 * pA * pC);
413 m.setElementAt(4, 5, 2.0 * pB * pC);
414 m.setElementAt(4, 6, 2.0 * pA * pD);
415 m.setElementAt(4, 7, 2.0 * pB * pD);
416 m.setElementAt(4, 8, 2.0 * pC * pD);
417 m.setElementAt(4, 9, pD * pD);
418 pA = plane6.getA();
419 pB = plane6.getB();
420 pC = plane6.getC();
421 pD = plane6.getD();
422 m.setElementAt(5, 0, pA * pA);
423 m.setElementAt(5, 1, pB * pB);
424 m.setElementAt(5, 2, pC * pC);
425 m.setElementAt(5, 3, 2.0 * pA * pB);
426 m.setElementAt(5, 4, 2.0 * pA * pC);
427 m.setElementAt(5, 5, 2.0 * pB * pC);
428 m.setElementAt(5, 6, 2.0 * pA * pD);
429 m.setElementAt(5, 7, 2.0 * pB * pD);
430 m.setElementAt(5, 8, 2.0 * pC * pD);
431 m.setElementAt(5, 9, pD * pD);
432 pA = plane7.getA();
433 pB = plane7.getB();
434 pC = plane7.getC();
435 pD = plane7.getD();
436 m.setElementAt(6, 0, pA * pA);
437 m.setElementAt(6, 1, pB * pB);
438 m.setElementAt(6, 2, pC * pC);
439 m.setElementAt(6, 3, 2.0 * pA * pB);
440 m.setElementAt(6, 4, 2.0 * pA * pC);
441 m.setElementAt(6, 5, 2.0 * pB * pC);
442 m.setElementAt(6, 6, 2.0 * pA * pD);
443 m.setElementAt(6, 7, 2.0 * pB * pD);
444 m.setElementAt(6, 8, 2.0 * pC * pD);
445 m.setElementAt(6, 9, pD * pD);
446 pA = plane8.getA();
447 pB = plane8.getB();
448 pC = plane8.getC();
449 pD = plane8.getD();
450 m.setElementAt(7, 0, pA * pA);
451 m.setElementAt(7, 1, pB * pB);
452 m.setElementAt(7, 2, pC * pC);
453 m.setElementAt(7, 3, 2.0 * pA * pB);
454 m.setElementAt(7, 4, 2.0 * pA * pC);
455 m.setElementAt(7, 5, 2.0 * pB * pC);
456 m.setElementAt(7, 6, 2.0 * pA * pD);
457 m.setElementAt(7, 7, 2.0 * pB * pD);
458 m.setElementAt(7, 8, 2.0 * pC * pD);
459 m.setElementAt(7, 9, pD * pD);
460 pA = plane9.getA();
461 pB = plane9.getB();
462 pC = plane9.getC();
463 pD = plane9.getD();
464 m.setElementAt(8, 0, pA * pA);
465 m.setElementAt(8, 1, pB * pB);
466 m.setElementAt(8, 2, pC * pC);
467 m.setElementAt(8, 3, 2.0 * pA * pB);
468 m.setElementAt(8, 4, 2.0 * pA * pC);
469 m.setElementAt(8, 5, 2.0 * pB * pC);
470 m.setElementAt(8, 6, 2.0 * pA * pD);
471 m.setElementAt(8, 7, 2.0 * pB * pD);
472 m.setElementAt(8, 8, 2.0 * pC * pD);
473 m.setElementAt(8, 9, pD * pD);
474
475
476 final var row = new double[10];
477 double rowNorm;
478 for (var j = 0; j < 9; j++) {
479 m.getSubmatrixAsArray(j, 0, j, 9, row);
480 rowNorm = com.irurueta.algebra.Utils.normF(row);
481 for (var i = 0; i < 10; i++)
482 m.setElementAt(j, i, m.getElementAt(j, i) / rowNorm);
483 }
484
485 final var decomposer = new SingularValueDecomposer(m);
486 decomposer.decompose();
487
488 if (decomposer.getRank() < 9) {
489 throw new CoincidentPlanesException();
490 }
491
492
493
494 final var v = decomposer.getV();
495
496 final var a = v.getElementAt(0, 9);
497 final var b = v.getElementAt(1, 9);
498 final var c = v.getElementAt(2, 9);
499 final var d = v.getElementAt(3, 9);
500
501 final var f = v.getElementAt(4, 9);
502 final var e = v.getElementAt(5, 9);
503
504 final var g = v.getElementAt(6, 9);
505 final var h = v.getElementAt(7, 9);
506 final var i = v.getElementAt(8, 9);
507 final var j = v.getElementAt(9, 9);
508
509 setParameters(a, b, c, d, e, f, g, h, i, j);
510 } catch (final AlgebraException ex) {
511 throw new CoincidentPlanesException(ex);
512 }
513 }
514
515
516
517
518
519
520
521
522
523
524
525 public static DualQuadric createCanonicalDualAbsoluteQuadric() {
526 return new DualQuadric(1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
527 }
528 }