package dwarf.util;
import dwarf.DwarfException;
/**
* A 2-dimensional, single-precision, double-point vector.
*
* @author Matthew 'siD' Van der Bijl
*
* @see java.lang.Object
* @see java.lang.Cloneable
*/
public class Vector2 extends java.lang.Object implements Cloneable {
public static final Vector2 ZERO = new Vector2(0, 0);
public static final Vector2 UNIT_X = new Vector2(1, 0);
public static final Vector2 UNIT_Y = new Vector2(0, 1);
public static final Vector2 UNIT_XY = new Vector2(1, 1);
/**
* A constant holding a Not-a-Number (NaN) value of type
* <code>Vector2</code>.
*/
public final static Vector2 NaN = new Vector2(Double.NaN, Double.NaN);
/**
* A constant holding the positive infinity of type <code>Vector2</code>.
*/
public static final Vector2 POSITIVE_INFINITY = new Vector2(
Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY
);
/**
* A constant holding the negative infinity of type <code>Vector2</code>.
*/
public static final Vector2 NEGATIVE_INFINITY = new Vector2(
Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY
);
/**
* Rotation in degrees (0-359)
*/
private double rotation = 0;
/**
* the x-component of this <code>Vector2</code>
*/
private double x;
/**
* the y-component of this <code>Vector2</code>
*/
private double y;
/**
* Default constructor.
*/
public Vector2() {
this(0, 0, 0);
}
public Vector2(double x, double y) {
super();
this.x = x;
this.y = y;
setRotation(0);
}
public Vector2(float x, float y) {
super();
this.x = x;
this.y = y;
setRotation(0);
}
public Vector2(double x, double y, double rotation) {
super();
this.x = x;
this.y = y;
setRotation(rotation);
}
public Vector2(Vector2 v) {
super();
this.x = v.getX();
this.y = v.getY();
setRotation(v.getRotation());
}
public Vector2(double[] points) {
super();
if (points.length == 2) {
this.x = points[0];
this.y = points[1];
setRotation(0);
} else {
throw new IllegalArgumentException("the double array inputed does not have 2 points");
}
}
public double length() {
return Math.sqrt(this.getX() * this.getX() + this.getY() * this.getY());
}
public double max() {
return Math.max(this.getX(), this.getY());
}
public double dot(Vector2 input) {
return this.getX() * input.getX()
+ this.getY() * input.getX();
}
public Vector2 normalized() {
return new Vector2(
this.getX() / length(),
this.getY() / length());
}
public double cross(Vector2 input) {
return this.getX() * input.getX()
- this.getY() * input.getY();
}
public void add(Vector2 v) {
this.x += v.getX();
this.y += v.getY();
}
public void add(double v) {
this.x += v;
this.y += v;
}
public void sub(Vector2 v) {
this.x -= v.getX();
this.y -= v.getY();
}
public void sub(double v) {
this.x -= v;
this.y -= v;
}
public void mul(Vector2 v) {
this.x *= v.getX();
this.y *= v.getY();
}
public void mul(double v) {
this.x *= v;
this.y *= v;
}
public void div(Vector2 v) {
this.x += v.getX();
this.y += v.getY();
}
public void div(double v) {
this.x /= v;
this.y /= v;
}
public void mod(Vector2 v) {
this.x %= v.getX();
this.y %= v.getY();
}
public void mod(double v) {
this.x %= v;
this.y %= v;
}
public Vector2 abs() {
return new Vector2(
Math.abs(this.getX()),
Math.abs(this.getY()));
}
public double absX() {
return Math.abs(this.getX());
}
public double absY() {
return Math.abs(this.getY());
}
/**
* Returns a string representation of the object.
* <p>
* In general, the toString method returns a string that "textually
* represents" this object. The result should be a concise but informative
* representation that is easy for a person to read. It is recommended that
* all subclasses override this method.</p>
*
* @return a textually representation of this object
*/
@Override
public String toString() {
return "Vector2d[" + getX() + ", " + getY() + "]";
}
public void set(double x, double y, double rotation) {
this.x = x;
this.y = y;
setRotation(rotation);
}
public void set(float x, float y, double rotation) {
this.x = x;
this.y = y;
setRotation(rotation);
}
public void set(Vector2 v) {
this.x = v.getX();
this.y = v.getY();
setRotation(v.getRotation());
}
public void set(double x, double y) {
this.x = x;
this.y = y;
}
public void set(float x, float y) {
this.x = x;
this.y = y;
}
public Vector2 get() {
return this;
}
public double getX() {
return this.x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return this.y;
}
public void setY(double y) {
this.y = y;
}
public void translate(Vector2 delta) {
this.x += delta.getX();
this.y += delta.getY();
}
public void translate(double delta) {
this.x += delta;
this.y += delta;
}
public void translateX(double deltaX) {
this.x += deltaX;
}
public void translateY(double deltaY) {
this.y += deltaY;
}
/**
* Class Object is the root of the class hierarchy. Every class has Object
* as a superclass. All objects, including arrays, implement the methods of
* this class.
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + (int) (Double.doubleToLongBits(getRotation()) ^ (Double.doubleToLongBits(getRotation()) >>> 32));
hash = 89 * hash + (int) (Double.doubleToLongBits(getX()) ^ (Double.doubleToLongBits(getX()) >>> 32));
hash = 89 * hash + (int) (Double.doubleToLongBits(getY()) ^ (Double.doubleToLongBits(getY()) >>> 32));
return hash;
}
/**
* Returns true if the <code>this</code> is equal to the argument and false
* otherwise. Consequently, if both argument are null, true is returned,
* false is returned. Otherwise, equality is determined by using the equals
* method of the first argument.
*
* @param obj the <code>Object</code> to be tested
* @see java.lang.Object#equals(java.lang.Object)
*
* @return true if the argument is equal to <code>this</code> other and
* false otherwise
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (getClass() != obj.getClass()) {
return false;
} else if (!super.equals(obj)) {
return false;
}
final Vector2 other = (Vector2) obj;
if (Double.doubleToLongBits(getRotation()) != Double.doubleToLongBits(other.getRotation())) {
return false;
} else if (Double.doubleToLongBits(getX()) != Double.doubleToLongBits(other.getX())) {
return false;
} else {
return Double.doubleToLongBits(getY()) == Double.doubleToLongBits(other.getY());
}
}
public void flipX() {
this.x = -x;
}
public void flipY() {
this.y = -y;
}
public void flip() {
this.x = -x;
this.y = -y;
}
public double[] toArray() {
return new double[]{
this.getX(), this.getY()
};
}
public double get(int index) {
switch (index) {
case 0:
return this.getX();
case 1:
return this.getY();
}
throw new IllegalArgumentException("index must be either 0, 1");
}
/**
* Shear (a, b): (x, y) → (x+ay, y+bx)
*
* @param input the <code>Vector2</code> to be sheered.
* @return Vector2d(x + input.y, y + input.x)
*/
public Vector2 sheer(Vector2 input) {
return new Vector2(this.getX() + input.getY(), this.getY() + input.getX());
}
/**
* Shear (a, b): (x, y) → (x+ay, y+bx)
*
* @param a the X component of the <code>Vector2</code>.
* @param b the Y component of the <code>Vector2</code>.
* @return Vector2d(x + a, y + b)
*/
public Vector2 sheer(double a, double b) {
return this.sheer(new Vector2(a, b));
}
/**
* Return the current rotation of this vector. Rotation is expressed as a
* degree value, range (0..359). Zero degrees is towards the east
* (right-hand side of the world), and the angle increases clockwise.
*
* @see #setRotation(double)
*
* @return The rotation in degrees.
*/
public double getRotation() {
return rotation;
}
/**
* Set the rotation of this vector. Rotation is expressed as a degree value,
* range (0..359). Zero degrees is to the east (right-hand side of the
* world), and the angle increases clockwise. <b>WARNING: Do NOT modify this
* code.</b>
*
* @param rotation The rotation in degrees.
*/
@SuppressWarnings({"AssignmentToMethodParameter", "AssignmentReplaceableWithOperatorAssignment"})
public final void setRotation(double rotation) {
// First normalize.
if (rotation >= 360) {
// Optimize the usual case: rotation has adjusted to a value greater than
// 360, but is still within the 360 - 720 bound.
if (rotation < 720) {
rotation -= 360;
} else {
rotation = rotation % 360;
}
} else if (rotation < 0) {
// Likwise, if less than 0, it's likely that the rotation was reduced by
// a small amount and so will be >= -360.
if (rotation >= -360) {
rotation += 360;
} else {
rotation = 360 + (rotation % 360);
}
}
if (this.rotation != rotation) {
this.rotation = rotation;
}
}
/**
* Turn this vector to face towards a certain location.
*
* @param xPos The x-coordinate of the cell to turn towards
* @param yPos The y-coordinate of the cell to turn towards
*/
public void turnTowards(double xPos, double yPos) {
double a = Math.atan2(yPos - this.getY(), xPos - this.getX());
setRotation(Math.toDegrees(a));
}
/**
* Move this vector the specified distance in the direction it is currently
* facing.
*
* <p>
* The direction can be set using the {@link #setRotation(double)} method.
*
* @param distance The distance to move (in pixels); a negative value will
* move backwards
*/
public void move(int distance) {
double radians = Math.toRadians(getRotation());
// We round to the nearest integer, to allow moving one unit at an angle
// to actually move.
int deltaX = (int) Math.round(Math.cos(radians) * distance);
int deltaY = (int) Math.round(Math.sin(radians) * distance);
this.set(this.getX() + deltaX, this.getY() + deltaY, this.getRotation());
}
/**
* Move this vector the specified distance in the direction it is currently
* facing.
*
* <p>
* The direction can be set using the {@link #setRotation(double)} method.
*
* @param distance The distance to move (in pixels); a negative value will
* move backwards
*/
public void move(double distance) {
double deltaX = Math.round(Math.cos(Math.toRadians(getRotation())) * distance);
double deltaY = Math.round(Math.sin(Math.toRadians(getRotation())) * distance);
this.set(
this.getX() + deltaX,
this.getY() + deltaY
);
}
/**
* Turn this vector by the specified amount (in degrees).
*
* @param amount the number of degrees to turn; positive values turn
* clockwise
*
* @see #setRotation(double)
*/
public void rotate(double amount) {
setRotation((getRotation() + amount));
}
public void turnTowards(Vector2 input) {
this.turnTowards(input.getX(), input.getY());
}
/**
* Creates a new object of the same class as this object.
*
* @exception OutOfMemoryError if there is not enough memory.
* @throws java.lang.CloneNotSupportedException thrown if the Vector2 can
* not be cloned
* @see java.lang.Cloneable
*
* @return a clone of this instance.
*/
@Override
public Vector2 clone() throws CloneNotSupportedException {
try {
return new Vector2(this);
} catch (Exception ex) {
throw new DwarfException(ex);
}
}
public Point2D toPoint() {
return new Point2D(x, y);
}
}