RosettaCodeData/Task/Find-if-a-point-is-within-a-triangle/Java/find-if-a-point-is-within-a-triangle.java
2023-07-01 13:44:08 -04:00

136 lines
4.7 KiB
Java

import java.util.Objects;
public class FindTriangle {
private static final double EPS = 0.001;
private static final double EPS_SQUARE = EPS * EPS;
public static class Point {
private final double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
@Override
public String toString() {
return String.format("(%f, %f)", x, y);
}
}
public static class Triangle {
private final Point p1, p2, p3;
public Triangle(Point p1, Point p2, Point p3) {
this.p1 = Objects.requireNonNull(p1);
this.p2 = Objects.requireNonNull(p2);
this.p3 = Objects.requireNonNull(p3);
}
public Point getP1() {
return p1;
}
public Point getP2() {
return p2;
}
public Point getP3() {
return p3;
}
private boolean pointInTriangleBoundingBox(Point p) {
var xMin = Math.min(p1.getX(), Math.min(p2.getX(), p3.getX())) - EPS;
var xMax = Math.max(p1.getX(), Math.max(p2.getX(), p3.getX())) + EPS;
var yMin = Math.min(p1.getY(), Math.min(p2.getY(), p3.getY())) - EPS;
var yMax = Math.max(p1.getY(), Math.max(p2.getY(), p3.getY())) + EPS;
return !(p.getX() < xMin || xMax < p.getX() || p.getY() < yMin || yMax < p.getY());
}
private static double side(Point p1, Point p2, Point p) {
return (p2.getY() - p1.getY()) * (p.getX() - p1.getX()) + (-p2.getX() + p1.getX()) * (p.getY() - p1.getY());
}
private boolean nativePointInTriangle(Point p) {
boolean checkSide1 = side(p1, p2, p) >= 0;
boolean checkSide2 = side(p2, p3, p) >= 0;
boolean checkSide3 = side(p3, p1, p) >= 0;
return checkSide1 && checkSide2 && checkSide3;
}
private double distanceSquarePointToSegment(Point p1, Point p2, Point p) {
double p1_p2_squareLength = (p2.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p2.getY() - p1.getY()) * (p2.getY() - p1.getY());
double dotProduct = ((p.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p.getY() - p1.getY()) * (p2.getY() - p1.getY())) / p1_p2_squareLength;
if (dotProduct < 0) {
return (p.getX() - p1.getX()) * (p.getX() - p1.getX()) + (p.getY() - p1.getY()) * (p.getY() - p1.getY());
}
if (dotProduct <= 1) {
double p_p1_squareLength = (p1.getX() - p.getX()) * (p1.getX() - p.getX()) + (p1.getY() - p.getY()) * (p1.getY() - p.getY());
return p_p1_squareLength - dotProduct * dotProduct * p1_p2_squareLength;
}
return (p.getX() - p2.getX()) * (p.getX() - p2.getX()) + (p.getY() - p2.getY()) * (p.getY() - p2.getY());
}
private boolean accuratePointInTriangle(Point p) {
if (!pointInTriangleBoundingBox(p)) {
return false;
}
if (nativePointInTriangle(p)) {
return true;
}
if (distanceSquarePointToSegment(p1, p2, p) <= EPS_SQUARE) {
return true;
}
if (distanceSquarePointToSegment(p2, p3, p) <= EPS_SQUARE) {
return true;
}
return distanceSquarePointToSegment(p3, p1, p) <= EPS_SQUARE;
}
public boolean within(Point p) {
Objects.requireNonNull(p);
return accuratePointInTriangle(p);
}
@Override
public String toString() {
return String.format("Triangle[%s, %s, %s]", p1, p2, p3);
}
}
private static void test(Triangle t, Point p) {
System.out.println(t);
System.out.printf("Point %s is within triangle? %s\n", p, t.within(p));
}
public static void main(String[] args) {
var p1 = new Point(1.5, 2.4);
var p2 = new Point(5.1, -3.1);
var p3 = new Point(-3.8, 1.2);
var tri = new Triangle(p1, p2, p3);
test(tri, new Point(0, 0));
test(tri, new Point(0, 1));
test(tri, new Point(3, 1));
System.out.println();
p1 = new Point(1.0 / 10, 1.0 / 9);
p2 = new Point(100.0 / 8, 100.0 / 3);
p3 = new Point(100.0 / 4, 100.0 / 9);
tri = new Triangle(p1, p2, p3);
var pt = new Point(p1.getX() + (3.0 / 7) * (p2.getX() - p1.getX()), p1.getY() + (3.0 / 7) * (p2.getY() - p1.getY()));
test(tri, pt);
System.out.println();
p3 = new Point(-100.0 / 8, 100.0 / 6);
tri = new Triangle(p1, p2, p3);
test(tri, pt);
}
}