View Javadoc
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 }