Triangle2D.java
/*
* Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.geometry;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* This class defines a triangle in the 2D space.
*/
@SuppressWarnings("DuplicatedCode")
public class Triangle2D implements Serializable {
/**
* Default threshold value. Thresholds are used to determine whether a point
* lies inside the triangle or not, or if it's locus or not, etc.
*/
public static final double DEFAULT_THRESHOLD = 1e-9;
/**
* Minimum allowed threshold value.
*/
public static final double MIN_THRESHOLD = 0.0;
/**
* Constant defining number of vertices on a triangle.
*/
public static final int NUM_VERTICES = 3;
/**
* 1st vertex of this triangle.
*/
private Point2D vertex1;
/**
* 2nd vertex of this triangle.
*/
private Point2D vertex2;
/**
* 3rd vertex of this triangle.
*/
private Point2D vertex3;
/**
* Constructor.
*
* @param vertex1 1st vertex.
* @param vertex2 2nd vertex.
* @param vertex3 3rd vertex.
* @throws NullPointerException Raised if any of the vertices is null.
*/
public Triangle2D(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
setVertices(vertex1, vertex2, vertex3);
}
/**
* Returns 1st vertex of this triangle.
*
* @return 1st vertex.
*/
public Point2D getVertex1() {
return vertex1;
}
/**
* Sets 1st vertex of this triangle.
*
* @param vertex1 1st vertex.
* @throws NullPointerException Raised if provided vertex is null.
*/
public void setVertex1(final Point2D vertex1) {
if (vertex1 == null) {
throw new NullPointerException();
}
this.vertex1 = vertex1;
}
/**
* Returns 2nd vertex of this triangle.
*
* @return 2nd vertex.
*/
public Point2D getVertex2() {
return vertex2;
}
/**
* Sets 2nd vertex of this triangle.
*
* @param vertex2 2nd vertex.
* @throws NullPointerException Raised if provided vertex is null.
*/
public void setVertex2(final Point2D vertex2) {
if (vertex2 == null) {
throw new NullPointerException();
}
this.vertex2 = vertex2;
}
/**
* Returns 3rd vertex of this triangle.
*
* @return 3rd vertex.
*/
public Point2D getVertex3() {
return vertex3;
}
/**
* Sets 3rd vertex of this triangle.
*
* @param vertex3 3rd vertex.
* @throws NullPointerException Raised if provided vertex is null.
*/
public void setVertex3(final Point2D vertex3) {
if (vertex3 == null) {
throw new NullPointerException();
}
this.vertex3 = vertex3;
}
/**
* Returns vertices of this triangle as a list of points.
*
* @return Vertices of this triangle.
*/
public List<Point2D> getVertices() {
final var vertices = new ArrayList<Point2D>(NUM_VERTICES);
vertices(vertices);
return vertices;
}
/**
* Stores vertices of this triangle in provided list. Note that content of
* list will be cleared before storing this triangle's vertices.
*
* @param result list where vertices will be stored.
*/
public void vertices(final List<Point2D> result) {
result.clear();
result.add(vertex1);
result.add(vertex2);
result.add(vertex3);
}
/**
* Sets all vertices of this triangle.
*
* @param vertex1 1st vertex.
* @param vertex2 2nd vertex.
* @param vertex3 3rd vertex.
* @throws NullPointerException Raised if any of the vertices is null.
*/
public final void setVertices(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
if (vertex1 == null || vertex2 == null || vertex3 == null) {
throw new NullPointerException();
}
this.vertex1 = vertex1;
this.vertex2 = vertex2;
this.vertex3 = vertex3;
}
/**
* Returns area of provided triangle with sign. If vertices are defined
* clockwise area is positive, otherwise returned area is negative.
*
* @param triangle Triangle to be evaluated.
* @return Area of triangle with sign. Positive sign indicates that vertices
* are clockwise, negative sign indicates that vertices are counterclockwise.
*/
public static double signedArea(final Triangle2D triangle) {
return signedArea(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3());
}
/**
* Returns area with sign of the triangle formed by provided vertices. If
* vertices are defined clockwise area is negative, otherwise returned area
* is positive.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @return Area of a triangle with sign. Negative sign indicates that
* vertices are clockwise, positive sign indicates that vertices are
* counterclockwise.
*/
public static double signedArea(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
// The signed area of a triangle is half the determinant of its vectors,
// or half the modulus of the cross product of its vectors
// Hence, having the vectors of the triangle defined as:
// v1 = vertex2 - vertex1, and v2 = vertex3 - vertex1, then:
final var p1x = vertex1.getInhomX();
final var p1y = vertex1.getInhomY();
final var x1 = vertex2.getInhomX() - p1x;
final var y1 = vertex2.getInhomY() - p1y;
final var x2 = vertex3.getInhomX() - p1x;
final var y2 = vertex3.getInhomY() - p1y;
// Considering the matrix:
// [x1 x2]
// [y1 y2]
// Then half its determinant or half the cross product of its column
// vectors is:
return 0.5 * (x1 * y2 - x2 * y1);
}
/**
* Returns area of this triangle with sign. If vertices are defined
* clockwise area is positive, otherwise returned area is negative.
*
* @return Area of this triangle with sign. Positive sign indicates that
* vertices are clockwise, negative sign indicates that vertices are
* counterclockwise.
*/
public double getSignedArea() {
return signedArea(vertex1, vertex2, vertex3);
}
/**
* Returns area of provided triangle.
*
* @param triangle Triangle to be checked.
* @return Area of triangle.
*/
public static double area(final Triangle2D triangle) {
return area(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3());
}
/**
* Returns area of a triangle formed by provided vertices.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @return Area of a triangle.
*/
public static double area(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
return Math.abs(signedArea(vertex1, vertex2, vertex3));
}
/**
* Returns area of this triangle.
*
* @return Area of this triangle.
*/
public double getArea() {
return area(vertex1, vertex2, vertex3);
}
/**
* Determines whether vertices of this triangle are considered to be
* co-linear. Points are considered to be colinear when area of triangle is
* very small.
*
* @return True if vertices are colinear, false otherwise.
*/
public boolean areVerticesColinear() {
return areVerticesColinear(DEFAULT_THRESHOLD);
}
/**
* Determines whether vertices of this triangle are considered to be
* co-linear up to certain threshold. Points are considered to be colinear
* when are of triangle is very small.
*
* @param threshold Threshold to determine whether vertices are colinear.
* Vertices will be colinear when area of triangle is smaller than provided
* threshold.
* @return True if vertices are colinear, false otherwise.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public boolean areVerticesColinear(final double threshold) {
if (threshold < MIN_THRESHOLD) {
throw new IllegalArgumentException();
}
return getArea() <= threshold;
}
/**
* Returns perimeter of provided triangle.
*
* @param triangle Perimeter of provided triangle.
* @return Perimeter of provided triangle.
*/
public static double perimeter(final Triangle2D triangle) {
return perimeter(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3());
}
/**
* Returns perimeter of triangle formed by provided vertices.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @return Perimeter of a triangle.
*/
public static double perimeter(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
return vertex1.distanceTo(vertex2) + vertex2.distanceTo(vertex3) + vertex3.distanceTo(vertex1);
}
/**
* Returns perimeter of this triangle.
*
* @return Perimeter of this triangle.
*/
public double getPerimeter() {
return perimeter(this);
}
/**
* Indicates whether provided point lies inside this triangle or not.
*
* @param point Point to be checked.
* @return True if point lies inside this triangle, false otherwise.
*/
public boolean isInside(final Point2D point) {
return isInside(point, DEFAULT_THRESHOLD);
}
/**
* Indicates whether provided point lies inside this triangle or not up to
* a certain threshold.
*
* @param point Point to be checked.
* @param threshold Threshold to determine whether point is inside this
* triangle, or not. This should usually be a small value.
* @return True if point lies inside this triangle, false otherwise.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public boolean isInside(final Point2D point, final double threshold) {
return isInside(vertex1, vertex2, vertex3, point, threshold);
}
/**
* Indicates whether provided point lies inside provided triangle or not.
*
* @param triangle A triangle.
* @param point Point to be checked.
* @return True if point lies inside provided triangle, false otherwise.
*/
public static boolean isInside(final Triangle2D triangle, final Point2D point) {
return isInside(triangle, point, DEFAULT_THRESHOLD);
}
/**
* Indicates whether provided point lies inside provided triangle or not up
* to a certain threshold.
*
* @param triangle A triangle.
* @param point Point to be checked.
* @param threshold Threshold to determine whether point is inside this
* triangle, or not. This should usually be a small value.
* @return True if point lies inside this triangle, false otherwise.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public static boolean isInside(final Triangle2D triangle, final Point2D point, final double threshold) {
return isInside(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3(), point, threshold);
}
/**
* Indicates whether provided point lies inside a triangle formed by
* provided vertices or not.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @param point Point to be checked.
* @return True if point lies inside triangle formed by provided vertices,
* false otherwise.
*/
public static boolean isInside(
final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D point) {
return isInside(vertex1, vertex2, vertex3, point, DEFAULT_THRESHOLD);
}
/**
* Indicates whether provided point lies inside a triangle formed by
* provided vertices or not up to a certain threshold.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @param point Point to be checked.
* @param threshold Threshold to determine whether point is inside the
* triangle formed by provided vertices or not. This should usually be a
* small value.
* @return True if point lies inside triangle formed by provided vertices,
* false otherwise.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public static boolean isInside(
final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D point,
final double threshold) {
if (threshold < MIN_THRESHOLD) {
throw new IllegalArgumentException();
}
// given triangle ABC made by vectors:
// ab = p2 - p1, and ac = p3 - p1
// If point (x, y) lies within triangle ABC, then we have 3 sub-triangles
// ApB, BpC and ApC made of points:
// ApB: mVertex1, point, mVertex2
// BpC: mVertex2, point, mVertex3
// ApC: mVertex3, point, mVertex1
// The point will lie inside triangle ABC if the sum of the areas of the
// 3 sub-triangles ApB, BpC and ApC equals the area of triangle ABC (up to
// certain accuracy to account for numerical precision)
// Then the areas of triangles are:
final var areaABC = area(vertex1, vertex2, vertex3);
final var areaApB = area(vertex1, point, vertex2);
final var areaBpC = area(vertex2, point, vertex3);
final var areaApC = area(vertex3, point, vertex1);
return Math.abs(areaApB + areaBpC + areaApC - areaABC) <= threshold;
}
/**
* Returns center of this triangle, which is the result of averaging its
* vertices.
*
* @return Center of this triangle.
*/
public Point2D getCenter() {
final var result = Point2D.create();
center(result);
return result;
}
/**
* Computes the center of this triangle and stores the result in provided
* point. The center of this triangle is computed as the average of its
* vertices.
*
* @param result Point instance where center will be stored.
*/
public void center(final Point2D result) {
center(vertex1, vertex2, vertex3, result);
}
/**
* Computes the center of a triangle formed by provided vertices.
* The center is computed as the average of the three vertices.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @return Center of a triangle formed by provided vertices.
*/
public static Point2D center(final Point2D vertex1, final Point2D vertex2, final Point2D vertex3) {
final var result = Point2D.create();
center(vertex1, vertex2, vertex3, result);
return result;
}
/**
* Computes the center of provided triangle.
* The center is computed as the average of the vertices of provided
* triangle.
*
* @param t A triangle.
* @return Center of provided triangle.
*/
public static Point2D center(final Triangle2D t) {
return center(t.getVertex1(), t.getVertex2(), t.getVertex3());
}
/**
* Computes the center of a triangle formed by provided vertices and stores
* the result in provided result point.
* The center is computed as the average of provided vertices.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @param result Point instance where center will be stored.
*/
public static void center(
final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D result) {
final var x = (vertex1.getInhomX() + vertex2.getInhomX() + vertex3.getInhomX()) / 3.0;
final var y = (vertex1.getInhomY() + vertex2.getInhomY() + vertex3.getInhomY()) / 3.0;
result.setInhomogeneousCoordinates(x, y);
}
/**
* Computes the center of provided triangle and stores the result in
* provided result point.
* The center is computed as the average of the vertices of provided
* triangle.
*
* @param t A triangle.
* @param result Point instance where center will be stored.
*/
public static void center(final Triangle2D t, final Point2D result) {
center(t.getVertex1(), t.getVertex2(), t.getVertex3(), result);
}
/**
* Computes the shortest distance from a given point to the boundaries of
* this triangle, considering its boundaries as lines with a finite length
* Distance is computed up to triangle boundary, no matter if point lies
* inside the triangle or not.
*
* @param point Point to be checked.
* @return Shortest distance to this triangle.
*/
public double getShortestDistance(final Point2D point) {
return shortestDistance(this, point);
}
/**
* Computes the shortest distance from a given point to the boundaries of
* provided triangle, considering its boundaries as lines with a finite
* length.
* Distance is computed up to triangle boundary, no matter if point lies
* inside the triangle or not.
*
* @param triangle A triangle.
* @param point Point to be checked.
* @return Shortest distance to this triangle.
*/
public static double shortestDistance(final Triangle2D triangle, final Point2D point) {
return shortestDistance(triangle.getVertex1(), triangle.getVertex2(), triangle.getVertex3(), point);
}
// shortest distance to the sides of the triangle, no matter if the point
// lies inside the triangle or not
/**
* Computes the shortest distance from a given point to the boundaries of
* a triangle formed by provided vertices, where those boundaries are
* considered to be lines with a finite length.
* Distance is computed up to triangle boundary, no matter if point lies
* inside the triangle or not.
*
* @param vertex1 1st vertex of a triangle.
* @param vertex2 2nd vertex of a triangle.
* @param vertex3 3rd vertex of a triangle.
* @param point Point to be checked.
* @return Shortest distance to the triangle formed by provided vertices.
*/
public static double shortestDistance(
final Point2D vertex1, final Point2D vertex2, final Point2D vertex3, final Point2D point) {
// normalize points to increase accuracy
vertex1.normalize();
vertex2.normalize();
vertex3.normalize();
point.normalize();
double bestDist;
double dist;
final var line = new Line2D();
line.setParametersFromPairOfPoints(vertex1, vertex2);
// to increase accuracy
line.normalize();
if (line.isLocus(point)) {
if (point.isBetween(vertex1, vertex2)) {
return 0.0;
} else {
// point is outside the triangle and
// point belongs to the line forming this side of the triangle,
// hence the closest vertex of this line will be the shortest
// distance
bestDist = vertex1.distanceTo(point);
dist = vertex2.distanceTo(point);
if (dist < bestDist) {
bestDist = dist;
}
return bestDist;
}
}
// point does not belong to the first line
bestDist = Math.abs(line.signedDistance(point));
// try on second side of the triangle
line.setParametersFromPairOfPoints(vertex1, vertex3);
// to increase accuracy
line.normalize();
if (line.isLocus(point)) {
if (point.isBetween(vertex1, vertex3)) {
return 0.0;
} else {
// point belongs to the line forming this side of the triangle,
// hence the closest vertex of this line will be the shortest
// distance
bestDist = vertex1.distanceTo(point);
dist = vertex3.distanceTo(point);
if (dist < bestDist) {
bestDist = dist;
}
return bestDist;
}
}
// point does not belong to the first or second line
dist = Math.abs(line.signedDistance(point));
// check if second line is closest to first line
if (dist < bestDist) {
bestDist = dist;
}
// try on third side of the triangle
line.setParametersFromPairOfPoints(vertex2, vertex3);
// to increase accuracy
line.normalize();
if (line.isLocus(point)) {
if (point.isBetween(vertex2, vertex3)) {
return 0.0;
} else {
// point belongs to the line forming this side of the triangle,
// hence the closest vertex of this line will be the shortest
// distance
bestDist = vertex2.distanceTo(point);
dist = vertex3.distanceTo(point);
if (dist < bestDist) {
bestDist = dist;
}
return bestDist;
}
}
// point does not belong to any line forming a side of the triangle
dist = Math.abs(line.signedDistance(point));
// check if distance to third line is the shortest
if (dist < bestDist) {
bestDist = dist;
}
return bestDist;
}
/**
* Returns the point which is locus of this triangle closest to provided
* point.
*
* @param point Point to be checked.
* @return Closest point laying in this triangle boundaries.
*/
public Point2D getClosestPoint(final Point2D point) {
return getClosestPoint(point, DEFAULT_THRESHOLD);
}
/**
* Returns the point which is locus of this triangle (up to a certain
* threshold) closest to provided point.
*
* @param point Point to be checked.
* @param threshold Threshold to determine when a point is locus of this
* triangle or not.
* @return Closest point laying in this triangle boundaries.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public Point2D getClosestPoint(final Point2D point, final double threshold) {
final var result = Point2D.create();
closestPoint(point, result, threshold);
return result;
}
/**
* Computes the point which is locus of this triangle closest to provided
* point and stores the result in provided result point.
*
* @param point Point to be checked.
* @param result Point where result will be stored.
*/
public void closestPoint(final Point2D point, final Point2D result) {
closestPoint(point, result, DEFAULT_THRESHOLD);
}
/**
* Computes the point which is locus of this triangle (up to a certain
* threshold) closest to provided point and stores the result in provided
* result point.
*
* @param point Point to be checked.
* @param result Point where result will be stored.
* @param threshold Threshold to determine when a point is locus of this
* triangle or not.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public void closestPoint(final Point2D point, final Point2D result, final double threshold) {
if (threshold < MIN_THRESHOLD) {
throw new IllegalArgumentException();
}
// normalize vertices and point to increase accuracy
vertex1.normalize();
vertex2.normalize();
vertex3.normalize();
point.normalize();
final var line1 = new Line2D(vertex1, vertex2);
// to increase accuracy
line1.normalize();
if (line1.isLocus(point)) {
if (point.isBetween(vertex1, vertex2)) {
// point is on this side of the triangle, so point must be the
// result
result.setCoordinates(point);
} else {
// point belongs to the line forming this side of the triangle,
// hence the closest vertex of this line will be the closest
// point to the triangle
final var dist1 = vertex1.distanceTo(point);
final var dist2 = vertex2.distanceTo(point);
if (dist1 < dist2) {
result.setCoordinates(vertex1);
} else {
result.setCoordinates(vertex2);
}
}
return;
}
// try on second side of the triangle
final var line2 = new Line2D(vertex1, vertex3);
// to increase accuracy
line2.normalize();
if (line2.isLocus(point)) {
if (point.isBetween(vertex1, vertex3)) {
// point is on this side of the triangle, so point must be the
// result
result.setCoordinates(point);
} else {
// point belongs to the line forming this side of the triangle,
// hence the closest vertex of this line will be the closest
// point to the triangle
final var dist1 = vertex1.distanceTo(point);
final var dist3 = vertex3.distanceTo(point);
if (dist1 < dist3) {
result.setCoordinates(vertex1);
} else {
result.setCoordinates(vertex3);
}
}
return;
}
// try on third side of the triangle
final var line3 = new Line2D(vertex2, vertex3);
// to increase accuracy
line3.normalize();
if (line3.isLocus(point)) {
if (point.isBetween(vertex2, vertex3)) {
// point is on this side of the triangle, so point must be the
// result
result.setCoordinates(point);
} else {
// point belongs to the line forming this side of the triangle,
// hence the closest vertex of this line will be the closest
// point to the triangle
final var dist2 = vertex2.distanceTo(point);
final var dist3 = vertex3.distanceTo(point);
if (dist2 < dist3) {
result.setCoordinates(vertex2);
} else {
result.setCoordinates(vertex3);
}
}
return;
}
// point does not belong to any line forming a side of the triangle,
// so we find the closest point for each side
final var closest1 = line1.getClosestPoint(point, threshold);
// to increase accuracy
closest1.normalize();
final var closest2 = line2.getClosestPoint(point, threshold);
// to increase accuracy
closest2.normalize();
final var closest3 = line3.getClosestPoint(point, threshold);
// to increase accuracy
closest3.normalize();
// check if points lie within sides of triangle
final var between1 = closest1.isBetween(vertex1, vertex2);
final var between2 = closest2.isBetween(vertex1, vertex3);
final var between3 = closest3.isBetween(vertex2, vertex3);
final var distClosest1 = closest1.distanceTo(point);
final var distClosest2 = closest2.distanceTo(point);
final var distClosest3 = closest3.distanceTo(point);
final var distVertex1 = vertex1.distanceTo(point);
final var distVertex2 = vertex2.distanceTo(point);
final var distVertex3 = vertex3.distanceTo(point);
if (between1 && !between2 && !between3) {
// choose closest1 or opposite vertex (vertex3)
if (distClosest1 < distVertex3) {
result.setCoordinates(closest1);
} else {
result.setCoordinates(vertex3);
}
} else if (!between1 && between2 && !between3) {
// choose closest2 or opposite vertex (vertex2)
if (distClosest2 < distVertex2) {
result.setCoordinates(closest2);
} else {
result.setCoordinates(vertex2);
}
} else if (!between1 && !between2 && between3) {
// choose closest3 or opposite vertex (vertex1)
if (distClosest3 < distVertex1) {
result.setCoordinates(closest3);
} else {
result.setCoordinates(vertex1);
}
} else if (between1 && between2 && !between3) {
// determine if closest1 or closest2
if (distClosest1 < distClosest2) {
result.setCoordinates(closest1);
} else {
result.setCoordinates(closest2);
}
} else if (!between1 && between2) {
// and between3
// determine if closest2 or closest3
if (distClosest2 < distClosest3) {
result.setCoordinates(closest2);
} else {
result.setCoordinates(closest3);
}
} else if (between1 && !between2) {
// and between3
//determine if closest1 or closest3
if (distClosest1 < distClosest3) {
result.setCoordinates(closest1);
} else {
result.setCoordinates(closest3);
}
} else if (between1) {
// and between2 and between3
//determine if closest1, closest2 or closest3
if (distClosest1 < distClosest2 && distClosest1 < distClosest3) {
// pick closest1
result.setCoordinates(closest1);
} else if (distClosest2 < distClosest1 &&
distClosest2 < distClosest3) {
// pick closest2
result.setCoordinates(closest2);
} else {
// pick closest3
result.setCoordinates(closest3);
}
} else {
// all closest points are outside vertex limits, so we pick the
// closest vertex
if (distVertex1 < distVertex2 && distVertex1 < distVertex3) {
// pick vertex1
result.setCoordinates(vertex1);
} else if (distVertex2 < distVertex1 && distVertex2 < distVertex3) {
// pick vertex2
result.setCoordinates(vertex2);
} else {
// pick vertex3
result.setCoordinates(vertex3);
}
}
}
/**
* Returns boolean indicating if provided point is locus of this triangle
* (i.e. lies within this triangle boundaries) up to a certain threshold.
*
* @param point Point to be checked.
* @param threshold Threshold to determine if point is locus or not. This
* should usually be a small value.
* @return True if provided point is locus, false otherwise.
* @throws IllegalArgumentException Raised if provided threshold is negative.
*/
public boolean isLocus(final Point2D point, final double threshold) {
if (threshold < MIN_THRESHOLD) {
throw new IllegalArgumentException();
}
return point.isBetween(vertex1, vertex2, threshold) || point.isBetween(vertex1, vertex3, threshold)
|| point.isBetween(vertex2, vertex3, threshold);
}
/**
* Returns boolean indicating if provided point is locus of this triangle
* (i.e. lies within this triangle boundaries).
*
* @param point Point to be checked.
* @return True if provided point is locus, false otherwise.
*/
public boolean isLocus(final Point2D point) {
return isLocus(point, DEFAULT_THRESHOLD);
}
/**
* Indicates whether the vertices of this triangle are provided in clockwise
* order or not.
* Vertices of a triangle are in clockwise order when the triangle's signed
* area is negative.
*
* @param threshold Threshold to determine if vertices are clockwise or not.
* This should usually be 0.0 and there is no restriction in sign
* @return True if vertices of this triangle are in clockwise order, false
* if they are in counterclockwise order.
*/
public boolean areVerticesClockwise(final double threshold) {
return getSignedArea() < threshold;
}
/**
* Indicates whether the vertices of this triangle are provided in clockwise
* order or not.
*
* @return True if vertices of this triangle are in clockwise order, false
* if they are in counterclockwise order.
*/
public boolean areVerticesClockwise() {
return areVerticesClockwise(0.0); // default threshold to check sign
}
//TODO: compute surrounding circle and inner circle
}