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 }