1 /*
2 * Copyright (C) 2012 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 package com.irurueta.geometry;
17
18 import java.io.Serializable;
19 import java.util.ArrayList;
20 import java.util.List;
21
22 /**
23 * This class defines a triangle in the 2D space.
24 */
25 @SuppressWarnings("DuplicatedCode")
26 public class Triangle2D implements Serializable {
27
28 /**
29 * Default threshold value. Thresholds are used to determine whether a point
30 * lies inside the triangle or not, or if it's locus or not, etc.
31 */
32 public static final double DEFAULT_THRESHOLD = 1e-9;
33
34 /**
35 * Minimum allowed threshold value.
36 */
37 public static final double MIN_THRESHOLD = 0.0;
38
39 /**
40 * Constant defining number of vertices on a triangle.
41 */
42 public static final int NUM_VERTICES = 3;
43
44 /**
45 * 1st vertex of this triangle.
46 */
47 private Point2D vertex1;
48
49 /**
50 * 2nd vertex of this triangle.
51 */
52 private Point2D vertex2;
53
54 /**
55 * 3rd vertex of this triangle.
56 */
57 private Point2D vertex3;
58
59 /**
60 * Constructor.
61 *
62 * @param vertex1 1st vertex.
63 * @param vertex2 2nd vertex.
64 * @param vertex3 3rd vertex.
65 * @throws NullPointerException Raised if any of the vertices is null.
66 */
67 public Triangle2D(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
68 setVertices(vertex1, vertex2, vertex3);
69 }
70
71 /**
72 * Returns 1st vertex of this triangle.
73 *
74 * @return 1st vertex.
75 */
76 public Point2D getVertex1() {
77 return vertex1;
78 }
79
80 /**
81 * Sets 1st vertex of this triangle.
82 *
83 * @param vertex1 1st vertex.
84 * @throws NullPointerException Raised if provided vertex is null.
85 */
86 public void setVertex1(final Point2D vertex1) {
87 if (vertex1 == null) {
88 throw new NullPointerException();
89 }
90 this.vertex1 = vertex1;
91 }
92
93 /**
94 * Returns 2nd vertex of this triangle.
95 *
96 * @return 2nd vertex.
97 */
98 public Point2D getVertex2() {
99 return vertex2;
100 }
101
102 /**
103 * Sets 2nd vertex of this triangle.
104 *
105 * @param vertex2 2nd vertex.
106 * @throws NullPointerException Raised if provided vertex is null.
107 */
108 public void setVertex2(final Point2D vertex2) {
109 if (vertex2 == null) {
110 throw new NullPointerException();
111 }
112 this.vertex2 = vertex2;
113 }
114
115 /**
116 * Returns 3rd vertex of this triangle.
117 *
118 * @return 3rd vertex.
119 */
120 public Point2D getVertex3() {
121 return vertex3;
122 }
123
124 /**
125 * Sets 3rd vertex of this triangle.
126 *
127 * @param vertex3 3rd vertex.
128 * @throws NullPointerException Raised if provided vertex is null.
129 */
130 public void setVertex3(final Point2D vertex3) {
131 if (vertex3 == null) {
132 throw new NullPointerException();
133 }
134 this.vertex3 = vertex3;
135 }
136
137 /**
138 * Returns vertices of this triangle as a list of points.
139 *
140 * @return Vertices of this triangle.
141 */
142 public List<Point2D> getVertices() {
143 final var vertices = new ArrayList<Point2D>(NUM_VERTICES);
144 vertices(vertices);
145 return vertices;
146 }
147
148 /**
149 * Stores vertices of this triangle in provided list. Note that content of
150 * list will be cleared before storing this triangle's vertices.
151 *
152 * @param result list where vertices will be stored.
153 */
154 public void vertices(final List<Point2D> result) {
155 result.clear();
156 result.add(vertex1);
157 result.add(vertex2);
158 result.add(vertex3);
159 }
160
161 /**
162 * Sets all vertices of this triangle.
163 *
164 * @param vertex1 1st vertex.
165 * @param vertex2 2nd vertex.
166 * @param vertex3 3rd vertex.
167 * @throws NullPointerException Raised if any of the vertices is null.
168 */
169 public final void setVertices(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
170 if (vertex1 == null || vertex2 == null || vertex3 == null) {
171 throw new NullPointerException();
172 }
173
174 this.vertex1 = vertex1;
175 this.vertex2 = vertex2;
176 this.vertex3 = vertex3;
177 }
178
179 /**
180 * Returns area of provided triangle with sign. If vertices are defined
181 * clockwise area is positive, otherwise returned area is negative.
182 *
183 * @param triangle Triangle to be evaluated.
184 * @return Area of triangle with sign. Positive sign indicates that vertices
185 * are clockwise, negative sign indicates that vertices are counterclockwise.
186 */
187 public static double signedArea(final Triangle2D triangle) {
188 return signedArea(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3());
189 }
190
191 /**
192 * Returns area with sign of the triangle formed by provided vertices. If
193 * vertices are defined clockwise area is negative, otherwise returned area
194 * is positive.
195 *
196 * @param vertex1 1st vertex of a triangle.
197 * @param vertex2 2nd vertex of a triangle.
198 * @param vertex3 3rd vertex of a triangle.
199 * @return Area of a triangle with sign. Negative sign indicates that
200 * vertices are clockwise, positive sign indicates that vertices are
201 * counterclockwise.
202 */
203 public static double signedArea(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
204 // The signed area of a triangle is half the determinant of its vectors,
205 // or half the modulus of the cross product of its vectors
206
207 // Hence, having the vectors of the triangle defined as:
208 // v1 = vertex2 - vertex1, and v2 = vertex3 - vertex1, then:
209 final var p1x = vertex1.getInhomX();
210 final var p1y = vertex1.getInhomY();
211
212 final var x1 = vertex2.getInhomX() - p1x;
213 final var y1 = vertex2.getInhomY() - p1y;
214
215 final var x2 = vertex3.getInhomX() - p1x;
216 final var y2 = vertex3.getInhomY() - p1y;
217
218 // Considering the matrix:
219 // [x1 x2]
220 // [y1 y2]
221 // Then half its determinant or half the cross product of its column
222 // vectors is:
223 return 0.5 * (x1 * y2 - x2 * y1);
224 }
225
226 /**
227 * Returns area of this triangle with sign. If vertices are defined
228 * clockwise area is positive, otherwise returned area is negative.
229 *
230 * @return Area of this triangle with sign. Positive sign indicates that
231 * vertices are clockwise, negative sign indicates that vertices are
232 * counterclockwise.
233 */
234 public double getSignedArea() {
235 return signedArea(vertex1, vertex2, vertex3);
236 }
237
238 /**
239 * Returns area of provided triangle.
240 *
241 * @param triangle Triangle to be checked.
242 * @return Area of triangle.
243 */
244 public static double area(final Triangle2D triangle) {
245 return area(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3());
246 }
247
248 /**
249 * Returns area of a triangle formed by provided vertices.
250 *
251 * @param vertex1 1st vertex of a triangle.
252 * @param vertex2 2nd vertex of a triangle.
253 * @param vertex3 3rd vertex of a triangle.
254 * @return Area of a triangle.
255 */
256 public static double area(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
257 return Math.abs(signedArea(vertex1, vertex2, vertex3));
258 }
259
260 /**
261 * Returns area of this triangle.
262 *
263 * @return Area of this triangle.
264 */
265 public double getArea() {
266 return area(vertex1, vertex2, vertex3);
267 }
268
269 /**
270 * Determines whether vertices of this triangle are considered to be
271 * co-linear. Points are considered to be colinear when area of triangle is
272 * very small.
273 *
274 * @return True if vertices are colinear, false otherwise.
275 */
276 public boolean areVerticesColinear() {
277 return areVerticesColinear(DEFAULT_THRESHOLD);
278 }
279
280 /**
281 * Determines whether vertices of this triangle are considered to be
282 * co-linear up to certain threshold. Points are considered to be colinear
283 * when are of triangle is very small.
284 *
285 * @param threshold Threshold to determine whether vertices are colinear.
286 * Vertices will be colinear when area of triangle is smaller than provided
287 * threshold.
288 * @return True if vertices are colinear, false otherwise.
289 * @throws IllegalArgumentException Raised if provided threshold is negative.
290 */
291 public boolean areVerticesColinear(final double threshold) {
292 if (threshold < MIN_THRESHOLD) {
293 throw new IllegalArgumentException();
294 }
295 return getArea() <= threshold;
296 }
297
298 /**
299 * Returns perimeter of provided triangle.
300 *
301 * @param triangle Perimeter of provided triangle.
302 * @return Perimeter of provided triangle.
303 */
304 public static double perimeter(final Triangle2D triangle) {
305 return perimeter(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3());
306 }
307
308 /**
309 * Returns perimeter of triangle formed by provided vertices.
310 *
311 * @param vertex1 1st vertex of a triangle.
312 * @param vertex2 2nd vertex of a triangle.
313 * @param vertex3 3rd vertex of a triangle.
314 * @return Perimeter of a triangle.
315 */
316 public static double perimeter(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
317 return vertex1.distanceTo(vertex2) + vertex2.distanceTo(vertex3) + vertex3.distanceTo(vertex1);
318 }
319
320 /**
321 * Returns perimeter of this triangle.
322 *
323 * @return Perimeter of this triangle.
324 */
325 public double getPerimeter() {
326 return perimeter(this);
327 }
328
329 /**
330 * Indicates whether provided point lies inside this triangle or not.
331 *
332 * @param point Point to be checked.
333 * @return True if point lies inside this triangle, false otherwise.
334 */
335 public boolean isInside(final Point2D point) {
336 return isInside(point, DEFAULT_THRESHOLD);
337 }
338
339 /**
340 * Indicates whether provided point lies inside this triangle or not up to
341 * a certain threshold.
342 *
343 * @param point Point to be checked.
344 * @param threshold Threshold to determine whether point is inside this
345 * triangle, or not. This should usually be a small value.
346 * @return True if point lies inside this triangle, false otherwise.
347 * @throws IllegalArgumentException Raised if provided threshold is negative.
348 */
349 public boolean isInside(final Point2D point, final double threshold) {
350 return isInside(vertex1, vertex2, vertex3, point, threshold);
351 }
352
353 /**
354 * Indicates whether provided point lies inside provided triangle or not.
355 *
356 * @param triangle A triangle.
357 * @param point Point to be checked.
358 * @return True if point lies inside provided triangle, false otherwise.
359 */
360 public static boolean isInside(final Triangle2D triangle, final Point2D point) {
361 return isInside(triangle, point, DEFAULT_THRESHOLD);
362 }
363
364 /**
365 * Indicates whether provided point lies inside provided triangle or not up
366 * to a certain threshold.
367 *
368 * @param triangle A triangle.
369 * @param point Point to be checked.
370 * @param threshold Threshold to determine whether point is inside this
371 * triangle, or not. This should usually be a small value.
372 * @return True if point lies inside this triangle, false otherwise.
373 * @throws IllegalArgumentException Raised if provided threshold is negative.
374 */
375 public static boolean isInside(final Triangle2D triangle, final Point2D point, final double threshold) {
376 return isInside(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3(), point, threshold);
377 }
378
379 /**
380 * Indicates whether provided point lies inside a triangle formed by
381 * provided vertices or not.
382 *
383 * @param vertex1 1st vertex of a triangle.
384 * @param vertex2 2nd vertex of a triangle.
385 * @param vertex3 3rd vertex of a triangle.
386 * @param point Point to be checked.
387 * @return True if point lies inside triangle formed by provided vertices,
388 * false otherwise.
389 */
390 public static boolean isInside(
391 final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D point) {
392 return isInside(vertex1, vertex2, vertex3, point, DEFAULT_THRESHOLD);
393 }
394
395 /**
396 * Indicates whether provided point lies inside a triangle formed by
397 * provided vertices or not up to a certain threshold.
398 *
399 * @param vertex1 1st vertex of a triangle.
400 * @param vertex2 2nd vertex of a triangle.
401 * @param vertex3 3rd vertex of a triangle.
402 * @param point Point to be checked.
403 * @param threshold Threshold to determine whether point is inside the
404 * triangle formed by provided vertices or not. This should usually be a
405 * small value.
406 * @return True if point lies inside triangle formed by provided vertices,
407 * false otherwise.
408 * @throws IllegalArgumentException Raised if provided threshold is negative.
409 */
410 public static boolean isInside(
411 final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D point,
412 final double threshold) {
413 if (threshold < MIN_THRESHOLD) {
414 throw new IllegalArgumentException();
415 }
416 // given triangle ABC made by vectors:
417 // ab = p2 - p1, and ac = p3 - p1
418
419 // If point (x, y) lies within triangle ABC, then we have 3 sub-triangles
420 // ApB, BpC and ApC made of points:
421 // ApB: mVertex1, point, mVertex2
422 // BpC: mVertex2, point, mVertex3
423 // ApC: mVertex3, point, mVertex1
424
425 // The point will lie inside triangle ABC if the sum of the areas of the
426 // 3 sub-triangles ApB, BpC and ApC equals the area of triangle ABC (up to
427 // certain accuracy to account for numerical precision)
428
429 // Then the areas of triangles are:
430 final var areaABC = area(vertex1, vertex2, vertex3);
431
432 final var areaApB = area(vertex1, point, vertex2);
433 final var areaBpC = area(vertex2, point, vertex3);
434 final var areaApC = area(vertex3, point, vertex1);
435
436 return Math.abs(areaApB + areaBpC + areaApC - areaABC) <= threshold;
437 }
438
439 /**
440 * Returns center of this triangle, which is the result of averaging its
441 * vertices.
442 *
443 * @return Center of this triangle.
444 */
445 public Point2D getCenter() {
446 final var result = Point2D.create();
447 center(result);
448 return result;
449 }
450
451 /**
452 * Computes the center of this triangle and stores the result in provided
453 * point. The center of this triangle is computed as the average of its
454 * vertices.
455 *
456 * @param result Point instance where center will be stored.
457 */
458 public void center(final Point2D result) {
459 center(vertex1, vertex2, vertex3, result);
460 }
461
462 /**
463 * Computes the center of a triangle formed by provided vertices.
464 * The center is computed as the average of the three vertices.
465 *
466 * @param vertex1 1st vertex of a triangle.
467 * @param vertex2 2nd vertex of a triangle.
468 * @param vertex3 3rd vertex of a triangle.
469 * @return Center of a triangle formed by provided vertices.
470 */
471 public static Point2D center(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
472 final var result = Point2D.create();
473 center(vertex1, vertex2, vertex3, result);
474 return result;
475 }
476
477 /**
478 * Computes the center of provided triangle.
479 * The center is computed as the average of the vertices of provided
480 * triangle.
481 *
482 * @param t A triangle.
483 * @return Center of provided triangle.
484 */
485 public static Point2D center(final Triangle2D t) {
486 return center(t.getVertex1(), t.getVertex2(), t.getVertex3());
487 }
488
489 /**
490 * Computes the center of a triangle formed by provided vertices and stores
491 * the result in provided result point.
492 * The center is computed as the average of provided vertices.
493 *
494 * @param vertex1 1st vertex of a triangle.
495 * @param vertex2 2nd vertex of a triangle.
496 * @param vertex3 3rd vertex of a triangle.
497 * @param result Point instance where center will be stored.
498 */
499 public static void center(
500 final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D result) {
501
502 final var x = (vertex1.getInhomX() + vertex2.getInhomX() + vertex3.getInhomX()) / 3.0;
503 final var y = (vertex1.getInhomY() + vertex2.getInhomY() + vertex3.getInhomY()) / 3.0;
504
505 result.setInhomogeneousCoordinates(x, y);
506 }
507
508 /**
509 * Computes the center of provided triangle and stores the result in
510 * provided result point.
511 * The center is computed as the average of the vertices of provided
512 * triangle.
513 *
514 * @param t A triangle.
515 * @param result Point instance where center will be stored.
516 */
517 public static void center(final Triangle2D t, final Point2D result) {
518 center(t.getVertex1(), t.getVertex2(), t.getVertex3(), result);
519 }
520
521 /**
522 * Computes the shortest distance from a given point to the boundaries of
523 * this triangle, considering its boundaries as lines with a finite length
524 * Distance is computed up to triangle boundary, no matter if point lies
525 * inside the triangle or not.
526 *
527 * @param point Point to be checked.
528 * @return Shortest distance to this triangle.
529 */
530 public double getShortestDistance(final Point2D point) {
531 return shortestDistance(this, point);
532 }
533
534 /**
535 * Computes the shortest distance from a given point to the boundaries of
536 * provided triangle, considering its boundaries as lines with a finite
537 * length.
538 * Distance is computed up to triangle boundary, no matter if point lies
539 * inside the triangle or not.
540 *
541 * @param triangle A triangle.
542 * @param point Point to be checked.
543 * @return Shortest distance to this triangle.
544 */
545 public static double shortestDistance(final Triangle2D triangle, final Point2D point) {
546 return shortestDistance(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3(), point);
547 }
548
549 // shortest distance to the sides of the triangle, no matter if the point
550 // lies inside the triangle or not
551
552 /**
553 * Computes the shortest distance from a given point to the boundaries of
554 * a triangle formed by provided vertices, where those boundaries are
555 * considered to be lines with a finite length.
556 * Distance is computed up to triangle boundary, no matter if point lies
557 * inside the triangle or not.
558 *
559 * @param vertex1 1st vertex of a triangle.
560 * @param vertex2 2nd vertex of a triangle.
561 * @param vertex3 3rd vertex of a triangle.
562 * @param point Point to be checked.
563 * @return Shortest distance to the triangle formed by provided vertices.
564 */
565 public static double shortestDistance(
566 final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D point) {
567
568 // normalize points to increase accuracy
569 vertex1.normalize();
570 vertex2.normalize();
571 vertex3.normalize();
572 point.normalize();
573
574 double bestDist;
575 double dist;
576
577 final var line = new Line2D();
578 line.setParametersFromPairOfPoints(vertex1, vertex2);
579 // to increase accuracy
580 line.normalize();
581 if (line.isLocus(point)) {
582 if (point.isBetween(vertex1, vertex2)) {
583 return 0.0;
584 } else {
585 // point is outside the triangle and
586 // point belongs to the line forming this side of the triangle,
587 // hence the closest vertex of this line will be the shortest
588 // distance
589 bestDist = vertex1.distanceTo(point);
590 dist = vertex2.distanceTo(point);
591 if (dist < bestDist) {
592 bestDist = dist;
593 }
594
595 return bestDist;
596 }
597 }
598
599 // point does not belong to the first line
600 bestDist = Math.abs(line.signedDistance(point));
601
602 // try on second side of the triangle
603 line.setParametersFromPairOfPoints(vertex1, vertex3);
604 // to increase accuracy
605 line.normalize();
606 if (line.isLocus(point)) {
607 if (point.isBetween(vertex1, vertex3)) {
608 return 0.0;
609 } else {
610 // point belongs to the line forming this side of the triangle,
611 // hence the closest vertex of this line will be the shortest
612 // distance
613 bestDist = vertex1.distanceTo(point);
614 dist = vertex3.distanceTo(point);
615 if (dist < bestDist) {
616 bestDist = dist;
617 }
618
619 return bestDist;
620 }
621 }
622
623 // point does not belong to the first or second line
624 dist = Math.abs(line.signedDistance(point));
625
626 // check if second line is closest to first line
627 if (dist < bestDist) {
628 bestDist = dist;
629 }
630
631 // try on third side of the triangle
632 line.setParametersFromPairOfPoints(vertex2, vertex3);
633 // to increase accuracy
634 line.normalize();
635 if (line.isLocus(point)) {
636 if (point.isBetween(vertex2, vertex3)) {
637 return 0.0;
638 } else {
639 // point belongs to the line forming this side of the triangle,
640 // hence the closest vertex of this line will be the shortest
641 // distance
642 bestDist = vertex2.distanceTo(point);
643 dist = vertex3.distanceTo(point);
644 if (dist < bestDist) {
645 bestDist = dist;
646 }
647
648 return bestDist;
649 }
650 }
651
652 // point does not belong to any line forming a side of the triangle
653 dist = Math.abs(line.signedDistance(point));
654
655 // check if distance to third line is the shortest
656 if (dist < bestDist) {
657 bestDist = dist;
658 }
659
660 return bestDist;
661 }
662
663 /**
664 * Returns the point which is locus of this triangle closest to provided
665 * point.
666 *
667 * @param point Point to be checked.
668 * @return Closest point laying in this triangle boundaries.
669 */
670 public Point2D getClosestPoint(final Point2D point) {
671 return getClosestPoint(point, DEFAULT_THRESHOLD);
672 }
673
674 /**
675 * Returns the point which is locus of this triangle (up to a certain
676 * threshold) closest to provided point.
677 *
678 * @param point Point to be checked.
679 * @param threshold Threshold to determine when a point is locus of this
680 * triangle or not.
681 * @return Closest point laying in this triangle boundaries.
682 * @throws IllegalArgumentException Raised if provided threshold is negative.
683 */
684 public Point2D getClosestPoint(final Point2D point, final double threshold) {
685 final var result = Point2D.create();
686 closestPoint(point, result, threshold);
687 return result;
688 }
689
690 /**
691 * Computes the point which is locus of this triangle closest to provided
692 * point and stores the result in provided result point.
693 *
694 * @param point Point to be checked.
695 * @param result Point where result will be stored.
696 */
697 public void closestPoint(final Point2D point, final Point2D result) {
698 closestPoint(point, result, DEFAULT_THRESHOLD);
699 }
700
701 /**
702 * Computes the point which is locus of this triangle (up to a certain
703 * threshold) closest to provided point and stores the result in provided
704 * result point.
705 *
706 * @param point Point to be checked.
707 * @param result Point where result will be stored.
708 * @param threshold Threshold to determine when a point is locus of this
709 * triangle or not.
710 * @throws IllegalArgumentException Raised if provided threshold is negative.
711 */
712 public void closestPoint(final Point2D point, final Point2D result, final double threshold) {
713 if (threshold < MIN_THRESHOLD) {
714 throw new IllegalArgumentException();
715 }
716
717 // normalize vertices and point to increase accuracy
718 vertex1.normalize();
719 vertex2.normalize();
720 vertex3.normalize();
721 point.normalize();
722
723 final var line1 = new Line2D(vertex1, vertex2);
724 // to increase accuracy
725 line1.normalize();
726 if (line1.isLocus(point)) {
727 if (point.isBetween(vertex1, vertex2)) {
728 // point is on this side of the triangle, so point must be the
729 // result
730 result.setCoordinates(point);
731 } else {
732 // point belongs to the line forming this side of the triangle,
733 // hence the closest vertex of this line will be the closest
734 // point to the triangle
735 final var dist1 = vertex1.distanceTo(point);
736 final var dist2 = vertex2.distanceTo(point);
737 if (dist1 < dist2) {
738 result.setCoordinates(vertex1);
739 } else {
740 result.setCoordinates(vertex2);
741 }
742 }
743 return;
744 }
745
746 // try on second side of the triangle
747 final var line2 = new Line2D(vertex1, vertex3);
748 // to increase accuracy
749 line2.normalize();
750 if (line2.isLocus(point)) {
751 if (point.isBetween(vertex1, vertex3)) {
752 // point is on this side of the triangle, so point must be the
753 // result
754 result.setCoordinates(point);
755 } else {
756 // point belongs to the line forming this side of the triangle,
757 // hence the closest vertex of this line will be the closest
758 // point to the triangle
759 final var dist1 = vertex1.distanceTo(point);
760 final var dist3 = vertex3.distanceTo(point);
761 if (dist1 < dist3) {
762 result.setCoordinates(vertex1);
763 } else {
764 result.setCoordinates(vertex3);
765 }
766 }
767 return;
768 }
769
770 // try on third side of the triangle
771 final var line3 = new Line2D(vertex2, vertex3);
772 // to increase accuracy
773 line3.normalize();
774 if (line3.isLocus(point)) {
775 if (point.isBetween(vertex2, vertex3)) {
776 // point is on this side of the triangle, so point must be the
777 // result
778 result.setCoordinates(point);
779 } else {
780 // point belongs to the line forming this side of the triangle,
781 // hence the closest vertex of this line will be the closest
782 // point to the triangle
783 final var dist2 = vertex2.distanceTo(point);
784 final var dist3 = vertex3.distanceTo(point);
785 if (dist2 < dist3) {
786 result.setCoordinates(vertex2);
787 } else {
788 result.setCoordinates(vertex3);
789 }
790 }
791 return;
792 }
793
794 // point does not belong to any line forming a side of the triangle,
795 // so we find the closest point for each side
796 final var closest1 = line1.getClosestPoint(point, threshold);
797 // to increase accuracy
798 closest1.normalize();
799 final var closest2 = line2.getClosestPoint(point, threshold);
800 // to increase accuracy
801 closest2.normalize();
802 final var closest3 = line3.getClosestPoint(point, threshold);
803 // to increase accuracy
804 closest3.normalize();
805
806 // check if points lie within sides of triangle
807 final var between1 = closest1.isBetween(vertex1, vertex2);
808 final var between2 = closest2.isBetween(vertex1, vertex3);
809 final var between3 = closest3.isBetween(vertex2, vertex3);
810
811 final var distClosest1 = closest1.distanceTo(point);
812 final var distClosest2 = closest2.distanceTo(point);
813 final var distClosest3 = closest3.distanceTo(point);
814
815 final var distVertex1 = vertex1.distanceTo(point);
816 final var distVertex2 = vertex2.distanceTo(point);
817 final var distVertex3 = vertex3.distanceTo(point);
818
819 if (between1 && !between2 && !between3) {
820 // choose closest1 or opposite vertex (vertex3)
821 if (distClosest1 < distVertex3) {
822 result.setCoordinates(closest1);
823 } else {
824 result.setCoordinates(vertex3);
825 }
826 } else if (!between1 && between2 && !between3) {
827 // choose closest2 or opposite vertex (vertex2)
828 if (distClosest2 < distVertex2) {
829 result.setCoordinates(closest2);
830 } else {
831 result.setCoordinates(vertex2);
832 }
833 } else if (!between1 && !between2 && between3) {
834 // choose closest3 or opposite vertex (vertex1)
835 if (distClosest3 < distVertex1) {
836 result.setCoordinates(closest3);
837 } else {
838 result.setCoordinates(vertex1);
839 }
840 } else if (between1 && between2 && !between3) {
841 // determine if closest1 or closest2
842 if (distClosest1 < distClosest2) {
843 result.setCoordinates(closest1);
844 } else {
845 result.setCoordinates(closest2);
846 }
847 } else if (!between1 && between2) {
848 // and between3
849
850 // determine if closest2 or closest3
851 if (distClosest2 < distClosest3) {
852 result.setCoordinates(closest2);
853 } else {
854 result.setCoordinates(closest3);
855 }
856 } else if (between1 && !between2) {
857 // and between3
858
859 //determine if closest1 or closest3
860 if (distClosest1 < distClosest3) {
861 result.setCoordinates(closest1);
862 } else {
863 result.setCoordinates(closest3);
864 }
865 } else if (between1) {
866 // and between2 and between3
867
868 //determine if closest1, closest2 or closest3
869 if (distClosest1 < distClosest2 && distClosest1 < distClosest3) {
870 // pick closest1
871 result.setCoordinates(closest1);
872 } else if (distClosest2 < distClosest1 &&
873 distClosest2 < distClosest3) {
874 // pick closest2
875 result.setCoordinates(closest2);
876 } else {
877 // pick closest3
878 result.setCoordinates(closest3);
879 }
880 } else {
881 // all closest points are outside vertex limits, so we pick the
882 // closest vertex
883
884 if (distVertex1 < distVertex2 && distVertex1 < distVertex3) {
885 // pick vertex1
886 result.setCoordinates(vertex1);
887 } else if (distVertex2 < distVertex1 && distVertex2 < distVertex3) {
888 // pick vertex2
889 result.setCoordinates(vertex2);
890 } else {
891 // pick vertex3
892 result.setCoordinates(vertex3);
893 }
894 }
895 }
896
897 /**
898 * Returns boolean indicating if provided point is locus of this triangle
899 * (i.e. lies within this triangle boundaries) up to a certain threshold.
900 *
901 * @param point Point to be checked.
902 * @param threshold Threshold to determine if point is locus or not. This
903 * should usually be a small value.
904 * @return True if provided point is locus, false otherwise.
905 * @throws IllegalArgumentException Raised if provided threshold is negative.
906 */
907 public boolean isLocus(final Point2D point, final double threshold) {
908 if (threshold < MIN_THRESHOLD) {
909 throw new IllegalArgumentException();
910 }
911
912 return point.isBetween(vertex1, vertex2, threshold) || point.isBetween(vertex1, vertex3, threshold)
913 || point.isBetween(vertex2, vertex3, threshold);
914 }
915
916 /**
917 * Returns boolean indicating if provided point is locus of this triangle
918 * (i.e. lies within this triangle boundaries).
919 *
920 * @param point Point to be checked.
921 * @return True if provided point is locus, false otherwise.
922 */
923 public boolean isLocus(final Point2D point) {
924 return isLocus(point, DEFAULT_THRESHOLD);
925 }
926
927 /**
928 * Indicates whether the vertices of this triangle are provided in clockwise
929 * order or not.
930 * Vertices of a triangle are in clockwise order when the triangle's signed
931 * area is negative.
932 *
933 * @param threshold Threshold to determine if vertices are clockwise or not.
934 * This should usually be 0.0 and there is no restriction in sign
935 * @return True if vertices of this triangle are in clockwise order, false
936 * if they are in counterclockwise order.
937 */
938 public boolean areVerticesClockwise(final double threshold) {
939 return getSignedArea() < threshold;
940 }
941
942 /**
943 * Indicates whether the vertices of this triangle are provided in clockwise
944 * order or not.
945 *
946 * @return True if vertices of this triangle are in clockwise order, false
947 * if they are in counterclockwise order.
948 */
949 public boolean areVerticesClockwise() {
950 return areVerticesClockwise(0.0); // default threshold to check sign
951 }
952
953 //TODO: compute surrounding circle and inner circle
954 }