package com.tilusnet.josm.plugins.alignways.geometry;
import java.util.Objects;
/**
* @author tilusnet <tilusnet@gmail.com>
*
*/
public class AlignWaysGeomLine {
double coef_a, coef_b, coef_c;
public enum IntersectionStatus {
UNDEFINED,
INTERSECT_POINT,
LINES_PARALLEL,
LINES_OVERLAP
}
IntersectionStatus isectStat = IntersectionStatus.UNDEFINED;
/**
* Constructor defining the line with the coordinates of two points.
* @param x1 x coordinate of point 1.
* @param y1 y coordinate of point 1.
* @param x2 x coordinate of point 2.
* @param y2 y coordinate of point 2.
*/
public AlignWaysGeomLine(double x1, double y1, double x2, double y2) {
// ax + by + c = 0
//
// y2 - y1
// a = ------- (the slope); b = -1; c = y1 - a*x1
// x2 - x1
//
// See conversion guidelines: http://www.webmath.com/equline1.html
if (x1 == x2) {
// Vertical line (would result div by zero if equation applied)
coef_a = 1;
coef_b = 0;
coef_c = -x1;
} else {
coef_a = ((y2 - y1)/(x2 - x1));
coef_b = -1;
coef_c = y1 - coef_a * x1;
}
}
/**
* Constructor defining the line with the 3 coefficients of its equation, i.e. ax + by + c = 0.
* @param a Coefficient a.
* @param b Coefficient b.
* @param c Coefficient c.
*/
public AlignWaysGeomLine(double a, double b, double c) {
coef_a = a;
coef_b = b;
coef_c = c;
}
/**
* Constructor defining the line with the slope m and the y-intercept b, i.e. y = mx + b;
* @param m Slope.
* @param b Y-intercept.
*/
public AlignWaysGeomLine(double m, double b) {
coef_a = m;
coef_b = -1;
coef_c = b;
}
public AlignWaysGeomLine(AlignWaysGeomLine line) {
this(line.coef_a, line.coef_b, line.coef_c);
}
public AlignWaysGeomLine(AlignWaysGeomPoint awPt1, AlignWaysGeomPoint awPt2) {
this(awPt1.getX(), awPt1.getY(), awPt2.getX(), awPt2.getY());
}
/**
* Returns the intersection point of the line with another line.
* If the lines are parallel or overlap, returns null.
* Use getIntersectionStatus() to determine the case.
* @param other_line The other line.
* @return The intersection point of the lines.
*/
public AlignWaysGeomPoint getIntersection(AlignWaysGeomLine other_line) {
AlignWaysGeomPoint result = null;
// Use Cramer-rule, i.e.:
// - if (det1 != 0), there is an intersection in a point
// - if (det1 == 0, det2 == 0, det3 == 0), the lines overlap
// - if (det1 == 0) and any of det2 or det3 != 0, the lines are parallel
// See: http://www.mathwizz.com/algebra/help/help21.htm
// and https://en.wikipedia.org/wiki/Cramer%27s_rule
double det1 = (coef_a * other_line.coef_b) - (other_line.coef_a * coef_b);
double det2 = (-coef_c * other_line.coef_b) - (-other_line.coef_c * coef_b);
double det3 = (coef_a * -other_line.coef_c) - (other_line.coef_a * -coef_c);
if (Math.abs(det1) < 0.01) {
if ((Math.abs(det2) < 0.01) && (Math.abs(det3) < 0.01)) {
// Lines overlap
isectStat = IntersectionStatus.LINES_OVERLAP;
} else {
// Lines are parallel
isectStat = IntersectionStatus.LINES_PARALLEL;
}
} else {
// Lines intersect in a point
result = new AlignWaysGeomPoint(det2/det1, det3/det1);
isectStat = IntersectionStatus.INTERSECT_POINT;
}
return result;
}
/**
* Return the last result of getIntersection(), therefore use in conjuction with getIntersection().
* If getIntersection() was never called before, it returns IntersectionStatus.UNDEFINED.
* @return The last result of getIntersection().
*/
public IntersectionStatus getIntersectionStatus() {
return isectStat;
}
/**
* Get the Y coordinate on the line of a point with the given X coordinate.
*
* @param x The x-coordinate of the given point.
* @return The calculated y-coordinate or Double.NaN if the line is vertical.
*/
public Double getYonLine(double x) {
Double y = Double.valueOf((-coef_a*x - coef_c)/coef_b);
if (y.isInfinite() || y.isNaN())
// Vertical line
return Double.NaN;
else
return y;
}
/**
* Get the X coordinate on the line of a point with the given Y coordinate.
*
* @param y The y-coordinate of the given point.
* @return The calculated x-coordinate or Double.NaN if the line is horizontal.
*/
public Double getXonLine(double y) {
Double x = Double.valueOf((-coef_b*y - coef_c)/coef_a);
if (x.isInfinite() || x.isNaN())
// Horizontal line
return Double.NaN;
else
return x;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AlignWaysGeomLine))
return false;
AlignWaysGeomLine other = (AlignWaysGeomLine) obj;
if (Math.abs(this.coef_a - other.coef_a) < 0.01 &&
Math.abs(this.coef_b - other.coef_b) < 0.01 &&
Math.abs(this.coef_c - other.coef_c) < 0.01)
return true;
else
return false;
}
@Override
public int hashCode() {
return Objects.hash(coef_a, coef_b, coef_c);
}
public boolean isPointOnLine(AlignWaysGeomPoint awPt) {
// Method:
// 1. create a new line from awPt and one point of 'this'
// 2. check getIntersectionStatus of the two lines
// 3. if status is LINES_OVERLAP, the point os one the line, otherwise not
// Need an arbitrary point on this line; let it be (x, y)
Double x = 0.0;
Double y = getYonLine(x);
if (y.isNaN()) y = 0.0;
AlignWaysGeomLine line2 = new AlignWaysGeomLine(awPt, new AlignWaysGeomPoint(x, y));
getIntersection(line2);
if (getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP)
return true;
else
return false;
}
}