/**
* Copyright 2008 - 2012
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.3.3
*/
package loon.geom;
import java.io.Serializable;
import loon.action.map.Field2D;
import loon.utils.Array;
import loon.utils.MathUtils;
import loon.utils.NumberUtils;
public class Vector2f implements Serializable, XY {
/**
*
*/
private static final long serialVersionUID = -1844534518528011982L;
private static final Array<Vector2f> _vec2_cache = new Array<Vector2f>();
public final static Vector2f STATIC_ZERO = new Vector2f();
public final static Vector2f TMP() {
Vector2f temp = _vec2_cache.pop();
if (temp == null) {
_vec2_cache.add(temp = new Vector2f(0, 0));
}
return temp;
}
public final static Vector2f ZERO() {
return new Vector2f(0, 0);
}
public final static Vector2f AXIS_X() {
return new Vector2f(1, 0);
}
public final static Vector2f AXIS_Y() {
return new Vector2f(0, 1);
}
public final static Vector2f at(float x, float y) {
return new Vector2f(x, y);
}
public float x;
public float y;
public Vector2f() {
this(0, 0);
}
public Vector2f(float x, float y) {
this.x = x;
this.y = y;
}
public Vector2f(Vector2f v) {
set(v);
}
public Vector2f cpy() {
return new Vector2f(this);
}
public float len() {
return MathUtils.sqrt(x * x + y * y);
}
public float len2() {
return x * x + y * y;
}
public Vector2f nor() {
float len = len();
if (len != 0) {
x /= len;
y /= len;
}
return this;
}
public Vector2f nor(float n) {
return new Vector2f(x == 0 ? 0 : x / n, y == 0 ? 0 : y / n);
}
public final Vector2f mul(float s) {
return new Vector2f(x * s, y * s);
}
public final Vector2f mul(float sx, float sy) {
return new Vector2f(x * sx, y * sy);
}
public Vector2f mulSelf(Matrix3 mat) {
float x = this.x * mat.val[0] + this.y * mat.val[3] + mat.val[6];
float y = this.x * mat.val[1] + this.y * mat.val[4] + mat.val[7];
this.x = x;
this.y = y;
return this;
}
public Vector2f sub(float x, float y) {
return new Vector2f(this.x - x, this.y - y);
}
public Vector2f div() {
return new Vector2f(x / 2, y / 2);
}
public Vector2f div(float v) {
return new Vector2f(x / v, y / v);
}
public Vector2f div(Vector2f v) {
return new Vector2f(x / v.x, y / v.y);
}
public Vector2f add(float x, float y) {
return new Vector2f(this.x + x, this.y + y);
}
public Vector2f addSelfX(float x) {
this.x = this.x + x;
return this;
}
public Vector2f addSelfY(float y) {
this.y = this.y + y;
return this;
}
public float dot(Vector2f v) {
return x * v.x + y * v.y;
}
public float dst(Vector2f v) {
final float x_d = v.x - x;
final float y_d = v.y - y;
return MathUtils.sqrt(x_d * x_d + y_d * y_d);
}
public float dst(float x, float y) {
final float x_d = x - this.x;
final float y_d = y - this.y;
return MathUtils.sqrt(x_d * x_d + y_d * y_d);
}
public float dst2(Vector2f v) {
final float x_d = v.x - x;
final float y_d = v.y - y;
return x_d * x_d + y_d * y_d;
}
public float dst2(float x, float y) {
final float x_d = x - this.x;
final float y_d = y - this.y;
return x_d * x_d + y_d * y_d;
}
public Vector2f tmp() {
return TMP().set(this);
}
public float crs(Vector2f v) {
return this.x * v.y - this.y * v.x;
}
public float crs(float x, float y) {
return this.x * y - this.y * x;
}
public float angle() {
float angle = MathUtils.atan2(y, x) * MathUtils.RAD_TO_DEG;
if (angle < 0) {
angle += 360;
}
return angle;
}
public Vector2f newRotate(float angle) {
float rad = angle * MathUtils.DEG_TO_RAD;
float cos = MathUtils.cos(rad);
float sin = MathUtils.sin(rad);
float newX = this.x * cos - this.y * sin;
float newY = this.x * sin + this.y * cos;
return new Vector2f(newX, newY);
}
public Vector2f rotate(float angle) {
if (angle != 0) {
float rad = angle * MathUtils.DEG_TO_RAD;
float cos = MathUtils.cos(rad);
float sin = MathUtils.sin(rad);
float newX = this.x * cos - this.y * sin;
float newY = this.x * sin + this.y * cos;
this.x = newX;
this.y = newY;
}
return this;
}
public Vector2f lerp(Vector2f target, float alpha) {
Vector2f r = this.mul(1.0f - alpha);
r.add(target.tmp().mul(alpha));
return r;
}
public Vector2f(float value) {
this(value, value);
}
public Vector2f(float[] coords) {
x = coords[0];
y = coords[1];
}
public void move_45D_up() {
move_45D_up(1);
}
public void move_45D_up(int multiples) {
move_multiples(Field2D.UP, multiples);
}
public void move_45D_left() {
move_45D_left(1);
}
public void move_45D_left(int multiples) {
move_multiples(Field2D.LEFT, multiples);
}
public void move_45D_right() {
move_45D_right(1);
}
public void move_45D_right(int multiples) {
move_multiples(Field2D.RIGHT, multiples);
}
public void move_45D_down() {
move_45D_down(1);
}
public void move_45D_down(int multiples) {
move_multiples(Field2D.DOWN, multiples);
}
public void move_up() {
move_up(1);
}
public void move_up(int multiples) {
move_multiples(Field2D.TUP, multiples);
}
public void move_left() {
move_left(1);
}
public void move_left(int multiples) {
move_multiples(Field2D.TLEFT, multiples);
}
public void move_right() {
move_right(1);
}
public void move_right(int multiples) {
move_multiples(Field2D.TRIGHT, multiples);
}
public void move_down() {
move_down(1);
}
public void move_down(int multiples) {
move_multiples(Field2D.TDOWN, multiples);
}
public void move(Vector2f vector2D) {
this.x += vector2D.x;
this.y += vector2D.y;
}
public void move_multiples(int direction, int multiples) {
if (multiples <= 0) {
multiples = 1;
}
Vector2f v = Field2D.getDirection(direction);
move(v.x() * multiples, v.y() * multiples);
}
public void moveX(int x) {
this.x += x;
}
public void moveY(int y) {
this.y += y;
}
public void moveByAngle(int degAngle, float distance) {
if (distance == 0) {
return;
}
float Angle = MathUtils.toRadians(degAngle);
float dX = (MathUtils.cos(Angle) * distance);
float dY = (-MathUtils.sin(Angle) * distance);
int idX = MathUtils.round(dX);
int idY = MathUtils.round(dY);
move(idX, idY);
}
public void move(float x, float y) {
this.x += x;
this.y += y;
}
public void move(float distance) {
float angle = MathUtils.toRadians(getAngle());
int x = MathUtils.round(getX() + MathUtils.cos(angle) * distance);
int y = MathUtils.round(getY() + MathUtils.sin(angle) * distance);
setLocation(x, y);
}
public boolean nearlyCompare(Vector2f v, int range) {
int dX = MathUtils.abs(x() - v.x());
int dY = MathUtils.abs(y() - v.y());
return (dX <= range) && (dY <= range);
}
public int angle(Vector2f v) {
int dx = v.x() - x();
int dy = v.y() - y();
int adx = MathUtils.abs(dx);
int ady = MathUtils.abs(dy);
if ((dy == 0) && (dx == 0)) {
return 0;
}
if ((dy == 0) && (dx > 0)) {
return 0;
}
if ((dy == 0) && (dx < 0)) {
return 180;
}
if ((dy > 0) && (dx == 0)) {
return 90;
}
if ((dy < 0) && (dx == 0)) {
return 270;
}
float rwinkel = MathUtils.atan(ady / adx);
float dwinkel = 0.0f;
if ((dx > 0) && (dy > 0)) {
dwinkel = MathUtils.toDegrees(rwinkel);
} else if ((dx < 0) && (dy > 0)) {
dwinkel = (180.0f - MathUtils.toDegrees(rwinkel));
} else if ((dx > 0) && (dy < 0)) {
dwinkel = (360.0f - MathUtils.toDegrees(rwinkel));
} else if ((dx < 0) && (dy < 0)) {
dwinkel = (180.0f + MathUtils.toDegrees(rwinkel));
}
int iwinkel = (int) dwinkel;
if (iwinkel == 360) {
iwinkel = 0;
}
return iwinkel;
}
public float getAngle() {
float theta = MathUtils.toDegrees(MathUtils.atan2(y, x));
if ((theta < -360) || (theta > 360)) {
theta = theta % 360;
}
if (theta < 0) {
theta = 360 + theta;
}
return theta;
}
public float[] getCoords() {
return (new float[] { x, y });
}
public void setLocation(float x, float y) {
this.x = x;
this.y = y;
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public int x() {
return (int) x;
}
public int y() {
return (int) y;
}
public Vector2f reverse() {
x = -x;
y = -y;
return this;
}
public static Vector2f sum(Vector2f a, Vector2f b) {
Vector2f answer = new Vector2f(a);
return answer.add(b);
}
public static Vector2f mult(Vector2f vector, float scalar) {
Vector2f answer = new Vector2f(vector);
return answer.mul(scalar);
}
public float cross(final Vector2f v) {
return this.x * v.y - v.x * this.y;
}
public float lenManhattan() {
return MathUtils.abs(this.x) + MathUtils.abs(this.y);
}
public static Vector2f cpy(Vector2f vectorA) {
Vector2f newSVector2 = new Vector2f();
newSVector2.x = vectorA.x;
newSVector2.y = vectorA.y;
return newSVector2;
}
public static float len(Vector2f vectorA) {
return MathUtils.sqrt(vectorA.x * vectorA.x + vectorA.y * vectorA.y);
}
public static float len2(Vector2f vectorA) {
return vectorA.x * vectorA.x + vectorA.y * vectorA.y;
}
public static Vector2f set(Vector2f vectorA, Vector2f vectorB) {
vectorA.x = vectorB.x;
vectorA.y = vectorB.y;
return vectorA;
}
public static Vector2f set(Vector2f vectorA, float x, float y) {
vectorA.x = x;
vectorA.y = y;
return vectorA;
}
public Vector2f subNew(Vector2f vectorB) {
return subNew(this, vectorB);
}
public static Vector2f subNew(Vector2f vectorA, Vector2f vectorB) {
return at(vectorA.x - vectorB.x, vectorA.y - vectorB.y);
}
public static Vector2f sub(Vector2f vectorA, Vector2f vectorB) {
vectorA.x -= vectorB.x;
vectorA.y -= vectorB.y;
return vectorA;
}
public static Vector2f nor(Vector2f vectorA) {
float len = len(vectorA);
if (len != 0) {
vectorA.x /= len;
vectorA.y /= len;
}
return vectorA;
}
public static Vector2f addNew(Vector2f vectorA, Vector2f vectorB) {
return at(vectorA.x + vectorB.x, vectorA.y + vectorB.y);
}
public static Vector2f add(Vector2f vectorA, Vector2f vectorB) {
vectorA.x += vectorB.x;
vectorA.y += vectorB.y;
return vectorA;
}
public static Vector2f add(Vector2f vectorA, float x, float y) {
vectorA.x += x;
vectorA.y += y;
return vectorA;
}
public Vector2f mul(Vector2f vectorA) {
return new Vector2f(x * vectorA.x, y * vectorA.y);
}
public static Vector2f mul(Vector2f vectorA, float scalar) {
vectorA.x *= scalar;
vectorA.y *= scalar;
return vectorA;
}
public static float dst(Vector2f vectorA, Vector2f vectorB) {
final float x_d = vectorB.x - vectorA.x;
final float y_d = vectorB.y - vectorA.y;
return MathUtils.sqrt(x_d * x_d + y_d * y_d);
}
public static float dst(Vector2f vectorA, float x, float y) {
final float x_d = x - vectorA.x;
final float y_d = y - vectorA.y;
return MathUtils.sqrt(x_d * x_d + y_d * y_d);
}
public static float dst2(Vector2f vectorA, Vector2f vectorB) {
final float x_d = vectorB.x - vectorA.x;
final float y_d = vectorB.y - vectorA.y;
return x_d * x_d + y_d * y_d;
}
public static Vector2f sub(Vector2f vectorA, float x, float y) {
vectorA.x -= x;
vectorA.y -= y;
return vectorA;
}
public static float crs(Vector2f vectorA, Vector2f vectorB) {
return vectorA.x * vectorB.y - vectorA.y * vectorB.x;
}
public static float crs(Vector2f vectorA, float x, float y) {
return vectorA.x * y - vectorA.y * x;
}
public static float angleTo(Vector2f vectorA) {
float angle = MathUtils.atan2(vectorA.y, vectorA.x) * MathUtils.RAD_TO_DEG;
if (angle < 0) {
angle += 360;
}
return angle;
}
public static Vector2f rotate(Vector2f vectorA, float angle) {
float rad = angle * MathUtils.DEG_TO_RAD;
float cos = MathUtils.cos(rad);
float sin = MathUtils.sin(rad);
float newX = vectorA.x * cos - vectorA.y * sin;
float newY = vectorA.x * sin + vectorA.y * cos;
vectorA.x = newX;
vectorA.y = newY;
return vectorA;
}
public static Vector2f lerp(Vector2f vectorA, Vector2f target, float alpha) {
Vector2f r = mul(vectorA, 1.0f - alpha);
add(r, mul(cpy(target), alpha));
return r;
}
public static float dst2(float x1, float y1, float x2, float y2) {
final float x_d = x2 - x1;
final float y_d = y2 - y1;
return x_d * x_d + y_d * y_d;
}
public final void setZero() {
x = 0.0f;
y = 0.0f;
}
public final Vector2f set(float v) {
return set(v, v);
}
public final Vector2f set(float x, float y) {
this.x = x;
this.y = y;
return this;
}
public final Vector2f set(XY v) {
this.x = v.getX();
this.y = v.getY();
return this;
}
public final Vector2f set(Vector2f v) {
this.x = v.x;
this.y = v.y;
return this;
}
public final Vector2f add(float v) {
return new Vector2f(x + v, y + v);
}
public final Vector2f add(Vector2f v) {
return new Vector2f(x + v.x, y + v.y);
}
public final Vector2f sub(Vector2f v) {
return new Vector2f(x - v.x, y - v.y);
}
public final Vector2f negate() {
return new Vector2f(-x, -y);
}
public final Vector2f negateLocal() {
x = -x;
y = -y;
return this;
}
public final Vector2f subLocal(Vector2f v) {
x -= v.x;
y -= v.y;
return this;
}
public final Vector2f mulLocal(float a) {
x *= a;
y *= a;
return this;
}
public final float length() {
return MathUtils.sqrt(x * x + y * y);
}
public final float lengthSquared() {
return (x * x + y * y);
}
public final float normalize() {
float length = length();
if (length < MathUtils.EPSILON) {
return 0f;
}
float invLength = 1.0f / length;
x *= invLength;
y *= invLength;
return length;
}
public final boolean isValid() {
return x != Float.NaN && x != Float.NEGATIVE_INFINITY && x != Float.POSITIVE_INFINITY && y != Float.NaN
&& y != Float.NEGATIVE_INFINITY && y != Float.POSITIVE_INFINITY;
}
public final Vector2f abs() {
return new Vector2f(MathUtils.abs(x), MathUtils.abs(y));
}
public final void absLocal() {
x = MathUtils.abs(x);
y = MathUtils.abs(y);
}
public final String toString() {
return "(" + x + "," + y + ")";
}
public final static Vector2f abs(Vector2f a) {
return new Vector2f(MathUtils.abs(a.x), MathUtils.abs(a.y));
}
public final static void absToOut(Vector2f a, Vector2f out) {
out.x = MathUtils.abs(a.x);
out.y = MathUtils.abs(a.y);
}
public final static float dot(Vector2f a, Vector2f b) {
return a.x * b.x + a.y * b.y;
}
public final static float cross(Vector2f a, Vector2f b) {
return a.x * b.y - a.y * b.x;
}
public final static Vector2f cross(Vector2f a, float s) {
return new Vector2f(s * a.y, -s * a.x);
}
public final static void crossToOut(Vector2f a, float s, Vector2f out) {
float tempy = -s * a.x;
out.x = s * a.y;
out.y = tempy;
}
public final static Vector2f cross(float s, Vector2f a) {
return new Vector2f(-s * a.y, s * a.x);
}
public final static void crossToOut(float s, Vector2f a, Vector2f out) {
float tempY = s * a.x;
out.x = -s * a.y;
out.y = tempY;
}
public final static void negateToOut(Vector2f a, Vector2f out) {
out.x = -a.x;
out.y = -a.y;
}
public final static Vector2f min(Vector2f a, Vector2f b) {
return new Vector2f(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y);
}
public final static Vector2f max(Vector2f a, Vector2f b) {
return new Vector2f(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y);
}
public final static void minToOut(Vector2f a, Vector2f b, Vector2f out) {
out.x = a.x < b.x ? a.x : b.x;
out.y = a.y < b.y ? a.y : b.y;
}
public final static void maxToOut(Vector2f a, Vector2f b, Vector2f out) {
out.x = a.x > b.x ? a.x : b.x;
out.y = a.y > b.y ? a.y : b.y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + NumberUtils.floatToIntBits(x);
result = prime * result + NumberUtils.floatToIntBits(y);
return result;
}
public boolean equals(float x, float y) {
return this.x == x && this.y == y;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vector2f other = (Vector2f) obj;
if (NumberUtils.floatToIntBits(x) != NumberUtils.floatToIntBits(other.x))
return false;
if (NumberUtils.floatToIntBits(y) != NumberUtils.floatToIntBits(other.y))
return false;
return true;
}
public boolean epsilonEquals(Vector2f other, float epsilon) {
if (other == null)
return false;
if (MathUtils.abs(other.x - x) > epsilon)
return false;
if (MathUtils.abs(other.y - y) > epsilon)
return false;
return true;
}
public boolean epsilonEquals(float x, float y, float epsilon) {
if (MathUtils.abs(x - this.x) > epsilon)
return false;
if (MathUtils.abs(y - this.y) > epsilon)
return false;
return true;
}
public boolean isUnit() {
return isUnit(0.000000001f);
}
public boolean isUnit(final float margin) {
return MathUtils.abs(len2() - 1f) < margin;
}
public boolean isZero() {
return x == 0 && y == 0;
}
public boolean isZero(final float margin) {
return len2() < margin;
}
public boolean isOnLine(Vector2f other) {
return MathUtils.isZero(x * other.y - y * other.x);
}
public boolean isOnLine(Vector2f other, float epsilon) {
return MathUtils.isZero(x * other.y - y * other.x, epsilon);
}
public boolean isCollinear(Vector2f other, float epsilon) {
return isOnLine(other, epsilon) && dot(other) > 0f;
}
public boolean isCollinear(Vector2f other) {
return isOnLine(other) && dot(other) > 0f;
}
public boolean isCollinearOpposite(Vector2f other, float epsilon) {
return isOnLine(other, epsilon) && dot(other) < 0f;
}
public boolean isCollinearOpposite(Vector2f other) {
return isOnLine(other) && dot(other) < 0f;
}
public boolean isPerpendicular(Vector2f vector) {
return MathUtils.isZero(dot(vector));
}
public boolean isPerpendicular(Vector2f vector, float epsilon) {
return MathUtils.isZero(dot(vector), epsilon);
}
public boolean hasSameDirection(Vector2f vector) {
return dot(vector) > 0;
}
public boolean hasOppositeDirection(Vector2f vector) {
return dot(vector) < 0;
}
public static String pointToString(float x, float y) {
return MathUtils.toString(x) + MathUtils.toString(y);
}
public final Vector2f addSelf(Vector2f v) {
this.x += v.x;
this.y += v.y;
return this;
}
public final Vector2f addSelf(float x, float y) {
this.x += x;
this.y += y;
return this;
}
public Vector2f subtract(float x, float y) {
return add(-x, -y);
}
public Vector2f normalizeSelf() {
float l = length();
if (l == 0 || l == 1)
return this;
return set(x / l, y / l);
}
public Vector2f negateSelf() {
return set(-x, -y);
}
public float dot(float x, float y) {
return this.x * x + this.y * y;
}
public float distance(Vector2f v) {
return MathUtils.sqrt(distanceSquared(v));
}
public float distanceSquared(Vector2f v) {
return (v.x - x) * (v.x - x) + (v.y - y) * (v.y - y);
}
public Vector2f lerpSelf(Vector2f target, float alpha) {
final float oneMinusAlpha = 1f - alpha;
float x = (this.x * oneMinusAlpha) + (target.x * alpha);
float y = (this.y * oneMinusAlpha) + (target.y * alpha);
return set(x, y);
}
public Vector2f perpendicular() {
return new Vector2f(y, -x);
}
public Vector2f perpendicularSelf() {
return set(y, x);
}
public Vector2f projectSelf(Vector2f v) {
return scaleSelf(dot(v) / v.lengthSquared());
}
public Vector2f scaleSelf(float s) {
return scaleSelf(s, s);
}
public Vector2f scaleSelf(float sx, float sy) {
return set(x * sx, y * sy);
}
public Vector2f reflect(Vector2f axis) {
return project(axis).scale(2).subtract(this);
}
public Vector2f subtract(Vector2f v) {
return add(-v.x, -v.y);
}
public Vector2f scale(float s) {
return scale(s, s);
}
public Vector2f project(Vector2f v) {
return scale(dot(v) / v.lengthSquared());
}
public Vector2f scale(float sx, float sy) {
return new Vector2f(x * sx, y * sy);
}
public Vector2f reflectSelf(Vector2f axis) {
return set(project(axis).scaleSelf(2).subtractSelf(this));
}
public Vector2f subtractSelf(Vector2f v) {
return subtractSelf(v.x, v.y);
}
public Vector2f subtractSelf(float x, float y) {
return addSelf(-x, -y);
}
public static Vector2f transform(Vector2f value, Quaternion rotation) {
return transform(value, rotation, null);
}
public static Vector2f transform(Vector2f value, Quaternion rotation, Vector2f result) {
if (result == null) {
result = new Vector2f();
}
Vector3f rot1 = new Vector3f(rotation.x + rotation.x, rotation.y + rotation.y, rotation.z + rotation.z);
Vector3f rot2 = new Vector3f(rotation.x, rotation.x, rotation.w);
Vector3f rot3 = new Vector3f(1, rotation.y, rotation.z);
Vector3f rot4 = rot1.mul(rot2);
Vector3f rot5 = rot1.mul(rot3);
Vector2f v = new Vector2f();
v.x = (value.x * (1f - rot5.y - rot5.z) + value.y * (rot4.y - rot4.z));
v.y = (value.x * (rot4.y + rot4.z) + value.y * (1f - rot4.x - rot5.z));
result.x = v.x;
result.y = v.y;
return result;
}
}