package tc.oc.commons.bukkit.geometry; import org.bukkit.util.ImVector; import org.bukkit.util.Vector; import static com.google.common.base.Preconditions.checkNotNull; /** * Line segment defined by two points */ public class LineSegment { private final ImVector start, finish, delta; private LineSegment(ImVector start, ImVector finish, ImVector delta) { this.start = checkNotNull(start); this.finish = checkNotNull(finish); this.delta = delta; } public static LineSegment of(double x1, double y1, double z1, double x2, double y2, double z2) { return new LineSegment(ImVector.of(x1, y1, z1), ImVector.of(x2, y2, z2), ImVector.of(x2 - x1, y2 - y1, z2 - z1)); } public static LineSegment between(Vector start, Vector finish) { return new LineSegment(ImVector.copyOf(start), ImVector.copyOf(finish), ImVector.copyOf(finish.minus(start))); } public static LineSegment from(Vector start, Vector delta) { return new LineSegment(ImVector.copyOf(start), ImVector.copyOf(start.plus(delta)), ImVector.copyOf(delta)); } /** * The "start" point of the line segment */ public ImVector start() { return start; } /** * The "finish" point of the line segment */ public ImVector finish() { return finish; } /** * The vector from {@link #start()} to {@link #finish()} */ public ImVector delta() { return delta; } /** * Return an interpolated point on the line (but not necessarily on the segment) * based on the given parameter, such that: * * t < 0 before start * t == 0 start * 0 < t < 1 between start and finish * t == 1 finish * t > 1 after finish */ public ImVector parametricPoint(double t) { return ImVector.interpolate(start, finish, t); } /** * Find the perpendicular projection of the given point onto this line, * as a parameter that can be passed to {@link #parametricPoint(double)}. * * The projection may be outside the segment. */ public double perpendicularProjectionParameter(Vector point) { return point.minus(start).dot(delta) / delta.lengthSquared(); } /** * Return the minimal distance of the given point from this line segment. * * This will either be the perpendicular distance to the line, or the * cartesian distance from one of the endpoints. */ public double distance(Vector point) { return Math.sqrt(distanceSquared(point)); } public double distanceSquared(Vector point) { if(delta.isZero()) { return point.distanceSquared(start); } else { final double t = perpendicularProjectionParameter(point); if(t <= 0) { return point.distanceSquared(start); } else if(t >= 1) { return point.distanceSquared(finish); } else { return point.distanceSquared(parametricPoint(t)); } } } }