package org.newdawn.slick.geom;
/**
* Implemenation of a bunch of maths functions to do with lines. Note
* that lines can't be used as dynamic shapes right now - also collision
* with the end of a line is undefined.
*
* @author Kevin Glass
*/
public class Line extends Shape {
/** The start point of the line */
private Vector2f start;
/** The end point of the line */
private Vector2f end;
/** The vector between the two points */
private Vector2f vec;
/** The length of the line squared */
private float lenSquared;
/** Temporary storage - declared globally to reduce GC */
private Vector2f loc = new Vector2f(0,0);
/** Temporary storage - declared globally to reduce GC */
private Vector2f v = new Vector2f(0,0);
/** Temporary storage - declared globally to reduce GC */
private Vector2f v2 = new Vector2f(0,0);
/** Temporary storage - declared globally to reduce GC */
private Vector2f proj = new Vector2f(0,0);
/** Temporary storage - declared globally to reduce GC */
private Vector2f closest = new Vector2f(0,0);
/** Temporary storage - declared globally to reduce GC */
private Vector2f other = new Vector2f(0,0);
/** True if this line blocks on the outer edge */
private boolean outerEdge = true;
/** True if this line blocks on the inner edge */
private boolean innerEdge = true;
/**
* Create a new line based on the origin and a single point
*
* @param x The end point of the line
* @param y The end point of the line
* @param inner True if this line blocks on it's inner edge
* @param outer True if this line blocks on it's outer edge
*/
public Line(float x, float y, boolean inner, boolean outer) {
this(0,0,x,y);
}
/**
* Create a new line based on the origin and a single point
*
* @param x The end point of the line
* @param y The end point of the line
*/
public Line(float x, float y) {
this(x,y,true,true);
}
/**
* Create a new line based on two points
*
* @param x1 The x coordinate of the start point
* @param y1 The y coordinate of the start point
* @param x2 The x coordinate of the end point
* @param y2 The y coordinate of the end point
*/
public Line(float x1, float y1, float x2, float y2) {
this(new Vector2f(x1,y1), new Vector2f(x2,y2));
}
/**
* Create a line with relative second point
*
* @param x1 The x coordinate of the start point
* @param y1 The y coordinate of the start point
* @param dx The x change to get to the second point
* @param dy The y change to get to the second point
* @param dummy A dummy value
*/
public Line(float x1, float y1, float dx, float dy, boolean dummy) {
this(new Vector2f(x1,y1), new Vector2f(x1+dx,y1+dy));
}
/**
* Create a new line based on two points
*
* @param start The start point
* @param end The end point
*/
public Line(Vector2f start, Vector2f end) {
super();
set(start,end);
}
/**
* Get the start point of the line
*
* @return The start point of the line
*/
public Vector2f getStart() {
return start;
}
/**
* Get the end point of the line
*
* @return The end point of the line
*/
public Vector2f getEnd() {
return end;
}
/**
* Find the length of the line
*
* @return The the length of the line
*/
public float length() {
return vec.length();
}
/**
* Find the length of the line squared (cheaper and good for comparisons)
*
* @return The length of the line squared
*/
public float lengthSquared() {
return vec.lengthSquared();
}
/**
* Configure the line
*
* @param start The start point of the line
* @param end The end point of the line
*/
public void set(Vector2f start, Vector2f end) {
super.pointsDirty = true;
if (this.start == null) {
this.start = new Vector2f();
}
this.start.set(start);
if (this.end == null) {
this.end = new Vector2f();
}
this.end.set(end);;
vec = new Vector2f(end);
vec.sub(start);
lenSquared = vec.lengthSquared();
}
/**
* Configure the line without garbage
*
* @param sx The x coordinate of the start
* @param sy The y coordinate of the start
* @param ex The x coordiante of the end
* @param ey The y coordinate of the end
*/
public void set(float sx, float sy, float ex, float ey) {
super.pointsDirty = true;
start.set(sx, sy);
end.set(ex,ey);
float dx = (ex-sx);
float dy = (ey-sy);
lenSquared = (dx*dx)+(dy*dy);
}
/**
* Get the x direction of this line
*
* @return The x direction of this line
*/
public float getDX() {
return end.getX() - start.getX();
}
/**
* Get the y direction of this line
*
* @return The y direction of this line
*/
public float getDY() {
return end.getY() - start.getY();
}
/**
* @see org.newdawn.slick.geom.Shape#getX()
*/
public float getX() {
return getX1();
}
/**
* @see org.newdawn.slick.geom.Shape#getY()
*/
public float getY() {
return getY1();
}
/**
* Get the x coordinate of the start point
*
* @return The x coordinate of the start point
*/
public float getX1() {
return start.getX();
}
/**
* Get the y coordinate of the start point
*
* @return The y coordinate of the start point
*/
public float getY1() {
return start.getY();
}
/**
* Get the x coordinate of the end point
*
* @return The x coordinate of the end point
*/
public float getX2() {
return end.getX();
}
/**
* Get the y coordinate of the end point
*
* @return The y coordinate of the end point
*/
public float getY2() {
return end.getY();
}
/**
* Get the shortest distance from a point to this line
*
* @param point The point from which we want the distance
* @return The distance from the line to the point
*/
public float distance(Vector2f point) {
return (float) Math.sqrt(distanceSquared(point));
}
/**
* Check if the given point is on the line
*
* @param point The point to check
* @return True if the point is on this line
*/
public boolean on(Vector2f point) {
getClosestPoint(point, closest);
return point.equals(closest);
}
/**
* Get the shortest distance squared from a point to this line
*
* @param point The point from which we want the distance
* @return The distance squared from the line to the point
*/
public float distanceSquared(Vector2f point) {
getClosestPoint(point, closest);
closest.sub(point);
float result = closest.lengthSquared();
return result;
}
/**
* Get the closest point on the line to a given point
*
* @param point The point which we want to project
* @param result The point on the line closest to the given point
*/
public void getClosestPoint(Vector2f point, Vector2f result) {
loc.set(point);
loc.sub(start);
float projDistance = vec.dot(loc);
projDistance /= vec.lengthSquared();
if (projDistance < 0) {
result.set(start);
return;
}
if (projDistance > 1) {
result.set(end);
return;
}
result.x = start.getX() + projDistance * vec.getX();
result.y = start.getY() + projDistance * vec.getY();
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "[Line "+start+","+end+"]";
}
/**
* Intersect this line with another
*
* @param other The other line we should intersect with
* @return The intersection point or null if the lines are parallel
*/
public Vector2f intersect(Line other) {
return intersect(other, false);
}
/**
* Intersect this line with another
*
* @param other The other line we should intersect with
* @param limit True if the collision is limited to the extent of the lines
* @return The intersection point or null if the lines don't intersect
*/
public Vector2f intersect(Line other, boolean limit) {
Vector2f temp = new Vector2f();
if (!intersect(other, limit, temp)) {
return null;
}
return temp;
}
/**
* Intersect this line with another
*
* @param other The other line we should intersect with
* @param limit True if the collision is limited to the extent of the lines
* @param result The resulting intersection point if any
* @return True if the lines intersect
*/
public boolean intersect(Line other, boolean limit, Vector2f result) {
float dx1 = end.getX() - start.getX();
float dx2 = other.end.getX() - other.start.getX();
float dy1 = end.getY() - start.getY();
float dy2 = other.end.getY() - other.start.getY();
float denom = (dy2 * dx1) - (dx2 * dy1);
if (denom == 0) {
return false;
}
float ua = (dx2 * (start.getY() - other.start.getY())) - (dy2 * (start.getX() - other.start.getX()));
ua /= denom;
float ub = (dx1 * (start.getY() - other.start.getY())) - (dy1 * (start.getX() - other.start.getX()));
ub /= denom;
if ((limit) && ((ua < 0) || (ua > 1) || (ub < 0) || (ub > 1))) {
return false;
}
float u = ua;
float ix = start.getX() + (u * (end.getX() - start.getX()));
float iy = start.getY() + (u * (end.getY() - start.getY()));
result.set(ix,iy);
return true;
}
/**
* @see org.newdawn.slick.geom.Shape#createPoints()
*/
protected void createPoints() {
points = new float[4];
points[0] = getX1();
points[1] = getY1();
points[2] = getX2();
points[3] = getY2();
}
/**
* @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
*/
public Shape transform(Transform transform) {
float[] temp = new float[4];
createPoints();
transform.transform(points, 0, temp, 0, 2);
return new Line(temp[0],temp[1],temp[2],temp[3]);
}
/**
* @see org.newdawn.slick.geom.Shape#closed()
*/
public boolean closed() {
return false;
}
}