package com.indyforge.twod.engine.graphics.rendering.scenegraph.math;
import java.awt.Point;
import java.io.Serializable;
import com.indyforge.twod.engine.graphics.rendering.scenegraph.Entity;
/**
* This is a simple 2d vector implementation. It contains the most basic
* features.
*
* @author Christopher Probst
* @see Entity
*/
public final class Vector2f implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
*
* @author Christopher Probst
*
*/
public enum Direction {
North, South, East, West, Undefined;
public Vector2f vector() {
switch (this) {
case North:
return Vector2f.north();
case South:
return Vector2f.south();
case West:
return Vector2f.west();
case East:
return Vector2f.east();
case Undefined:
return Vector2f.zero();
default:
throw new UnsupportedOperationException(this
+ " not implemented");
}
}
}
/**
* Lerps two vectors (Linear interpolation). This means you provide two
* vectors (start, end) and a float between 0 and 1. This method calculates
* a new vector which is between start and end using the given float value.
* 0 = start und 1 = end.
*
* @param start
* The start vector.
* @param end
* The end vector
* @param time
* A float value between 0 and 1.
* @return a vector which is lerped between start and end.
*/
public static Vector2f lerp(Vector2f start, Vector2f end, float time) {
if (start == null) {
throw new NullPointerException("start");
} else if (end == null) {
throw new NullPointerException("end");
}
// Return the lerp vector
return start.add(end.sub(start).scale(MathExt.clamp(time, 0f, 1f)));
}
/**
* Moves the start vector to the end vector but never exceeds the max delta
* value.
*
* @param start
* The start vector.
* @param end
* The end vector
* @param maxDelta
* The start vector never gets closer to the end vector as max
* delta.
* @return the transformed start vector.
*/
public static Vector2f moveTowards(Vector2f start, Vector2f end,
float maxDelta) {
if (start == null) {
throw new NullPointerException("start");
} else if (end == null) {
throw new NullPointerException("end");
}
// Calc distance between the vectors
Vector2f dist = end.sub(start);
// Calc the time to move
float time = maxDelta / dist.length();
// Return a lerped value
return start.add(dist.scale(MathExt.clamp(time, 0f, 1f)));
}
/**
* @return a new vector initialized with 0,0
*/
public static Vector2f zero() {
return new Vector2f();
}
/**
* @return a new vector initialized with 0,1
*/
public static Vector2f south() {
return new Vector2f(0, 1);
}
/**
* @return a new vector initialized with 1,0
*/
public static Vector2f east() {
return new Vector2f(1, 0);
}
/**
* @return a new vector initialized with -1,0
*/
public static Vector2f west() {
return new Vector2f(-1, 0);
}
/**
* @return a new vector initialized with 0,-1
*/
public static Vector2f north() {
return new Vector2f(0, -1);
}
/*
* The x and y components of this vector.
*/
public float x, y;
/**
* Creates a copy of the given point.
*
* @param point
* The point you want to copy.
*/
public Vector2f(Point point) {
this(point.x, point.y);
}
/**
* Creates a copy of the given vector.
*
* @param other
* The vector you want to copy.
*/
public Vector2f(Vector2f other) {
this(other.x, other.y);
}
/**
* Creates a new vector with 0,0.
*/
public Vector2f() {
this(0, 0);
}
/**
* Creates a new vector using the given values.
*
* @param x
* The x component of the new vector.
* @param y
* The y component of the new vector.
*/
public Vector2f(float x, float y) {
this.x = x;
this.y = y;
}
/**
* Sets the components of this vector.
*
* @param x
* The x component of this vector.
* @param y
* The y component of this vector.
* @return this for chaining.
*/
public Vector2f set(float x, float y) {
this.x = x;
this.y = y;
return this;
}
/**
* Sets the components of this vector.
*
* @param other
* The vector which you want to copy.
* @return this for chaining.
*/
public Vector2f set(Vector2f other) {
if (other == null) {
throw new NullPointerException("other");
}
this.x = other.x;
this.y = other.y;
return this;
}
/**
* @return the length of this vector.
*/
public float length() {
return (float) Math.sqrt(x * x + y * y);
}
/**
* Sets this vector to 0,0.
*
* @return this for chaining.
*/
public Vector2f setZero() {
x = y = 0;
return this;
}
/**
* Adds an other vector to this vector.
*
* @param other
* The vector you want to add to this.
* @return this for chaining.
*/
public Vector2f addLocal(Vector2f other) {
if (other == null) {
throw new NullPointerException("other");
}
x += other.x;
y += other.y;
return this;
}
/**
* Subtracts an other from this vector.
*
* @param other
* The vector you want to subtract.
* @return this for chaining.
*/
public Vector2f subLocal(Vector2f other) {
if (other == null) {
throw new NullPointerException("other");
}
x -= other.x;
y -= other.y;
return this;
}
/**
* Swaps both components of this vector.
*
* @return this for chaining.
*/
public Vector2f swapLocal() {
float ptr = x;
x = y;
y = ptr;
return this;
}
/**
* Swaps both components and returns a new vector which contains the result.
*
* @return a new result vector.
*/
public Vector2f swap() {
return new Vector2f(this).swapLocal();
}
/**
* Scales this vector by multiplying it with a scalar.
*
* @param scalar
* The scale factor.
* @return this for chaining.
*/
public Vector2f scaleLocal(float scalar) {
x *= scalar;
y *= scalar;
return this;
}
/**
* Multiplies this vector with a scalar and returns a new vector which
* contains the result.
*
* @param scalar
* The scale factor.
* @return a new result vector.
*/
public Vector2f scale(float scalar) {
return new Vector2f(this).scaleLocal(scalar);
}
/**
* Scales this vector by multiplying each component with each other
* component of the other vector.
*
* @param other
* The scale vector.
* @return this for chaining.
*/
public Vector2f scaleLocal(Vector2f other) {
if (other == null) {
throw new NullPointerException("other");
}
x *= other.x;
y *= other.y;
return this;
}
/**
* Multiplies each component of this vector with each component of the other
* vector and returns a new vector which contains the result.
*
* @param other
* The scale vector.
* @return the new result vector.
*/
public Vector2f scale(Vector2f other) {
return new Vector2f(this).scaleLocal(other);
}
/**
* Adds this vector with an other vector and returns a new vector which
* contains the result.
*
* @param other
* The vector you want to add.
* @return a new result vector.
*/
public Vector2f add(Vector2f other) {
if (other == null) {
throw new NullPointerException("other");
}
return new Vector2f(this).addLocal(other);
}
/**
* Substracts an other vector from this vector and returns a new vector
* which contains the result.
*
* @param other
* The vector you want to subtract.
* @return a new result vector.
*/
public Vector2f sub(Vector2f other) {
if (other == null) {
throw new NullPointerException("other");
}
return new Vector2f(this).subLocal(other);
}
/**
* Inverts this vector.
*
* @return this for chaining.
*/
public Vector2f invertLocal() {
x = 1f / x;
y = 1f / y;
return this;
}
/**
* @return a new vector which contains the inverse of this vector.
*/
public Vector2f invert() {
return new Vector2f(this).invertLocal();
}
/**
* Normalizes this vector.
*
* @return this for chaining.
*/
public Vector2f normalizeLocal() {
float length = length();
x /= length;
y /= length;
return this;
}
/**
* @return a new vector which contains normalized components.
*/
public Vector2f normalize() {
return new Vector2f(this).normalizeLocal();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(x);
result = prime * result + Float.floatToIntBits(y);
return result;
}
/**
* Rounds this entity.
*
* @return this for chaining.
*/
public Vector2f roundLocal() {
x = Math.round(x);
y = Math.round(y);
return this;
}
/**
* @return a new rounded vector.
*/
public Vector2f round() {
return new Vector2f(this).roundLocal();
}
/**
* @return the vector as point. The float components will not be rounded but
* converted to integers.
*/
public Point point() {
return new Point((int) x, (int) y);
}
/**
* @return the nearest direction of this vector.
*/
public Direction nearestDirection() {
if (MathExt.equals(x, y)) {
return Direction.Undefined;
} else if (Math.abs(x) > Math.abs(y)) {
return x > 0.0f ? Direction.East : Direction.West;
} else {
return y > 0.0f ? Direction.South : Direction.North;
}
}
/**
* Check whether or not this vector is inside the given rectangle.
*
* @param leftUp
* The left-up corner.
* @param rightDown
* The right-down corner.
* @return true if this vector is inside the rectangle, otherwise false.
*/
public boolean inside(Vector2f leftUp, Vector2f rightDown) {
if (leftUp == null) {
throw new NullPointerException("leftUp");
} else if (rightDown == null) {
throw new NullPointerException("rightDown");
}
return x >= leftUp.x && x <= rightDown.x && y >= leftUp.y
&& y <= rightDown.y;
}
/**
* @param other
* The vector you want to compare with this vector.
* @param threshold
* The threshold.
* @return true if this vector equals the other vector (using the
* threshold), otherwise false.
*/
public boolean equals(Vector2f other, Vector2f threshold) {
if (other == null) {
throw new NullPointerException("other");
} else if (threshold == null) {
throw new NullPointerException("threshold");
}
return MathExt.equals(x, other.x, threshold.x)
&& MathExt.equals(y, other.y, threshold.y);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Vector2f)) {
return false;
}
Vector2f other = (Vector2f) obj;
if (!MathExt.equals(x, other.x)) {
return false;
}
if (!MathExt.equals(y, other.y)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Vector2f[x = " + x + ", y = " + y + "]";
}
}