/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.gef.draw2d.geometry;
import java.util.ArrayList;
import java.util.List;
/**
* @author Frank Shaka
*/
public class PrecisionLine extends PrecisionPointPairBase implements Cloneable,
java.io.Serializable {
public static final double DEFAULT_TOLERANCE = 0.0001;
public enum LineType {
LineSegment, Ray, Line;
}
public enum Side {
Left, Right, OnLine;
}
private static final long serialVersionUID = 8018247328242867430L;
public static boolean contains(PrecisionPoint origin,
PrecisionPoint terminus, PrecisionPoint p, double tolerance) {
return origin.getDistance(p) + p.getDistance(terminus) <= origin
.getDistance(terminus)
+ tolerance;
}
/**
* Returns the coefficients of the generalized equation of the line passing
* through points (x1,y1) and (x2,y2) Generalized line equation: ax+by=c =>
* a==result[0], b==result[1], c==result[2]
*
* @param x1 -
* x coordinate of the 1st point
* @param y1 -
* y coordinate of the 1st point
* @param x2 -
* x coordinate of the 2nd point
* @param y2 -
* y coordinate of the 2nd point
* @return the coefficients of the generalized equation of the line passing
* through points (x1,y1) and (x2,y2)
*/
public static double[] getLineEquation(double x1, double y1, double x2,
double y2) {
double equation[] = new double[3];
for (int i = 0; i < 3; i++)
equation[i] = 0;
if (x1 == x2 && y1 == y2)
return equation;
if (x1 == x2) {
equation[0] = 1;
equation[1] = 0;
equation[2] = x1;
return equation;
}
equation[0] = (y1 - y2) / (x2 - x1);
equation[1] = 1.0;
equation[2] = y2 + equation[0] * x2;
return equation;
}
private LineType lineType;
public PrecisionLine(PrecisionPoint origin, PrecisionPoint terminus) {
this(origin, terminus, LineType.LineSegment);
}
public PrecisionLine(PrecisionPoint origin, PrecisionPoint terminus,
LineType lineType) {
super(origin, terminus);
this.lineType = lineType;
}
public PrecisionLine(double x1, double y1, double x2, double y2) {
this(x1, y1, x2, y2, LineType.LineSegment);
}
public PrecisionLine(double x1, double y1, double x2, double y2,
LineType lineType) {
super(x1, y1, x2, y2);
this.lineType = lineType;
}
public boolean contains(PrecisionPoint p, double tolerance) {
boolean b = contains(point1, point2, p, tolerance);
if (lineType == LineType.Line || lineType == LineType.Ray) {
b |= contains(point1, p, point2, tolerance);
if (lineType == LineType.Line)
b |= contains(point2, p, point1, tolerance);
}
return b;
}
public boolean contains(PrecisionPoint p) {
return contains(p, DEFAULT_TOLERANCE);
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof PrecisionLine))
return false;
PrecisionLine ls = (PrecisionLine) obj;
return point1.equals(ls.point1) && point2.equals(ls.point2)
&& lineType == ls.lineType;
}
public PrecisionLine getCopy() {
return new PrecisionLine(point1, point2, lineType);
}
public double[] getEquation() {
return getLineEquation(point1.x, point1.y, point2.x, point2.y);
}
public List<PrecisionPoint> getLinesIntersections(PrecisionLine line) {
List<PrecisionPoint> intersections = new ArrayList<PrecisionPoint>();
double temp[] = getEquation();
double a1 = temp[0];
double b1 = temp[1];
double c1 = temp[2];
temp = line.getEquation();
double a2 = temp[0];
double b2 = temp[1];
double c2 = temp[2];
// Cramer's rule for the system of linear equations
double det = a1 * b2 - b1 * a2;
if (det == 0) {
if (a1 == a2 && b1 == b2 && c1 == c2) {
// if lines are the same, then instead of infinite number of intersections
// we will put the end points of the line segment passed as an argument
intersections.add(line.getOrigin().getCopy());
intersections.add(line.getTerminus().getCopy());
}
} else {
intersections.add(new PrecisionPoint((c1 * b2 - b1 * c2) / det, (a1
* c2 - c1 * a2)
/ det));
}
return intersections;
}
public LineType getLineType() {
return lineType;
}
public PrecisionPoint getOrigin() {
return point1;
}
public PrecisionPoint getTerminus() {
return point2;
}
public PrecisionPoint intersect(PrecisionLine line) {
return intersect(line, DEFAULT_TOLERANCE);
}
/**
* Determines the intersect point between this line and the line passed in
* as a parameter. If they intersect, then true is returned and the point
* reference passed in will be set to the intersect point. If they don't
* intersect, then the method returns <code>false</code>.
*
* @param line
* <code>LineSeg</code> to test the intersection against.
* @param nTolerance
* int tolerance value for detecting the intersection.
* @return <code>Point</code> that represents the intersection with this
* line, or <code>null</code> if the calculation is not possible.
*/
public PrecisionPoint intersect(final PrecisionLine line,
final double nTolerance) {
List<PrecisionPoint> intersections = getLinesIntersections(line);
if (intersections.size() > 1) {
intersections.add(point1.getCopy());
intersections.add(point2.getCopy());
}
for (int i = 0; i < intersections.size(); i++) {
PrecisionPoint result = intersections.get(i).getCopy();
if (contains(result, nTolerance)
&& line.contains(result, nTolerance)) {
return result;
}
}
return null;
}
public double length() {
return point1.getDistance(point2);
}
public void setLineType(LineType lineType) {
this.lineType = lineType;
}
public void setOrigin(PrecisionPoint origin) {
point1.setLocation(origin);
}
public void setTerminus(PrecisionPoint terminus) {
point2.setLocation(terminus);
}
public PrecisionLine swap() {
return (PrecisionLine) super.swap();
}
/**
* Constant to avoid divide by zero errors.
*/
private static final double BIGSLOPE = 999999.99999;
/**
* Calculates the slope of this line segment (y=mx+b)
*
* @return <code>float</code> the slope of this segment. If the slope is
* not defined such as when the line segment is vertical, then the
* constant <code>BIGSLOPE</code> is returned to avoid divide by
* zero errors.
*/
public final double slope() {
if (isVertical())
return BIGSLOPE;
return (point2.y - point1.y) / (point2.x - point1.x);
}
/**
* Determines if this a vertical segment
*
* @return <code>boolean</code> <code>true</code> if vertical,
* <code>false</code> otherwise.
*/
public final boolean isVertical() {
return (point1.x == point2.x);
}
public double getAngle() {
return getTerminus().getAngle(point1);
}
public double getRelativeAngle(PrecisionLine another) {
return getAngle() - another.getAngle();
}
public Side getSide(PrecisionPoint p) {
if (point1.equals(p) || point2.equals(p))
return Side.OnLine;
double v = (p.x - point1.x) * (point2.y - point1.y) - (p.y - point1.y)
* (point2.x - point1.x);
if (Math.abs(v) < DEFAULT_TOLERANCE)
return Side.OnLine;
return v > 0 ? Side.Left : Side.Right;
//
// PrecisionLine line = new PrecisionLine(getOrigin(), p, LineType.Ray);
// double angle = line.getRelativeAngle(this);
// double sin = Math.sin(angle);
// return sin == 0 ? Side.OnLine : (sin > 0 ? Side.Left : Side.Right);
}
public String toString() {
return lineType + "[" + point1 + ", " + point2 + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}