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  
21  /**
22   * Abstract class defining the base interface that all 2D points should have.
23   * 2D points describe points in a 2D space such as images. They can be
24   * implemented either as homogeneous or inhomogeneous points.
25   */
26  public abstract class Point2D implements Serializable, Point<Point2D> {
27  
28      /**
29       * Defines the threshold used when comparing two values.
30       */
31      public static final double DEFAULT_COMPARISON_THRESHOLD = 1e-10;
32  
33      /**
34       * Constant defining minimum threshold.
35       */
36      public static final double MIN_THRESHOLD = 0.0;
37  
38      /**
39       * Length of homogeneous coordinates array.
40       */
41      public static final int POINT2D_HOMOGENEOUS_COORDINATES_LENGTH = 3;
42  
43      /**
44       * Length of inhomogeneous coordinates array.
45       */
46      public static final int POINT2D_INHOMOGENEOUS_COORDINATES_LENGTH = 2;
47  
48      /**
49       * Default type of coordinates.
50       */
51      public static final CoordinatesType DEFAULT_COORDINATES_TYPE = CoordinatesType.HOMOGENEOUS_COORDINATES;
52  
53      /**
54       * Constructor of this class.
55       */
56      protected Point2D() {
57      }
58  
59      /**
60       * Creates and returns an instance of any existing subclass of Point2D
61       * specified in coordinatesType. The right size of the provided array is
62       * also checked depending on the type of coordinates used.
63       *
64       * @param coordinatesType Type of coordinates used.
65       * @param v               Array containing the coordinates of the 2d homogeneous or
66       *                        inhomogeneous point.
67       * @return Created Point2D.
68       * @throws IllegalArgumentException Raised if the size of provided array is
69       *                                  not valid.
70       */
71      public static Point2D create(final CoordinatesType coordinatesType, final double[] v) {
72          if (coordinatesType == CoordinatesType.INHOMOGENEOUS_COORDINATES) {
73              return new InhomogeneousPoint2D(v);
74          } else {
75              return new HomogeneousPoint2D(v);
76          }
77      }
78  
79      /**
80       * Creates and returns an instance of any existing subclass of Point2D
81       * depending on provided vector length. Size of provided vector is also
82       * checked to ensure it has appropriate size to represent 2d points either
83       * using inhomogeneous or homogeneous coordinates.
84       *
85       * @param v Array containing the coordinates of the 2D homogeneous or
86       *          inhomogeneous 2d point.
87       * @return Created Point2D.
88       * @throws IllegalArgumentException Raised if the size of provided array
89       *                                  is not valid.
90       */
91      public static Point2D create(final double[] v) {
92          return create(DEFAULT_COORDINATES_TYPE, v);
93      }
94  
95      /**
96       * Creates and returns an instance of any existing subclass of Point2D
97       * specified in coordinatesType.
98       *
99       * @param coordinatesType Type of coordinates used.
100      * @return Created Point2D.
101      */
102     public static Point2D create(final CoordinatesType coordinatesType) {
103         if (coordinatesType == CoordinatesType.INHOMOGENEOUS_COORDINATES) {
104             return new InhomogeneousPoint2D();
105         } else {
106             return new HomogeneousPoint2D();
107         }
108     }
109 
110     /**
111      * Creates and returns an instance of an existing subclass of Point2D
112      * using DEFAULT_COORDINATES_TYPE.
113      *
114      * @return Create Point2D.
115      */
116     public static Point2D create() {
117         return create(DEFAULT_COORDINATES_TYPE);
118     }
119 
120     /**
121      * Returns an array containing the coordinates of this Point2D.
122      *
123      * @return Array containing coordinates of this Point2D.
124      */
125     public abstract double[] asArray();
126 
127     /**
128      * Uses provided array to store the coordinates of this Point2D.
129      *
130      * @param array Array where coordinates will be stored.
131      * @throws IllegalArgumentException Raised if length of array is not valid.
132      */
133     public abstract void asArray(final double[] array);
134 
135     /**
136      * Sets the coordinates of a 2d point using an array containing its
137      * coordinates.
138      *
139      * @param v Array containing the coordinates of the point.
140      * @throws IllegalArgumentException Raised if provided array does not have
141      *                                  a valid size.
142      */
143     public abstract void setCoordinates(final double[] v);
144 
145     /**
146      * Sets coordinates of this instance using the coordinates of provided 2D
147      * point.
148      *
149      * @param point Input point.
150      */
151     public abstract void setCoordinates(final Point2D point);
152 
153     /**
154      * Returns X homogeneous coordinate of this 2d point.
155      *
156      * @return X homogeneous coordinate.
157      */
158     public abstract double getHomX();
159 
160     /**
161      * Returns Y homogeneous coordinate of this 2d point.
162      *
163      * @return Y homogeneous coordinate.
164      */
165     public abstract double getHomY();
166 
167     /**
168      * Returns W homogeneous coordinate of this 2d point.
169      *
170      * @return W homogeneous coordinate.
171      */
172     public abstract double getHomW();
173 
174     /**
175      * Sets coordinates of this 2d point instance using provided homogeneous
176      * coordinates.
177      *
178      * @param homX x homogeneous coordinate.
179      * @param homY y homogeneous coordinate.
180      * @param homW w homogeneous coordinate.
181      */
182     public abstract void setHomogeneousCoordinates(final double homX, final double homY, final double homW);
183 
184     /**
185      * Returns X inhomogeneous coordinate of this 2d point.
186      *
187      * @return X inhomogeneous coordinate.
188      */
189     public abstract double getInhomX();
190 
191     /**
192      * Sets X inhomogeneous coordinate of this 2d point.
193      *
194      * @param inhomX X inhomogeneous coordinate.
195      */
196     public abstract void setInhomX(final double inhomX);
197 
198     /**
199      * Returns Y inhomogeneous coordinate of this 2d point.
200      *
201      * @return Y inhomogeneous coordinate.
202      */
203     public abstract double getInhomY();
204 
205     /**
206      * Sets Y inhomogeneous coordinate of this 2d point.
207      *
208      * @param inhomY Y inhomogeneous coordinate.
209      */
210     public abstract void setInhomY(final double inhomY);
211 
212     /**
213      * Sets coordinates of this 2d point instance using provided inhomogeneous
214      * coordinates.
215      *
216      * @param inhomX x inhomogeneous coordinate.
217      * @param inhomY y inhomogeneous coordinate.
218      */
219     public abstract void setInhomogeneousCoordinates(final double inhomX, final double inhomY);
220 
221     /**
222      * Checks if the 2d point described by this class equals the input {@link Point2D}
223      * (using a comparison threshold).
224      *
225      * @param point     Point that will be compared to.
226      * @param threshold threshold used to check that the difference of the
227      *                  values is close to zero with an absolute error defined by threshold.
228      * @return True if current point and input point are the same, false
229      * otherwise.
230      * @throws IllegalArgumentException Raised if threshold is negative.
231      */
232     public abstract boolean equals(final Point2D point, final double threshold);
233 
234     /**
235      * Checks if the 2d point described by this class equals the input {@link Point2D}
236      * (using DEFAULT_COMPARISON_THRESHOLD).
237      *
238      * @param point Point that will be compared to.
239      * @return True if current point and input point are the same, false
240      * otherwise.
241      */
242     public boolean equals(final Point2D point) {
243         return equals(point, DEFAULT_COMPARISON_THRESHOLD);
244     }
245 
246     /**
247      * Checks if provided object equals current 2d point.
248      *
249      * @param obj Object to compare.
250      * @return True if both objects are considered to be equal, false otherwise.
251      */
252     @Override
253     public boolean equals(final Object obj) {
254         if (!(obj instanceof Point2D point)) {
255             return false;
256         }
257         if (obj == this) {
258             return true;
259         }
260 
261         return equals(point);
262     }
263 
264     /**
265      * Returns hash code value. This is only defined to keep the compiler happy.
266      * This method must be overridden in subclasses of this class.
267      *
268      * @return Hash code.
269      */
270     @Override
271     public abstract int hashCode();
272 
273     /**
274      * Checks whether this Point2D is at infinity or not.
275      *
276      * @return True if the point is at infinity. False otherwise.
277      */
278     public abstract boolean isAtInfinity();
279 
280     /**
281      * Returns the type of coordinates used to represent a Point2D.
282      *
283      * @return Type of coordinates of this 2d point.
284      */
285     public abstract CoordinatesType getType();
286 
287     /**
288      * Method to normalize a 2d point. This only applies to homogeneous
289      * 2d points, otherwise it has no effect.
290      * This method is meant to be overridden.
291      */
292     public void normalize() {
293     }
294 
295     /**
296      * Returns boolean indicating whether this point has already been
297      * normalized.
298      * This method is meant to be overridden. By default, it will always return
299      * true, to indicate that no further normalization is possible.
300      *
301      * @return True if normalized, false otherwise.
302      */
303     public boolean isNormalized() {
304         return true;
305     }
306 
307     /**
308      * Returns number of dimensions of this point implementation.
309      *
310      * @return number of dimensions.
311      */
312     @Override
313     public int getDimensions() {
314         return POINT2D_INHOMOGENEOUS_COORDINATES_LENGTH;
315     }
316 
317     /**
318      * Gets value of inhomogeneous coordinate for provided dimension.
319      *
320      * @param dim dimension to retrieve coordinate for (i.e. 0 means x, 1 means y, 2 means z, etc).
321      * @return value of inhomogeneous coordinate.
322      * @throws IllegalArgumentException if provided dimension value is negative or exceeds number of dimensions.
323      */
324     @Override
325     public double getInhomogeneousCoordinate(final int dim) {
326         if (dim < 0 || dim >= getDimensions()) {
327             throw new IllegalArgumentException();
328         }
329 
330         if (dim == 0) {
331             return getInhomX();
332         } else {
333             return getInhomY();
334         }
335     }
336 
337     /**
338      * Sets value of inhomogeneous coordinate for provided dimension.
339      *
340      * @param dim   dimension to set coordinate for (i.e. 0 means x, 1 means y, 2 means z, etc.).
341      * @param value value to be set.
342      * @throws IllegalArgumentException if provided dimension value is negative or exceeds number of dimensions.
343      */
344     @Override
345     public void setInhomogeneousCoordinate(final int dim, final double value) {
346         switch (dim) {
347             case 0:
348                 setInhomX(value);
349                 break;
350             case 1:
351                 setInhomY(value);
352                 break;
353             default:
354                 throw new IllegalArgumentException();
355         }
356     }
357 
358     /**
359      * Returns Euclidean distance between this point and provided point.
360      *
361      * @param point Point to compare.
362      * @return Euclidean distance between this point and provided point.
363      */
364     @Override
365     public double distanceTo(final Point2D point) {
366         return Math.sqrt(sqrDistanceTo(point));
367     }
368 
369     /**
370      * Returns squared Euclidean distance between this point and provided point.
371      *
372      * @param point point to compare.
373      * @return Euclidean distance between this point and provided point.
374      */
375     @Override
376     public double sqrDistanceTo(final Point2D point) {
377         final var diffX = getInhomX() - point.getInhomX();
378         final var diffY = getInhomY() - point.getInhomY();
379 
380         return diffX * diffX + diffY * diffY;
381     }
382 
383 
384     /**
385      * Computes the dot product between the homogeneous coordinates x, y,w of
386      * this point and the ones of provided point.
387      *
388      * @param point point to compute dot product with.
389      * @return dot product value.
390      */
391     public double dotProduct(final Point2D point) {
392         final var thisHomX = getHomX();
393         final var thisHomY = getHomY();
394         final var thisHomW = getHomW();
395         final var otherHomX = point.getHomX();
396         final var otherHomY = point.getHomY();
397         final var otherHomW = point.getHomW();
398 
399         final var thisNormSqr = thisHomX * thisHomX + thisHomY * thisHomY + thisHomW * thisHomW;
400         final var otherNormSqr = otherHomX * otherHomX + otherHomY * otherHomY + otherHomW * otherHomW;
401         final var denom = Math.sqrt(thisNormSqr * otherNormSqr);
402         final var num = thisHomX * otherHomX + thisHomY * otherHomY + thisHomW * otherHomW;
403 
404         return num / denom;
405     }
406 
407     /**
408      * Returns true if this point is between points point1 and point2, in other
409      * words, is inside the segment formed by those 2 points.
410      *
411      * @param point1 Point 1.
412      * @param point2 Point 2.
413      * @return True if point is between point1 and point2, false otherwise.
414      */
415     public boolean isBetween(final Point2D point1, final Point2D point2) {
416         return isBetween(point1, point2, DEFAULT_COMPARISON_THRESHOLD);
417     }
418 
419     /**
420      * Returns true if this point is between points point1 and point2, in other
421      * words, is inside the segment formed by those 2 points.
422      *
423      * @param point1    Point 1.
424      * @param point2    Point 2.
425      * @param threshold Threshold to determine if point is between.
426      * @return True if point is between point1 and point2, false otherwise.
427      */
428     public boolean isBetween(final Point2D point1, final Point2D point2, final double threshold) {
429         if (threshold < MIN_THRESHOLD) {
430             throw new IllegalArgumentException();
431         }
432         // If this point is between point1 and point2 then,
433         // dist(point1,this) + dist(point2, this) == dist(point1,point2) except
434         // for some small difference due to machine precision
435         return Math.abs(distanceTo(point1) + distanceTo(point2) - point1.distanceTo(point2)) <= threshold;
436     }
437 }