Commit f4fc4d2b authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

improve special case handling in triangulation walk.

parent 6fac4803
......@@ -706,10 +706,16 @@ public interface IPolyConnectivity<P extends IPoint, V extends IVertex<P>, E ext
* @param face the face
* @return true if the point (x, y) is contained in the face, false otherwise
*/
default boolean contains(final double x, final double y, final F face) {
default boolean contains(final double x, final double y, @NotNull final F face) {
return getMesh().streamEdges(face).noneMatch(edge -> isRightOf(x, y, edge));
}
default boolean contains(final double x, final double y, @NotNull final V v1, @NotNull final V v2, @NotNull final V v3) {
return !GeometryUtils.isRightOf(v1.getX(), v1.getY(), v2.getX(), v2.getY(), x, y)
&& !GeometryUtils.isRightOf(v2.getX(), v2.getY(), v3.getX(), v3.getY(), x, y)
&& !GeometryUtils.isRightOf(v3.getX(), v3.getY(), v1.getX(), v1.getY(), x, y);
}
/**
* Returns true if the point (x1, y1) is part of the face in O(n),
* where n is the number of edges of the face.
......
......@@ -18,6 +18,7 @@ import java.util.function.Consumer;
/**
* Builder class for Tikz Graphics used in tex / latex documents.
* @author Stefan Schuhbaeck
*/
public class TexGraphBuilder<P extends IPoint, V extends IVertex<P>, E extends IHalfEdge<P>, F extends IFace<P>> {
......
......@@ -16,8 +16,24 @@ import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author Benedikt Zoennchen
*/
public class TexGraphGenerator {
public static <P extends IPoint, V extends IVertex<P>, E extends IHalfEdge<P>, F extends IFace<P>> String toTikz(
@NotNull final IMesh<P, V, E, F> mesh, final float scaling){
return toTikz(mesh, scaling, false);
}
public static <P extends IPoint, V extends IVertex<P>, E extends IHalfEdge<P>, F extends IFace<P>> String toTikz(
@NotNull final IMesh<P, V, E, F> mesh, boolean standalone){
return toTikz(mesh, 1.0f, standalone);
}
/**
* Transforms a {@link IMesh} into a tikz string. The tikz graphic is scaled by the scaling.
*
......@@ -30,8 +46,13 @@ public class TexGraphGenerator {
* @return a string representing a tikz graphic
*/
public static <P extends IPoint, V extends IVertex<P>, E extends IHalfEdge<P>, F extends IFace<P>> String toTikz(
@NotNull final IMesh<P, V, E, F> mesh, final float scaling){
@NotNull final IMesh<P, V, E, F> mesh, final float scaling, final boolean standalone){
StringBuilder builder = new StringBuilder();
if(standalone) {
builder.append("\\documentclass[usenames,dvipsnames]{standalone}\n");
builder.append("\\usepackage{tikz}\n");
builder.append("\\begin{document}\n");
}
builder.append("\\begin{tikzpicture}[scale="+scaling+"]\n");
for(VPoint point : mesh.getUniquePoints()) {
......@@ -48,6 +69,10 @@ public class TexGraphGenerator {
builder.append("\\end{tikzpicture}");
if(standalone) {
builder.append("\\end{document}");
}
return builder.toString();
}
......
......@@ -409,11 +409,11 @@ public class EikonalSolverFMMTriangulation<P extends IPotentialPoint, V extends
Predicate<E> isEdgeInCone = e -> isPointInCone.test(e) || isPointInCone.test(getMesh().getPrev(e));
LinkedList<F> visitedFaces = triangulation.straightWalk2DGatherDirectional(halfEdge, face, direction2, isEdgeInCone);
F destination = visitedFaces.getLast();
LinkedList<E> visitedFaces = triangulation.straightWalk2DGatherDirectional(face, direction2, isEdgeInCone);
F destination = triangulation.getMesh().getFace(visitedFaces.getLast());
SimpleTriCanvas canvas = SimpleTriCanvas.simpleCanvas(getMesh());
visitedFaces.stream().forEach(f -> canvas.getColorFunctions().overwriteFillColor(f, Color.MAGENTA));
/*SimpleTriCanvas canvas = SimpleTriCanvas.simpleCanvas(getMesh());
visitedFaces.stream().map(e -> triangulation.getMesh().getFace(e)).forEach(f -> canvas.getColorFunctions().overwriteFillColor(f, Color.MAGENTA));
DebugGui.setDebugOn(true);
if(DebugGui.isDebugOn()) {
// attention the view is mirrowed.
......@@ -429,7 +429,7 @@ public class EikonalSolverFMMTriangulation<P extends IPotentialPoint, V extends
//graphics2D.fill(new VCircle(q, 0.05));
});
DebugGui.showAndWait(canvas);
}
}*/
assert !destination.equals(face);
......@@ -438,6 +438,26 @@ public class EikonalSolverFMMTriangulation<P extends IPotentialPoint, V extends
}
else {
logger.warn("walked to boundary");
visitedFaces = triangulation.straightWalk2DGatherDirectional(face, direction2, isEdgeInCone);
SimpleTriCanvas canvas = SimpleTriCanvas.simpleCanvas(getMesh());
visitedFaces.stream().map(e -> triangulation.getMesh().getFace(e)).forEach(f -> canvas.getColorFunctions().overwriteFillColor(f, Color.MAGENTA));
DebugGui.setDebugOn(true);
if(DebugGui.isDebugOn()) {
// attention the view is mirrowed.
canvas.addGuiDecorator(graphics -> {
Graphics2D graphics2D = (Graphics2D)graphics;
graphics2D.setColor(Color.GREEN);
graphics2D.setStroke(new BasicStroke(0.05f));
logger.info("p: " + p);
graphics2D.draw(new VLine(p, p.add(direction1.scalarMultiply(10))));
graphics2D.setColor(Color.BLUE);
graphics2D.draw(new VLine(p, p.add(direction2.scalarMultiply(10))));
//graphics2D.fill(new VCircle(new VPoint(getMesh().toPoint(startVertex)), 0.05));
//graphics2D.fill(new VCircle(q, 0.05));
});
DebugGui.showAndWait(canvas);
}
return Optional.empty();
}
......@@ -512,83 +532,6 @@ public class EikonalSolverFMMTriangulation<P extends IPotentialPoint, V extends
return minHe;
}
//TODO: refactoring!
private E findPointInCone(final E halfEdge, final P p1, final P p2) {
P point = getMesh().getPoint(halfEdge);
VTriangle triangle = new VTriangle(new VPoint(point), new VPoint(p1), new VPoint(p2));
// 1. construct the acute cone
VPoint direction = triangle.getIncenter().subtract(point);
double angle = Math.PI - GeometryUtils.angle(p1, point, p2);
VPoint origin = new VPoint(point);
VCone cone = new VCone(origin, direction, angle);
// 2. search for the nearest point inside the cone
Set<F> visitedFaces = new HashSet<>();
LinkedList<E> pointList = new LinkedList<>();
E edge = getMesh().getNext(getMesh().getTwin(getMesh().getPrev(halfEdge)));
pointList.add(edge);
visitedFaces.add(getMesh().getFace(edge));
while (!pointList.isEmpty()) {
E candidate = pointList.removeFirst();
// we can not search further since we reach the boundary.
if (!getMesh().isBoundary(candidate)) {
P vertex = getMesh().getPoint(candidate);
if (isFeasibleForComputation(vertex) && cone.contains(new VPoint(vertex))) {
return candidate;
} else if(cone.contains(new VPoint(vertex))) {
E newCandidate = getMesh().getNext(getMesh().getTwin(candidate));
if (!visitedFaces.contains(getMesh().getFace(newCandidate))) {
visitedFaces.add(getMesh().getFace(newCandidate));
pointList.add(newCandidate);
}
newCandidate = getMesh().getNext(getMesh().getTwin(getMesh().getNext(candidate)));
if (!visitedFaces.contains(getMesh().getFace(newCandidate))) {
visitedFaces.add(getMesh().getFace(newCandidate));
pointList.add(newCandidate);
}
}
else {
P v1 = getMesh().getPoint(candidate);
P v2 = getMesh().getPoint(getMesh().getPrev(candidate));
P v3 = getMesh().getPoint(getMesh().getNext(candidate));
VLine line1 = new VLine(new VPoint(v1), new VPoint(v2));
VLine line2 = new VLine(new VPoint(v1), new VPoint(v3));
if (cone.overlapLineSegment(line1)) {
E newCandidate = getMesh().getNext(getMesh().getTwin(candidate));
if (!visitedFaces.contains(getMesh().getFace(newCandidate))) {
visitedFaces.add(getMesh().getFace(newCandidate));
pointList.add(newCandidate);
}
}
if (cone.overlapLineSegment(line2)) {
E newCandidate = getMesh().getNext(getMesh().getTwin(getMesh().getNext(candidate)));
if (!visitedFaces.contains(getMesh().getFace(newCandidate))) {
visitedFaces.add(getMesh().getFace(newCandidate));
pointList.add(newCandidate);
}
}
}
}
else {
logger.warn("boundary reached!");
}
}
logger.warn("no virtual vertex was found");
return null;
}
private double computePotential(final P point, final P point1, final P point2) {
// see: Sethian, Level Set Methods and Fast Marching Methods, page 124.
......
package org.vadere.simulator.models.potential.solver;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.vadere.meshing.mesh.gen.MeshPanel;
import org.vadere.meshing.mesh.gen.PFace;
import org.vadere.meshing.mesh.gen.PHalfEdge;
import org.vadere.meshing.mesh.gen.PMesh;
import org.vadere.meshing.mesh.gen.PVertex;
import org.vadere.meshing.mesh.inter.IMeshSupplier;
import org.vadere.meshing.mesh.inter.IIncrementalTriangulation;
import org.vadere.meshing.mesh.inter.IVertex;
import org.vadere.meshing.mesh.triangulation.improver.eikmesh.gen.EikMesh;
import org.vadere.meshing.utils.tex.TexGraphGenerator;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.simulator.models.potential.solver.calculators.EikonalSolver;
import org.vadere.simulator.models.potential.solver.calculators.mesh.PotentialPoint;
import org.vadere.simulator.models.potential.solver.calculators.cartesian.EikonalSolverFMM;
import org.vadere.simulator.models.potential.solver.calculators.mesh.EikonalSolverFMMTriangulation;
import org.vadere.simulator.models.potential.solver.timecost.UnitTimeCostFunction;
import org.vadere.util.math.IDistanceFunction;
import org.vadere.meshing.mesh.triangulation.IEdgeLengthFunction;
import org.vadere.util.data.cellgrid.CellGrid;
import org.vadere.util.data.cellgrid.CellState;
import org.vadere.util.data.cellgrid.PathFindingTag;
import java.awt.*;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PerformanceTriangleFMM {
private static Logger log = LogManager.getLogger(TestFFMNonUniformTriangulation.class);
private static int width;
private static int height;
private static VRectangle bbox;
private static IIncrementalTriangulation<PotentialPoint, PVertex<PotentialPoint>, PHalfEdge<PotentialPoint>, PFace<PotentialPoint>> triangulation;
private static IDistanceFunction distanceFunc;
private static EikMesh<PotentialPoint, PVertex<PotentialPoint>, PHalfEdge<PotentialPoint>, PFace<PotentialPoint>> createEikMesh(
@NotNull final IEdgeLengthFunction edgeLengthFunc,
final double initialEdgeLen) {
IMeshSupplier<PotentialPoint, PVertex<PotentialPoint>, PHalfEdge<PotentialPoint>, PFace<PotentialPoint>> meshSupplier = () -> new PMesh<>((x, y) -> new PotentialPoint(x, y));
EikMesh<PotentialPoint, PVertex<PotentialPoint>, PHalfEdge<PotentialPoint>, PFace<PotentialPoint>> eikMesh = new EikMesh<>(
distanceFunc,
edgeLengthFunc,
initialEdgeLen,
bbox,
new ArrayList<>(),
meshSupplier);
return eikMesh;
}
public static void main(String... args) {
//IDistanceFunction distanceFunc = p -> Math.abs(7 - Math.sqrt(p.getX() * p.getX() + p.getY() * p.getY())) - 3;
distanceFunc = p -> Math.abs(6 - Math.sqrt(p.getX() * p.getX() + p.getY() * p.getY())) - 4;
//distanceFunc = p -> -10+Math.sqrt(p.getX() * p.getX() + p.getY() * p.getY());
//IDistanceFunction distanceFunc = p -> Math.abs(7 - Math.max(Math.abs(p.getX()), Math.abs(p.getY()))) - 3;
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0 + p.distanceToOrigin()*10;
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0 + Math.abs(distanceFunc.apply(p));
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0;
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0 + p.distanceToOrigin();
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0 + Math.abs(distanceFunc.apply(p));
bbox = new VRectangle(-12, -12, 24, 24);
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0 + 0.5*Math.min(Math.abs(distanceFunc.apply(p) + 4), Math.abs(distanceFunc.apply(p)));
IEdgeLengthFunction edgeLengthFunc = p -> 1.0;
//IEdgeLengthFunction edgeLengthFunc = p -> 1.0 + Math.abs(distanceFunc.apply(p)) * 0.5;
List<VRectangle> targetAreas = new ArrayList<>();
List<IPoint> targetPoints = new ArrayList<>();
/**
* We use the pointer based implementation
*/
EikMesh<PotentialPoint, PVertex<PotentialPoint>, PHalfEdge<PotentialPoint>, PFace<PotentialPoint>> meshGenerator = createEikMesh(edgeLengthFunc, 0.6);
// () -> new PMesh<>((x, y) -> new EikMeshPoint(x, y, false))
meshGenerator.generate();
triangulation = meshGenerator.getTriangulation();
Predicate<PFace<PotentialPoint>> nonAccute = f -> triangulation.getMesh().toTriangle(f).isNonAcute();
//MeshPanel meshPanel = new MeshPanel(meshGenerator.getMesh(), nonAccute, 1000, 1000, bbox);
//meshPanel.display();
//targetPoints.add(new MeshPoint(0, 0, false));
VRectangle rect = new VRectangle(width / 2, height / 2, 100, 100);
targetAreas.add(rect);
List<PVertex<PotentialPoint>> targetVertices = triangulation.getMesh().getBoundaryVertices().stream().collect(Collectors.toList());
EikonalSolver solver = new EikonalSolverFMMTriangulation(
new UnitTimeCostFunction(),
triangulation,
targetVertices,
distanceFunc);
long ms = System.currentTimeMillis();
log.info("start FFM");
solver.initialize();
log.info("FFM finished");
log.info("time: " + (System.currentTimeMillis() - ms));
double maxError = 0;
double sum = 0;
int counter = 0;
try {
//System.out.println(getClass().getClassLoader().getResource("./potentialField.csv").getFile());
Date timestamp = new Date();
FileWriter potentialFieldWriter = new FileWriter("./output/" + timestamp.getTime() + "potentialField_adapt_0_7.csv");
FileWriter meshWriter = new FileWriter("./output/"+ timestamp.getTime() + "mesh.tex");
meshWriter.write(TexGraphGenerator.toTikz(triangulation.getMesh(), true));
for(double y = bbox.getMinY()+2; y <= bbox.getMaxY()-2; y += 0.1) {
for(double x = bbox.getMinX()+2; x < bbox.getMaxX()-2; x += 0.1) {
double val = solver.getPotential(x ,y);
if(val >= 0.0 && val < Double.MAX_VALUE) {
double side = Math.min((new VPoint(x, y).distanceToOrigin()-2.0), (10 - new VPoint(x, y).distanceToOrigin()));
side = Math.max(side, 0.0);
maxError = Math.max(maxError, Math.abs(val - side));
sum += Math.abs(val - side) * Math.abs(val - side);
counter++;
}
potentialFieldWriter.write(""+solver.getPotential(x ,y) + " ");
}
potentialFieldWriter.write("\n");
}
potentialFieldWriter.flush();
potentialFieldWriter.close();
meshWriter.flush();
meshWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
log.info(triangulation.getMesh().getVertices().size());
log.info("max edge length: " + triangulation.getMesh().streamEdges().map(e -> triangulation.getMesh().toLine(e).length()).max(Comparator.comparingDouble(d -> d)));
log.info("min edge length: " +triangulation.getMesh().streamEdges().map(e -> triangulation.getMesh().toLine(e).length()).min(Comparator.comparingDouble(d -> d)));
log.info("max distance to boundary: " + triangulation.getMesh().getBoundaryVertices().stream().map(p -> Math.abs(distanceFunc.apply(p))).max(Comparator.comparingDouble(d -> d)));
//log.info("L2-Error: " + computeL2Error(triangulation, distanceFunc));
log.info("max error: " + maxError);
log.info("max error-2: " + triangulation.getMesh().getVertices().stream().map(p -> Math.abs(Math.abs(p.getPoint().getPotential() + distanceFunc.apply(p)))).max(Comparator.comparingDouble(d -> d)));
log.info("L2-error: " + Math.sqrt(sum / counter));
log.info("L2-error-2: " + Math.sqrt(triangulation.getMesh().getVertices().stream()
.map(p -> Math.abs(Math.abs(p.getPoint().getPotential() + distanceFunc.apply(p))))
.map(val -> val * val)
.reduce(0.0, (d1, d2) -> d1 + d2) / triangulation.getMesh().getNumberOfVertices()));
//assertTrue(0.0 == solver.getValue(5, 5));
//assertTrue(0.0 < solver.getValue(1, 7));
}
}
......@@ -15,6 +15,7 @@ import org.vadere.meshing.mesh.inter.IMeshSupplier;
import org.vadere.meshing.mesh.inter.IIncrementalTriangulation;
import org.vadere.meshing.mesh.inter.IVertex;
import org.vadere.meshing.mesh.triangulation.improver.eikmesh.gen.EikMesh;
import org.vadere.meshing.utils.tex.TexGraphGenerator;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
......@@ -35,6 +36,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
......@@ -99,6 +101,8 @@ public class TestFFMNonUniformTriangulation {
meshGenerator.generate();
triangulation = meshGenerator.getTriangulation();
Predicate<PFace<PotentialPoint>> nonAccute = f -> triangulation.getMesh().toTriangle(f).isNonAcute();
//MeshPanel meshPanel = new MeshPanel(meshGenerator.getMesh(), nonAccute, 1000, 1000, bbox);
//meshPanel.display();
......@@ -116,33 +120,40 @@ public class TestFFMNonUniformTriangulation {
triangulation,
targetVertices,
distanceFunc);
long ms = System.currentTimeMillis();
log.info("start FFM");
solver.initialize();
log.info("FFM finished");
log.info("time: " + (System.currentTimeMillis() - ms));
double maxError = 0;
double sum = 0;
int counter = 0;
try {
//System.out.println(getClass().getClassLoader().getResource("./potentialField.csv").getFile());
FileWriter writer = new FileWriter("./potentialField_adapt_0_7.csv");
Date timestamp = new Date();
FileWriter potentialFieldWriter = new FileWriter("./output/" + timestamp.getTime() + "potentialField_adapt_0_7.csv");
FileWriter meshWriter = new FileWriter("./output/"+ timestamp.getTime() + "mesh.tex");
meshWriter.write(TexGraphGenerator.toTikz(triangulation.getMesh(), true));
for(double y = bbox.getMinY()+2; y <= bbox.getMaxY()-2; y += 0.1) {
for(double x = bbox.getMinX()+2; x < bbox.getMaxX()-2; x += 0.1) {
double val = solver.getPotential(x ,y);
if(val >= 0.0) {
if(val >= 0.0 && val < Double.MAX_VALUE) {
double side = Math.min((new VPoint(x, y).distanceToOrigin()-2.0), (10 - new VPoint(x, y).distanceToOrigin()));
side = Math.max(side, 0.0);
maxError = Math.max(maxError, Math.abs(val - side));
sum += Math.abs(val - side) * Math.abs(val - side);
counter++;
}
writer.write(""+solver.getPotential(x ,y) + " ");
potentialFieldWriter.write(""+solver.getPotential(x ,y) + " ");
}
writer.write("\n");
potentialFieldWriter.write("\n");
}
writer.flush();
potentialFieldWriter.flush();
potentialFieldWriter.close();
meshWriter.flush();
meshWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
......@@ -393,6 +404,7 @@ public class TestFFMNonUniformTriangulation {
//assertTrue(0.0 < solver.getValue(1, 7));
}
@Ignore
@Test
public void testRegularFMMCase2() {
......
......@@ -450,15 +450,27 @@ public class GeometryUtils {
return intersectHalfLineSegment(p.getX(), p.getY(), q.getX(), q.getY(), p1.getX(), p1.getY(), p2.getX(), p2.getY());
}
/*public static boolean intersectHalfLineSegment(final double pX, final double pY, final double qX, final double qY, final double p1X, final double p1Y, final double p2X, final double p2Y) {
GeometryUtils.distanceToLineSegment()
}*/
public static boolean intersectHalfLineSegment(final double pX, final double pY, final double qX, final double qY, final double p1X, final double p1Y, final double p2X, final double p2Y) {
double ccw1 = ccw(pX, pY, qX, qY, p1X, p1X);
double ccw1 = ccw(pX, pY, qX, qY, p1X, p1Y);
double ccw2 = ccw(pX, pY, qX, qY, p2X, p2Y);
if((ccw1 < 0 && ccw2 > 0)) {
return isCCW(pX, pY, p2X, p2Y, p1X, p1Y);
}
else if((ccw1 > 0 && ccw2 < 0)) {
return isCCW(pX, pY, p1X, p1Y, p2X, p2Y);
// p1 and p2 are on different sides of directed line (q,p) if this is not the case there is no intersection
if((ccw1 < 0 && ccw2 > 0) || (ccw1 > 0 && ccw2 < 0)) {
double ccwq = ccw(p1X, p1Y, p2X, p2Y, qX, qY);
double ccwp = ccw(p1X, p1Y, p2X, p2Y, pX, pY);
// p and q on different sides, therefore the half-segment (q,p) intersects with the line (p1,p2)
if((ccwq < 0 && ccwp > 0) || (ccwq > 0 && ccwp < 0)) {
return true;
} // otherwise p has to be closer to the line-segment p1, p2 than q
else {
return GeometryUtils.distanceToLineSegment(p1X, p1Y, p2X, p2Y, qX, qY) < GeometryUtils.distanceToLineSegment(p1X, p1Y, p2X, p2Y, pX, pY);
}
}
else {
return false;
......@@ -661,26 +673,72 @@ public class GeometryUtils {
}
public static double distanceToLineSegment(@NotNull final IPoint p1, @NotNull final IPoint p2, final double x, final double y) {
if (p1.getX() == p2.getX() && p1.getY() == p2.getY())
return p1.distance(x,y);
return distanceToLineSegment(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
}
public static double distance(final double px, final double py, final double qx, final double qy) {
return Math.sqrt((px - qx) * (px - qx) + (py - qy) * (py - qy));
}
public static double distanceToLineSegment(final double p1X, final double p1Y, final double p2X, final double p2Y, final double x, final double y) {
// special cases
/*if(p1X == p2X) {
if((y > p1Y && p1Y > p2Y) || (y < p1Y && p1Y < p2Y)) {
return distance(p1X, p1Y, x, y);
}
else if((y > p2Y && p2Y > p1Y) || (y < p2Y && p2Y < p1Y)) {
return distance(p2X, p2Y, x, y);
}
else {
return Math.abs(p1X - x);
}
}
if(p1Y == p2Y) {
if((x > p1X && p1X > p2X) || (x < p1X && p1X < p2X)) {
return distance(p1X, p1Y, x, y);
}
else if((x > p2X && p2X > p1X) || (x < p2X && p2X < p1X)) {
return distance(p2X, p2Y, x, y);
}
else {
return Math.abs(p1Y - y);
}
}*/
double len2 = (p2.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p2.getY() - p1.getY()) * (p2.getY() - p1.getY());
double r = ((x - p1.getX()) * (p2.getX