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 }