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