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 Point2D defining an homogeneous 2D point.
24   * An homogeneous 2d point is defined by three coordinates: (x,y,w), where
25   * x and y are the horizontal and vertical coordinates, respectively, and w
26   * is a normalization (homogenization) factor. Homogeneous 2d points at
27   * infinity are expressed using w=0 (x,y,0) where (x,y) describe the direction
28   * of the 2d point towards infinity.
29   * Inhomogeneous 2d points can be transformed into homogeneous 2d points by
30   * setting the w coordinate to one (w=1, not at infinity) as follows:
31   * Inhomogeneous 2d point (x,y) -< Homogeneous 2d point (x,y,1).
32   */
33  public class HomogeneousPoint2D extends Point2D 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 W coordinate of an homogeneous 2D point.
57       */
58      private double w;
59  
60      /**
61       * Determines whether this point is already normalized.
62       */
63      private boolean normalized;
64  
65      /**
66       * Empty constructor.
67       */
68      public HomogeneousPoint2D() {
69          super();
70          x = y = 0.0;
71          w = 1.0;
72          normalized = false;
73      }
74  
75      /**
76       * Constructor of this class. This constructor sets a new homogeneous
77       * v array containing the coordinates X, Y and W of the given point.
78       *
79       * @param v Array of length 3 containing the 2D coordinates of an
80       *          homogeneous point.
81       * @throws IllegalArgumentException Raised when the size of the array is
82       *                                  different of 3.
83       */
84      public HomogeneousPoint2D(final double[] v) {
85          super();
86          setCoordinates(v);
87      }
88  
89      /**
90       * Constructor of this class. This constructor sets a new homogeneous 2D
91       * point using the coordinates X, Y and W of the given point.
92       *
93       * @param x X coordinate of the given 2D point.
94       * @param y Y coordinate of the given 2D point.
95       * @param w W coordinate of the given 2D point.
96       */
97      public HomogeneousPoint2D(final double x, final double y, final double w) {
98          this.x = x;
99          this.y = y;
100         this.w = w;
101         normalized = false;
102     }
103 
104     /**
105      * This constructor sets a new homogeneous 2D point using as initialization
106      * provided Point2D instance.
107      *
108      * @param point Point to initialize new instance to.
109      */
110     public HomogeneousPoint2D(final Point2D point) {
111         setCoordinates(point);
112     }
113 
114     /**
115      * Returns the X coordinate of the given homogeneous 2D point instance.
116      *
117      * @return X coordinate.
118      */
119     public double getX() {
120         return x;
121     }
122 
123     /**
124      * Sets the X coordinate of this homogeneous point.
125      *
126      * @param x X coordinate.
127      */
128     public void setX(final double x) {
129         this.x = x;
130         normalized = false;
131     }
132 
133     /**
134      * Returns the Y coordinate of the given homogeneous 2D point instance.
135      *
136      * @return Y coordinate.
137      */
138     public double getY() {
139         return y;
140     }
141 
142     /**
143      * Sets the Y coordinate of this homogeneous point.
144      *
145      * @param y Y coordinate.
146      */
147     public void setY(final double y) {
148         this.y = y;
149         normalized = false;
150     }
151 
152     /**
153      * Returns the W coordinate of the given homogeneous 2D point instance.
154      *
155      * @return W coordinate.
156      */
157     public double getW() {
158         return w;
159     }
160 
161     /**
162      * Sets the W coordinate of this homogeneous point.
163      *
164      * @param w W coordinate.
165      */
166     public void setW(final double w) {
167         this.w = w;
168         normalized = false;
169     }
170 
171     /**
172      * Sets the coordinates of this homogeneous 2D point by using provided X,
173      * Y and W coordinates.
174      *
175      * @param x X coordinate.
176      * @param y Y coordinate.
177      * @param w W coordinate.
178      */
179     public void setCoordinates(final double x, final double y, final double w) {
180         this.x = x;
181         this.y = y;
182         this.w = w;
183         normalized = false;
184     }
185 
186     /**
187      * Sets the coordinates of a 2d point using an array containing its
188      * coordinates.
189      *
190      * @param v Array containing the coordinates of the point.
191      * @throws IllegalArgumentException Raised if provided array does not have
192      *                                  a valid size.
193      */
194     @Override
195     public final void setCoordinates(final double[] v) {
196         if (v.length != POINT2D_HOMOGENEOUS_COORDINATES_LENGTH) {
197             throw new IllegalArgumentException();
198         } else {
199             x = v[0];
200             y = v[1];
201             w = v[2];
202             normalized = false;
203         }
204     }
205 
206     /**
207      * Sets coordinates of this instance using the coordinates of provided 2D
208      * point.
209      *
210      * @param point Input point.
211      */
212     @Override
213     public final void setCoordinates(final Point2D point) {
214         switch (point.getType()) {
215             case INHOMOGENEOUS_COORDINATES:
216                 final var inhomPoint = (InhomogeneousPoint2D) point;
217                 x = inhomPoint.getX();
218                 y = inhomPoint.getY();
219                 w = 1.0;
220                 normalized = false;
221                 break;
222 
223             case HOMOGENEOUS_COORDINATES:
224             default:
225                 final var homPoint = (HomogeneousPoint2D) point;
226                 x = homPoint.getX();
227                 y = homPoint.getY();
228                 w = homPoint.getW();
229                 normalized = false;
230                 break;
231         }
232     }
233 
234     /**
235      * Returns X homogeneous coordinate of this 2d point.
236      *
237      * @return X homogeneous coordinate.
238      */
239     @Override
240     public double getHomX() {
241         return getX();
242     }
243 
244     /**
245      * Returns Y homogeneous coordinate of this 2d point.
246      *
247      * @return Y homogeneous coordinate.
248      */
249     @Override
250     public double getHomY() {
251         return getY();
252     }
253 
254     /**
255      * Returns W homogeneous coordinate of this 2d point.
256      *
257      * @return W homogeneous coordinate.
258      */
259     @Override
260     public double getHomW() {
261         return getW();
262     }
263 
264     /**
265      * Sets coordinates of this 2d point instance using provided homogeneous
266      * coordinates.
267      *
268      * @param homX x homogeneous coordinate.
269      * @param homY y homogeneous coordinate.
270      * @param homW w homogeneous coordinate.
271      */
272     @Override
273     public void setHomogeneousCoordinates(final double homX, final double homY, final double homW) {
274         setCoordinates(homX, homY, homW);
275     }
276 
277     /**
278      * Returns X inhomogeneous coordinate of this 2d point.
279      *
280      * @return X inhomogeneous coordinate.
281      */
282     @Override
283     public double getInhomX() {
284         return (x / w);
285     }
286 
287     /**
288      * Sets X inhomogeneous coordinate of this 2d point.
289      *
290      * @param inhomX inhomogeneous coordinate.
291      */
292     @Override
293     public void setInhomX(final double inhomX) {
294         x = inhomX * w;
295         normalized = false;
296     }
297 
298     /**
299      * Returns Y inhomogeneous coordinate of this 2d point.
300      *
301      * @return Y inhomogeneous coordinate.
302      */
303     @Override
304     public double getInhomY() {
305         return (y / w);
306     }
307 
308     /**
309      * Sets Y inhomogeneous coordinate of this 2d point.
310      *
311      * @param inhomY Y inhomogeneous coordinate.
312      */
313     @Override
314     public void setInhomY(final double inhomY) {
315         y = inhomY * w;
316         normalized = false;
317     }
318 
319     /**
320      * Sets coordinates of this 2d point instance using provided inhomogeneous
321      * coordinates.
322      *
323      * @param inhomX x inhomogeneous coordinate.
324      * @param inhomY y inhomogeneous coordinate.
325      */
326     @Override
327     public void setInhomogeneousCoordinates(final double inhomX, final double inhomY) {
328         x = inhomX;
329         y = inhomY;
330         w = 1.0;
331         normalized = false;
332     }
333 
334     /**
335      * Checks if provided object equals current 2D point.
336      *
337      * @param obj Object to compare.
338      * @return True if both objects are considered to be equal, false otherwise.
339      */
340     @Override
341     public boolean equals(final Object obj) {
342         if (!(obj instanceof Point2D point)) {
343             return false;
344         }
345         if (obj == this) {
346             return true;
347         }
348 
349         return equals(point);
350     }
351 
352     /**
353      * Returns hash code value.
354      *
355      * @return Hash code value.
356      */
357     @Override
358     public int hashCode() {
359         return Objects.hash(x, y, w);
360     }
361 
362     /**
363      * Checks if the homogeneous 2d point described by this instance equals the
364      * input {@link Point2D} (using a comparison threshold).
365      *
366      * @param point     Point that will be compared to.
367      * @param threshold threshold grade of tolerance to determine whether the
368      *                  points are equal or not. It is used because due to machine precision, the
369      *                  values might not be exactly equal (if not provided
370      *                  DEFAULT_COMPARISON_THRESHOLD is used).
371      * @return True if current point and input point are the same, false
372      * otherwise.
373      * @throws IllegalArgumentException Raised if threshold is negative.
374      */
375     @Override
376     public boolean equals(final Point2D point, final double threshold) {
377         if (point.getType() == CoordinatesType.INHOMOGENEOUS_COORDINATES) {
378             return equals((InhomogeneousPoint2D) point, threshold);
379         } else {
380             return equals((HomogeneousPoint2D) point, threshold);
381         }
382     }
383 
384     /**
385      * Checks if the homogeneous 2d point described by this instance equals the
386      * input HomogeneousPoint2d (using a comparison threshold).
387      *
388      * @param point     Point that will be compared to.
389      * @param threshold threshold grade of tolerance to determine whether the
390      *                  points are equal or not. It is used because due to machine precision, the
391      *                  values might not be exactly equal (if not provided
392      *                  DEFAULT_COMPARISON_THRESHOLD is used).
393      * @return True if current point and input point are the same, false
394      * otherwise.
395      * @throws IllegalArgumentException Raised if threshold is negative.
396      */
397     public boolean equals(final HomogeneousPoint2D point, final double threshold) {
398         if (threshold < MIN_THRESHOLD) {
399             throw new IllegalArgumentException();
400         }
401 
402         normalize();
403         point.normalize();
404 
405         // compute sign for the case when points have different sign
406         final var signThis = (w > 0.0) ? 1.0 : -1.0;
407         final var signPoint = (point.w > 0.0) ? 1.0 : -1.0;
408 
409         final var normThis = Math.sqrt(x * x + y * y + w * w) * signThis;
410         final var normPoint = Math.sqrt(point.x * point.x + point.y * point.y + point.w * point.w) * signPoint;
411 
412         final var validX = Math.abs(x / normThis - point.x / normPoint) <= threshold;
413         final var validY = Math.abs(y / normThis - point.y / normPoint) <= threshold;
414         final var validW = Math.abs(w / normThis - point.w / normPoint) <= threshold;
415 
416         return (validX && validY && validW);
417     }
418 
419     /**
420      * Checks if the homogeneous 2d point described by this instance equals the
421      * input HomogeneousPoint2d (using a comparison threshold).
422      *
423      * @param point Point that will be compared to.
424      * @return True if current point and input point are the same, false
425      * otherwise.
426      */
427     public boolean equals(final HomogeneousPoint2D point) {
428         return equals(point, DEFAULT_COMPARISON_THRESHOLD);
429     }
430 
431     /**
432      * Checks if the homogeneous 2d point described by this instance equals the
433      * input InhomogeneousPoint2d (using a comparison threshold).
434      *
435      * @param point     Point that will be compared to.
436      * @param threshold threshold grade of tolerance to determine whether the
437      *                  points are equal or not. It is used because due to machine precision, the
438      *                  values might not be exactly equal (if not provided
439      *                  DEFAULT_COMPARISON_THRESHOLD is used).
440      * @return True if current point and input point are the same, false
441      * otherwise.
442      * @throws IllegalArgumentException Raised if threshold is negative.
443      */
444     public boolean equals(final InhomogeneousPoint2D point, final double threshold) {
445         if (threshold < MIN_THRESHOLD) {
446             throw new IllegalArgumentException();
447         }
448 
449         final var dX = Math.abs(point.getX() - (x / w)) <= threshold;
450         final var dY = Math.abs(point.getY() - (y / w)) <= threshold;
451         return (dX && dY);
452     }
453 
454     /**
455      * Checks if the homogeneous 2d point described by this instance equals the
456      * input InhomogeneousPoint2d (using a comparison threshold).
457      *
458      * @param point Point that will be compared to.
459      * @return True if current point and input point are the same, false
460      * otherwise.
461      */
462     public boolean equals(final InhomogeneousPoint2D point) {
463         return equals(point, DEFAULT_COMPARISON_THRESHOLD);
464     }
465 
466     /**
467      * Checks whether this Point2D is at infinity or not.
468      *
469      * @return True if the point is at infinity. False otherwise.
470      */
471     @Override
472     public boolean isAtInfinity() {
473         return isAtInfinity(DEFAULT_INFINITY_THRESHOLD);
474     }
475 
476     /**
477      * Checks whether this homogeneous 2D point is at infinity or not. An
478      * homogeneous 2D point is at infinity when W coordinates are equal or close
479      * to zero.
480      *
481      * @param threshold Grade of tolerance to determine whether the point is at
482      *                  infinity or not. It is used because due to machine precision, the values
483      *                  might not be exactly equal.
484      * @return True if point is at infinity, false otherwise.
485      * @throws IllegalArgumentException Raised if threshold is negative.
486      */
487     public boolean isAtInfinity(final double threshold) {
488         if (threshold < MIN_THRESHOLD) {
489             throw new IllegalArgumentException();
490         }
491 
492         return (Math.abs(w) <= threshold);
493     }
494 
495     /**
496      * Returns the type of coordinates used to represent a Point2D.
497      *
498      * @return Type of coordinates of this 2d point.
499      */
500     @Override
501     public CoordinatesType getType() {
502         return CoordinatesType.HOMOGENEOUS_COORDINATES;
503     }
504 
505     /**
506      * Method to normalize a 2d point by dividing all homogeneous components by
507      * its norm. This only applies to homogeneous 2d points, because they are
508      * defined up to scale.
509      */
510     @SuppressWarnings("DuplicatedCode")
511     @Override
512     public void normalize() {
513         if (!normalized) {
514             final var norm = Math.sqrt(x * x + y * y + w * w);
515             if (norm > PRECISION) {
516                 x /= norm;
517                 y /= norm;
518                 w /= norm;
519                 normalized = true;
520             }
521         }
522     }
523 
524     /**
525      * Returns boolean indicating whether this point has already been mNormalized
526      *
527      * @return True if mNormalized, false otherwise.
528      */
529     @Override
530     public boolean isNormalized() {
531         return normalized;
532     }
533 
534     /**
535      * Converts this instance into an inhomogeneous 2D point and returns the
536      * result as a new inhomogeneous 2D point instance.
537      *
538      * @return Converts and returns this point as an inhomogeneous 2D point.
539      */
540     public InhomogeneousPoint2D toInhomogeneous() {
541         return new InhomogeneousPoint2D(x / w, y / w);
542     }
543 
544     /**
545      * Returns an array containing the coordinates of this Point2D.
546      *
547      * @return Array containing coordinates of this Point2D.
548      */
549     @Override
550     public double[] asArray() {
551         final var out = new double[POINT2D_HOMOGENEOUS_COORDINATES_LENGTH];
552         asArray(out);
553         return out;
554     }
555 
556     /**
557      * Uses provided array to store the coordinates of this HomogeneousPoint2D
558      *
559      * @param array Array where coordinates will be stored.
560      * @throws IllegalArgumentException Raised if length of array is not 3.
561      */
562     @Override
563     public void asArray(double[] array) {
564         if (array.length != POINT2D_HOMOGENEOUS_COORDINATES_LENGTH) {
565             throw new IllegalArgumentException();
566         }
567         array[0] = x;
568         array[1] = y;
569         array[2] = w;
570     }
571 }