1 /*
2 * Copyright (C) 2017 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.irurueta.geometry;
18
19 import java.io.Serializable;
20 import java.util.Objects;
21
22 /**
23 * Subclass of Point3D defining an homogeneous 3D point.
24 * An homogeneous 3D point is defined by four coordinates: (x,y,z,w), where
25 * x, y and z are the horizontal, vertical and depth coordinates, respectively,
26 * and w is a normalization (homogenization) factor. Homogeneous 3D points at
27 * infinity are expressed using w=0 (x,y,z,0) where (x,y,z) describe the
28 * direction of the 3D point towards infinity.
29 * Inhomogeneous 3D points can be transformed into homogeneous 3D points by
30 * setting the w coordinate to one (w=1, not at infinity) as follows:
31 * Inhomogeneous 3D point (x,y,z) -< Homogeneous 3D point (x,y,z,1).
32 */
33 public class HomogeneousPoint3D extends Point3D implements Serializable {
34
35 /**
36 * Default threshold to consider a point is located at infinity.
37 */
38 private static final double DEFAULT_INFINITY_THRESHOLD = 1e-10;
39
40 /**
41 * Machine precision.
42 */
43 private static final double PRECISION = 1e-12;
44
45 /**
46 * Defines the X coordinate of an homogeneous 2D point.
47 */
48 private double x;
49
50 /**
51 * Defines the Y coordinate of an homogeneous 2D point.
52 */
53 private double y;
54
55 /**
56 * Defines the Z coordinate of an homogeneous 2D point.
57 */
58 private double z;
59
60 /**
61 * Defines the W coordinate of an homogeneous 2D point.
62 */
63 private double w;
64
65 /**
66 * Determines whether this point is already normalized.
67 */
68 private boolean normalized;
69
70 /**
71 * Empty constructor.
72 */
73 public HomogeneousPoint3D() {
74 super();
75 x = y = z = 0.0;
76 w = 1.0;
77 normalized = false;
78 }
79
80 /**
81 * Constructor of this class. This constructor sets a new homogeneous
82 * v array containing the coordinates X, Y, Z and W of the given point.
83 *
84 * @param v Array of length 4 containing the 3D coordinates of an
85 * homogeneous point.
86 * @throws IllegalArgumentException Raised when the size of the array is
87 * different of 4.
88 */
89 public HomogeneousPoint3D(final double[] v) {
90 super();
91 setCoordinates(v);
92 }
93
94 /**
95 * Constructor of this class. This constructor sets a new homogeneous 3D
96 * point using the coordinates X, Y, Z and W of the given point.
97 *
98 * @param x X coordinate of the given 3D point.
99 * @param y Y coordinate of the given 3D point.
100 * @param z Z coordinate of the given 3D point.
101 * @param w W coordinate of the given 3D point.
102 */
103 public HomogeneousPoint3D(final double x, final double y, final double z, final double w) {
104 this.x = x;
105 this.y = y;
106 this.z = z;
107 this.w = w;
108 normalized = false;
109 }
110
111 /**
112 * This constructor sets a new homogeneous 3D point using as initialization
113 * provided Point3D instance.
114 *
115 * @param point Point to initialize new instance to
116 */
117 public HomogeneousPoint3D(final Point3D point) {
118 setCoordinates(point);
119 }
120
121 /**
122 * Returns the X coordinate of the given homogeneous 3D point instance.
123 *
124 * @return X coordinate.
125 */
126 public double getX() {
127 return x;
128 }
129
130 /**
131 * Sets the X coordinate of this homogeneous point.
132 *
133 * @param x X coordinate.
134 */
135 public void setX(final double x) {
136 this.x = x;
137 normalized = false;
138 }
139
140 /**
141 * Returns the Y coordinate of the given homogeneous 3D point instance.
142 *
143 * @return Y coordinate.
144 */
145 public double getY() {
146 return y;
147 }
148
149 /**
150 * Sets the Y coordinate of this homogeneous point.
151 *
152 * @param y Y coordinate.
153 */
154 public void setY(final double y) {
155 this.y = y;
156 normalized = false;
157 }
158
159 /**
160 * Returns the Z coordinate of the given homogeneous 3D point instance.
161 *
162 * @return Z coordinate.
163 */
164 public double getZ() {
165 return z;
166 }
167
168 /**
169 * Sets the Z coordinate of this homogeneous point.
170 *
171 * @param z Z coordinate.
172 */
173 public void setZ(final double z) {
174 this.z = z;
175 normalized = false;
176 }
177
178 /**
179 * Returns the W coordinate of the given homogeneous 3D point instance.
180 *
181 * @return W coordinate.
182 */
183 public double getW() {
184 return w;
185 }
186
187 /**
188 * Sets the W coordinate of this homogeneous point.
189 *
190 * @param w W coordinate.
191 */
192 public void setW(final double w) {
193 this.w = w;
194 normalized = false;
195 }
196
197 /**
198 * Sets the coordinates of this homogeneous 3D point by using provided X,
199 * Y, Z and W coordinates.
200 *
201 * @param x X coordinate.
202 * @param y Y coordinate.
203 * @param z Z coordinate.
204 * @param w W coordinate.
205 */
206 public void setCoordinates(final double x, final double y, final double z, final double w) {
207 this.x = x;
208 this.y = y;
209 this.z = z;
210 this.w = w;
211 normalized = false;
212 }
213
214 /**
215 * Sets the coordinates of a 3D point using an array containing its
216 * coordinates.
217 *
218 * @param v Array containing the coordinates of the point.
219 * @throws IllegalArgumentException Raised if provided array does not have
220 * a valid size.
221 */
222 @Override
223 public final void setCoordinates(final double[] v) {
224 if (v.length != POINT3D_HOMOGENEOUS_COORDINATES_LENGTH) {
225 throw new IllegalArgumentException();
226 } else {
227 x = v[0];
228 y = v[1];
229 z = v[2];
230 w = v[3];
231 normalized = false;
232 }
233 }
234
235 /**
236 * Sets coordinates of this instance using the coordinates of provided 3D
237 * point.
238 *
239 * @param point Input point.
240 */
241 @Override
242 public final void setCoordinates(final Point3D point) {
243 switch (point.getType()) {
244 case INHOMOGENEOUS_COORDINATES:
245 final var inhomPoint = (InhomogeneousPoint3D) point;
246 x = inhomPoint.getX();
247 y = inhomPoint.getY();
248 z = inhomPoint.getZ();
249 w = 1.0;
250 normalized = false;
251 break;
252
253 case HOMOGENEOUS_COORDINATES:
254 default:
255 final var homPoint = (HomogeneousPoint3D) point;
256 x = homPoint.getX();
257 y = homPoint.getY();
258 z = homPoint.getZ();
259 w = homPoint.getW();
260 normalized = false;
261 break;
262 }
263 }
264
265 /**
266 * Returns X homogeneous coordinate of this 3D point.
267 *
268 * @return X homogeneous coordinate.
269 */
270 @Override
271 public double getHomX() {
272 return getX();
273 }
274
275 /**
276 * Returns Y homogeneous coordinate of this 3D point.
277 *
278 * @return Y homogeneous coordinate.
279 */
280 @Override
281 public double getHomY() {
282 return getY();
283 }
284
285 /**
286 * Returns Z homogeneous coordinate of this 3D point.
287 *
288 * @return Z homogeneous coordinate.
289 */
290 @Override
291 public double getHomZ() {
292 return getZ();
293 }
294
295 /**
296 * Returns W homogeneous coordinate of this 3D point.
297 *
298 * @return W homogeneous coordinate.
299 */
300 @Override
301 public double getHomW() {
302 return getW();
303 }
304
305 /**
306 * Sets coordinates of this 3D point instance using provided homogeneous
307 * coordinates.
308 *
309 * @param homX x homogeneous coordinate.
310 * @param homY y homogeneous coordinate.
311 * @param homZ z homogeneous coordinate.
312 * @param homW w homogeneous coordinate.
313 */
314 @Override
315 public void setHomogeneousCoordinates(final double homX, final double homY, final double homZ, final double homW) {
316 setCoordinates(homX, homY, homZ, homW);
317 }
318
319 /**
320 * Returns X inhomogeneous coordinate of this 3D point.
321 *
322 * @return X inhomogeneous coordinate.
323 */
324 @Override
325 public double getInhomX() {
326 return (x / w);
327 }
328
329 /**
330 * Sets X inhomogeneous coordinate of this 3D point.
331 *
332 * @param inhomX inhomogeneous coordinate.
333 */
334 @Override
335 public void setInhomX(final double inhomX) {
336 x = inhomX * w;
337 normalized = false;
338 }
339
340 /**
341 * Returns Y inhomogeneous coordinate of this 3D point.
342 *
343 * @return Y inhomogeneous coordinate.
344 */
345 @Override
346 public double getInhomY() {
347 return (y / w);
348 }
349
350 /**
351 * Sets Y inhomogeneous coordinate of this 3D point.
352 *
353 * @param inhomY Y inhomogeneous coordinate.
354 */
355 @Override
356 public void setInhomY(final double inhomY) {
357 y = inhomY * w;
358 normalized = false;
359 }
360
361 /**
362 * Returns Z inhomogeneous coordinate of this 3D point.
363 *
364 * @return Z inhomogeneous coordinate.
365 */
366 @Override
367 public double getInhomZ() {
368 return (z / w);
369 }
370
371 /**
372 * Sets Z inhomogeneous coordinate of this 3D point.
373 *
374 * @param inhomZ Z inhomogeneous coordinate.
375 */
376 public void setInhomZ(final double inhomZ) {
377 z = inhomZ * w;
378 normalized = false;
379 }
380
381 /**
382 * Sets coordinates of this 3D point instance using provided inhomogeneous
383 * coordinates.
384 *
385 * @param inhomX x inhomogeneous coordinate.
386 * @param inhomY y inhomogeneous coordinate.
387 * @param inhomZ z inhomogeneous coordinate.
388 */
389 @Override
390 public void setInhomogeneousCoordinates(final double inhomX, final double inhomY, final double inhomZ) {
391 x = inhomX;
392 y = inhomY;
393 z = inhomZ;
394 w = 1.0;
395 normalized = false;
396 }
397
398 /**
399 * Checks if provided object equals current 2D point.
400 *
401 * @param obj Object to compare.
402 * @return True if both objects are considered to be equal, false otherwise.
403 */
404 @Override
405 public boolean equals(final Object obj) {
406 if (!(obj instanceof Point3D point)) {
407 return false;
408 }
409 if (obj == this) {
410 return true;
411 }
412
413 return equals(point);
414 }
415
416 /**
417 * Returns hash code value.
418 *
419 * @return Hash code value.
420 */
421 @Override
422 public int hashCode() {
423 return Objects.hash(x, y, z, w);
424 }
425
426 /**
427 * Checks if the homogeneous 3D point described by this instance equals the
428 * input Point3D (using a comparison threshold).
429 *
430 * @param point Point that will be compared to.
431 * @param threshold threshold grade of tolerance to determine whether the
432 * points are equal or not. It is used because due to machine precision, the
433 * values might not be exactly equal (if not provided
434 * DEFAULT_COMPARISON_THRESHOLD is used).
435 * @return True if current point and input point are the same, false
436 * otherwise.
437 * @throws IllegalArgumentException Raised if threshold is negative.
438 */
439 @Override
440 public boolean equals(final Point3D point, final double threshold) {
441 if (point.getType() == CoordinatesType.INHOMOGENEOUS_COORDINATES) {
442 return equals((InhomogeneousPoint3D) point, threshold);
443 } else {
444 return equals((HomogeneousPoint3D) point, threshold);
445 }
446 }
447
448 /**
449 * Checks if the homogeneous 3D point described by this instance equals the
450 * input HomogeneousPoint2d (using a comparison threshold).
451 *
452 * @param point Point that will be compared to.
453 * @param threshold threshold grade of tolerance to determine whether the
454 * points are equal or not. It is used because due to machine precision, the
455 * values might not be exactly equal (if not provided
456 * DEFAULT_COMPARISON_THRESHOLD is used).
457 * @return True if current point and input point are the same, false
458 * otherwise.
459 * @throws IllegalArgumentException Raised if threshold is negative.
460 */
461 public boolean equals(final HomogeneousPoint3D point, final double threshold) {
462 if (threshold < MIN_THRESHOLD) {
463 throw new IllegalArgumentException();
464 }
465
466 normalize();
467 point.normalize();
468
469 // compute sign for the case when points have different sign
470 final var signThis = (w > 0.0) ? 1.0 : -1.0;
471 final var signPoint = (point.w > 0.0) ? 1.0 : -1.0;
472
473 final var normThis = Math.sqrt(x * x + y * y + z * z + w * w) * signThis;
474 final var normPoint = Math.sqrt(point.x * point.x + point.y * point.y + point.z * point.z
475 + point.w * point.w) * signPoint;
476
477 final var validX = Math.abs(x / normThis - point.x / normPoint) <= threshold;
478 final var validY = Math.abs(y / normThis - point.y / normPoint) <= threshold;
479 final var validZ = Math.abs(z / normThis - point.z / normPoint) <= threshold;
480 final var validW = Math.abs(w / normThis - point.w / normPoint) <= threshold;
481
482 return (validX && validY && validZ && validW);
483 }
484
485 /**
486 * Checks if the homogeneous 3D point described by this instance equals the
487 * input HomogeneousPoint3D (using a comparison threshold).
488 *
489 * @param point Point that will be compared to.
490 * @return True if current point and input point are the same, false
491 * otherwise.
492 */
493 public boolean equals(final HomogeneousPoint3D point) {
494 return equals(point, DEFAULT_COMPARISON_THRESHOLD);
495 }
496
497 /**
498 * Checks if the homogeneous 3D point described by this instance equals the
499 * input InhomogeneousPoint3D (using a comparison threshold).
500 *
501 * @param point Point that will be compared to.
502 * @param threshold threshold grade of tolerance to determine whether the
503 * points are equal or not. It is used because due to machine precision, the
504 * values might not be exactly equal (if not provided
505 * DEFAULT_COMPARISON_THRESHOLD is used).
506 * @return True if current point and input point are the same, false
507 * otherwise.
508 * @throws IllegalArgumentException Raised if threshold is negative.
509 */
510 public boolean equals(final InhomogeneousPoint3D point, final double threshold) {
511 if (threshold < MIN_THRESHOLD) {
512 throw new IllegalArgumentException();
513 }
514
515 final var dX = Math.abs(point.getX() - (x / w)) <= threshold;
516 final var dY = Math.abs(point.getY() - (y / w)) <= threshold;
517 final var dZ = Math.abs(point.getZ() - (z / w)) <= threshold;
518 return (dX && dY && dZ);
519 }
520
521 /**
522 * Checks if the homogeneous 3D point described by this instance equals the
523 * input InhomogeneousPoint3D (using a comparison threshold).
524 *
525 * @param point Point that will be compared to.
526 * @return True if current point and input point are the same, false
527 * otherwise.
528 */
529 public boolean equals(final InhomogeneousPoint3D point) {
530 return equals(point, DEFAULT_COMPARISON_THRESHOLD);
531 }
532
533 /**
534 * Checks whether this Point3D is at infinity or not.
535 *
536 * @return True if the point is at infinity. False otherwise.
537 */
538 @Override
539 public boolean isAtInfinity() {
540 return isAtInfinity(DEFAULT_INFINITY_THRESHOLD);
541 }
542
543 /**
544 * Checks whether this homogeneous 3D point is at infinity or not. An
545 * homogeneous 3D point is at infinity when W coordinates are equal or close
546 * to zero.
547 *
548 * @param threshold Grade of tolerance to determine whether the point is at
549 * infinity or not. It is used because due to machine precision, the values
550 * might not be exactly equal.
551 * @return True if point is at infinity, false otherwise.
552 * @throws IllegalArgumentException Raised if threshold is negative.
553 */
554 public boolean isAtInfinity(final double threshold) {
555 if (threshold < MIN_THRESHOLD) {
556 throw new IllegalArgumentException();
557 }
558
559 return (Math.abs(w) <= threshold);
560 }
561
562 /**
563 * Returns the type of coordinates used to represent a Point3D.
564 *
565 * @return Type of coordinates of this 3D point.
566 */
567 @Override
568 public CoordinatesType getType() {
569 return CoordinatesType.HOMOGENEOUS_COORDINATES;
570 }
571
572 /**
573 * Method to normalize a 3D point by dividing all homogeneous components by
574 * its norm. This only applies to homogeneous 3D points, because they are
575 * defined up to scale.
576 */
577 @Override
578 public void normalize() {
579 if (!normalized) {
580 var norm = Math.sqrt(x * x + y * y + z * z + w * w);
581 if (norm > PRECISION) {
582 x /= norm;
583 y /= norm;
584 z /= norm;
585 w /= norm;
586 normalized = true;
587 }
588 }
589 }
590
591 /**
592 * Returns boolean indicating whether this point has already been normalized.
593 *
594 * @return True if normalized, false otherwise.
595 */
596 @Override
597 public boolean isNormalized() {
598 return normalized;
599 }
600
601 /**
602 * Converts this instance into an inhomogeneous 3D point and returns the
603 * result as a new inhomogeneous 3D point instance.
604 *
605 * @return Converts and returns this point as an inhomogeneous 3D point.
606 */
607 public InhomogeneousPoint3D toInhomogeneous() {
608 return new InhomogeneousPoint3D(x / w, y / w, z / w);
609 }
610
611 /**
612 * Returns an array containing the coordinates of this Point2D.
613 *
614 * @return Array containing coordinates of this Point2D.
615 */
616 @Override
617 public double[] asArray() {
618 final var out = new double[POINT3D_HOMOGENEOUS_COORDINATES_LENGTH];
619 asArray(out);
620 return out;
621 }
622
623 /**
624 * Uses provided array to store the coordinates of this HomogeneousPoint2D.
625 *
626 * @param array Array where coordinates will be stored.
627 * @throws IllegalArgumentException Raised if length of array is not 3.
628 */
629 @Override
630 public void asArray(final double[] array) {
631 if (array.length != POINT3D_HOMOGENEOUS_COORDINATES_LENGTH) {
632 throw new IllegalArgumentException();
633 }
634 array[0] = x;
635 array[1] = y;
636 array[2] = z;
637 array[3] = w;
638 }
639 }