View Javadoc
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 }