/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.gwt.client.spatial;
import org.geomajas.geometry.Coordinate;
/**
* Representation of a LineSegment, mainly for mathematical purposes. Can calculate much needed line to line and line to
* point calculations.
*
* @author Pieter De Graef
*/
public class LineSegment {
/**
* The line-segment's first(begin) coordinate.
*/
private Coordinate c1;
/**
* The line-segment's last(end) coordinate.
*/
private Coordinate c2;
// -------------------------------------------------------------------------
// Constructors:
// -------------------------------------------------------------------------
public LineSegment() {
c1 = null;
c2 = null;
}
public LineSegment(Coordinate c1, Coordinate c2) {
this.c1 = c1;
this.c2 = c2;
}
// -------------------------------------------------------------------------
// Class specific functions:
// -------------------------------------------------------------------------
/**
* Return the length of the linesegment.
*/
public double getLength() {
double deltaX = this.c2.getX() - this.c1.getX();
double deltaY = this.c2.getY() - this.c1.getY();
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
}
/**
* Return the middle point of this linesegment.
*/
public Coordinate getMiddlePoint() {
double x = this.x1() + 0.5 * (this.x2() - this.x1());
double y = this.y1() + 0.5 * (this.y2() - this.y1());
return new Coordinate(x, y);
}
/**
* Return the distance from a point to this linesegment. If the point is not perpendicular to the linesegment, the
* closest endpoint will be returned.
*
* @param c
* The {@link Coordinate} to check distance from.
*/
public double distance(Coordinate c) {
Coordinate nearest = this.nearest(c);
LineSegment ls = new LineSegment(c, nearest);
return ls.getLength();
}
/**
* Does this linesegment intersect with another or not?
*
* @param lineSegment
* The other linesegment.
* @return Returns true if they intersect in 1 point, false otherwise.
*/
public boolean intersects(LineSegment lineSegment) {
double x1 = this.x1();
double y1 = this.y1();
double x2 = this.x2();
double y2 = this.y2();
double x3 = lineSegment.x1();
double y3 = lineSegment.y1();
double x4 = lineSegment.x2();
double y4 = lineSegment.y2();
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (denom == 0) {
return false;
}
double u1 = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
double u2 = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
return (u1 > 0 && u1 < 1 && u2 > 0 && u2 < 1);
}
/**
* Return the intersection point of 2 lines. Yes you are reading this correctly! For this function the LineSegments
* are treated as lines. This means that the intersection point does not necessarily lie on the LineSegment (in that
* case, the {@link #intersects} function would return false).
*
* @param lineSegment
* The other LineSegment.
* @return A {@link Coordinate} representing the intersection point.
*/
public Coordinate getIntersection(LineSegment lineSegment) { // may not be on either one of the line segments.
// http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
double x1 = this.x1();
double y1 = this.y1();
double x2 = this.x2();
double y2 = this.y2();
double x3 = lineSegment.x1();
double y3 = lineSegment.y1();
double x4 = lineSegment.x2();
double y4 = lineSegment.y2();
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
double u1 = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
double x = x1 + u1 * (x2 - x1);
double y = y1 + u1 * (y2 - y1);
return new Coordinate(x, y);
}
/**
* Return the intersection point of 2 line segments if they intersect in 1 point.
*
* @param lineSegment
* The other LineSegment.
* @return A {@link Coordinate} representing the intersection point (so on both line segments), null if segments are
* not intersecting or corresponding lines are coinciding.
*/
public Coordinate getIntersectionSegments(LineSegment lineSegment) {
// http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
double x1 = this.x1();
double y1 = this.y1();
double x2 = this.x2();
double y2 = this.y2();
double x3 = lineSegment.x1();
double y3 = lineSegment.y1();
double x4 = lineSegment.x2();
double y4 = lineSegment.y2();
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (denom == 0) {
return null;
}
double u1 = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
if (u1 <= 0 || u1 >= 1) {
return null;
}
double u2 = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
if (u2 <= 0 || u2 >= 1) {
return null;
}
double x = x1 + u1 * (x2 - x1);
double y = y1 + u1 * (y2 - y1);
return new Coordinate(x, y);
}
/**
* Calculate which point on the LineSegment is nearest to the given coordinate. Will be perpendicular or one of the
* end-points.
*
* @param c
* The coordinate to check.
* @return The point on the LineSegment nearest to the given coordinate.
*/
public Coordinate nearest(Coordinate c) {
double len = this.getLength();
double u = (c.getX() - this.c1.getX()) * (this.c2.getX() - this.c1.getX()) + (c.getY() - this.c1.getY())
* (this.c2.getY() - this.c1.getY());
u = u / (len * len);
if (u < 0.00001 || u > 1) {
// Shortest point not within LineSegment, so take closest end-point.
LineSegment ls1 = new LineSegment(c, this.c1);
LineSegment ls2 = new LineSegment(c, this.c2);
double len1 = ls1.getLength();
double len2 = ls2.getLength();
if (len1 < len2) {
return this.c1;
}
return this.c2;
} else {
// Intersecting point is on the line, use the formula: P = P1 + u (P2 - P1)
double x1 = this.c1.getX() + u * (this.c2.getX() - this.c1.getX());
double y1 = this.c1.getY() + u * (this.c2.getY() - this.c1.getY());
return new Coordinate(x1, y1);
}
}
//-------------------------------------------------------------------------
// Getters
//-------------------------------------------------------------------------
public double x1() {
return this.c1.getX();
}
public double y1() {
return this.c1.getY();
}
public double x2() {
return this.c2.getX();
}
public double y2() {
return this.c2.getY();
}
public Coordinate getC1() {
return this.c1;
}
public Coordinate getC2() {
return this.c2;
}
}