/*
* Copyright (C) 2014 MillerV
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package aspect.util;
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Vector3f;
/**
* A vector quantity with 3 components. This class also contains static utility
* methods for performing mathematical operations on 3-component vectors.
* Instances are mutable, so copy() should be used whenever a separate copy is
* needed.
*
* @author MillerV
*/
public class Vector3 extends Vector3f {
/**
* Return a Vector3 with the components (0, 0, 0).
*
* @return the origin
*/
public static Vector3 zero() {
return new Vector3(0, 0, 0);
}
public static Vector3 one() {
return new Vector3(1, 1, 1);
}
public static Vector3 xAxis() {
return new Vector3(1, 0, 0);
}
public static Vector3 yAxis() {
return new Vector3(0, 1, 0);
}
public static Vector3 zAxis() {
return new Vector3(0, 0, 1);
}
/**
* Create a new Vector3 with the specified x, y, and z components.
*
* @param x the x component of the vector
* @param y the y component of the vector
* @param z the z component of the vector
*/
public Vector3(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public void set(Vector3 v) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
}
public Vector3(float val) {
this(val, val, val);
}
public Vector3(float x, float y) {
this(x, y, 0);
}
/**
* Copy this Vector3.
*
* @return a copy of this Vector3
*/
public Vector3 copy() {
return new Vector3(x, y, z);
}
/**
* Get the magnitude of this Vector3, or its distance from the origin.
*
* @return the magnitude of this Vector3
*/
public float mag() {
return mag(this);
}
public float mag2() {
return mag2(this);
}
/**
* Get a Vector2 whose x component is equal to this Vector3's x component
* and whose y component is equal to this Vector3's y component.
*
* @return a Vector2 with the (x, y) components of this Vector3
*/
public Vector2 xy() {
return new Vector2(x, y);
}
/**
* Get a Vector2 whose x component is equal to this Vector3's y component
* and whose y component is equal to this Vector3's z component.
*
* @return a Vector2 with the (y, z) components of this Vector3
*/
public Vector2 zy() {
return new Vector2(z, y);
}
/**
* Get a Vector2 whose x component is equal to this Vector3's x component
* and whose y component is equal to this Vector3's z component.
*
* @return a Vector2 with the (x, z) components of this Vector3
*/
public Vector2 xz() {
return new Vector2(x, z);
}
public Vector3 component(Vector3 axis) {
return new Vector3(x * axis.x, y * axis.y, z * axis.z);
}
public Vector3 project1D(Vector3 line) {
return line.times(dot(this, line) / dot(line, line));
}
public Vector3 project1D(Vector3 a, Vector3 b) {
Vector3 ab = b.minus(a);
Vector3 ap = this.minus(a);
return a.plus(ap.project1D(ab));
}
/**
* Find the sum of this Vector3 and another Vector3.
*
* @param v the other Vector3
* @return the sum of the two vectors
*/
public Vector3 plus(Vector3 v) {
return add(this, v);
}
/**
* Find the difference between this Vector3 and another Vector3.
*
* @param v the other Vector3
* @return the sum of the two vectors
*/
public Vector3 minus(Vector3 v) {
return subtract(this, v);
}
/**
* Get a Vector3 whose components are equal to the opposite of this
* Vector3's components, resulting in equal magnitude and opposite
* direction.
*
* @return the negated Vector3
*/
@Override
public Vector3 negate() {
return negate(this);
}
/**
* Get a Vector3 whose components are equal to this Vector3's components
* multiplied by the given value.
*
* @param f the multiplier
* @return the multiplied Vector3
*/
public Vector3 times(float f) {
return multiply(this, f);
}
/**
* Get a Vector3 whose magnitude is equal to this vector's magnitude and
* whose direction is equal to this vector's direction plus the given
* angles.
*
* @param angles the angles to add
* @return the rotated vector
*/
public Vector3 rotate(Angles angles) {
return fromAngles(angles.plus(dir()), mag());
}
/**
* Get the direction of this vector as a set of Angles.
*
* @return the direction of this vector
*/
public Angles dir() {
return dir(this);
}
/**
* Get a Vector3 whose magnitude is 1 and whose direction is equal to the
* direction of this Vector3.
*
* @return the normalized Vector3
*/
public Vector3 normalize() {
return normalize(this);
}
public Vector3 abs() {
return new Vector3(Math.abs(x), Math.abs(y), Math.abs(z));
}
/**
* Convert the Cartesian components of a Vector3 to pitch and yaw. Roll will
* always be 0 because a vector has no roll.
*
* @param v the Vector3 to convert
* @return the direction of this vector
*/
public static Angles dir(Vector3 v) {
float pitch = Trig.asin(v.y / v.mag());
float yaw = Trig.atan2(v.x, -v.z);
return new Angles(pitch, yaw, 0);
}
/**
* Find the Angles pointing from one Vector3 to another.
*
* @param v1 the first Vector3
* @param v2 the second Vector3
* @return the angles pointing from v1 to v2
*/
public static Angles toAngles(Vector3 v1, Vector3 v2) {
return dir(v2.minus(v1));
}
/**
* Construct a new Vector3 from standard form (direction, magnitude).
*
* @param direction the direction of the new Vector3
* @param magnitude the magnitude of the new Vector3
* @return a new Vector3 with the given direction and magnitude
*/
public static Vector3 fromAngles(Angles direction, float magnitude) {
float x = Trig.cos(direction.pitch) * Trig.sin(direction.yaw) * magnitude;
float z = -Trig.cos(direction.pitch) * Trig.cos(direction.yaw) * magnitude;
float y = Trig.sin(direction.pitch) * magnitude;
return new Vector3(x, y, z);
}
private static Vector3 move(Vector3 dir, float amount, boolean flat) {
if (flat) {
Angles angles = dir.dir();
Vector2 newPos = Vector2.fromAngle(angles.yaw, amount);
return new Vector3(newPos.x, 0, newPos.y);
} else {
return dir.times(amount);
}
}
/**
* Get a Vector3 that represents a move along the local negative z axis of
* the given Vector3. If flat is set to true, the new Vector3 will only
* represent a move along the absolute x and z axes.
*
* @param dir the forward direction
* @param amount the the amount to move forward
* @param flat whether the new Vector3 should have 0 as the y component
* @return the vector representing a move
*/
public static Vector3 moveForward(Vector3 dir, float amount, boolean flat) {
return move(dir, amount, flat);
}
/**
* Get a Vector3 that represents a move along the local positive z axis of
* the given Vector3. If flat is set to true, the new Vector3 will only
* represent a move along the absolute x and z axes.
*
* @param dir the forward direction
* @param amount the amount to move backward
* @param flat whether the new Vector3 should have 0 as the y component
* @return the vector representing a move
*/
public static Vector3 moveBackward(Vector3 dir, float amount, boolean flat) {
return move(dir, -amount, flat);
}
public static Vector3 moveUp(Vector3 dir, float amount) {
Vector3 localX = cross(dir, yAxis());
return move(cross(dir, localX), amount, false);
}
public static Vector3 moveDown(Vector3 dir, float amount) {
return moveUp(dir, -amount);
}
/**
* Get a Vector3 that represents a move along the local negative x axis of
* the given Vector3. If flat is set to true, the new Vector3 will only
* represent a move along the absolute x and z axes.
*
* @param dir the forward direction
* @param amount the amount to move left
* @param flat whether the new Vector3 should have 0 as the y component
* @return the vector representing a move
*/
public static Vector3 strafeLeft(Vector3 dir, float amount, boolean flat) {
return move(cross(dir, yAxis()), -amount, flat);
}
/**
* Get a Vector3 that represents a move along the local positive x axis of
* the given Vector3. If flat is set to true, the new Vector3 will only
* represent a move along the absolute x and z axes.
*
* @param dir the forward direction
* @param amount the amount to move right
* @param flat whether the new Vector3 should have 0 as the y component
* @return the vector representing a move
*/
public static Vector3 strafeRight(Vector3 dir, float amount, boolean flat) {
return move(cross(dir, yAxis()), amount, flat);
}
public Vector3 rotateTowards(Vector3 target, float maxAngle) {
float angle = angle(this, target);
if (angle <= maxAngle) {
return target;
} else {
Vector3 axis = cross(this, target).normalize();
Matrix4x4 m = Matrix4x4.identity().rotate(axis, maxAngle);
return m.transformVector(this);
}
}
/**
* Find the sum of two Vector3s.
*
* @param v1 the first Vector3
* @param v2 the second Vector3
* @return the sum p1 + p2
*/
public static Vector3 add(Vector3 v1, Vector3 v2) {
return new Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
/**
* Find the difference of two Vector3s.
*
* @param s the subtrahend Vector3
* @param m the minuend Vector3
* @return the difference s - m
*/
public static Vector3 subtract(Vector3 s, Vector3 m) {
return new Vector3(s.x - m.x, s.y - m.y, s.z - m.z);
}
/**
* Get a Vector3 whose components are equal to the given Vector3's
* components multiplied by -1.
*
* @param v the Vector3 to negate
* @return the negated Vector3
*/
public static Vector3 negate(Vector3 v) {
return new Vector3(-v.x, -v.y, -v.z);
}
/**
* Find the dot product of two Vector3s, or
* <code>v1.x * v2.x + v1.y * v2.y + v1.z * v2.z</code>
*
* @param v1 the first Vector3
* @param v2 the second Vector3
* @return the dot product of v1 and v2
*/
public static float dot(Vector3 v1, Vector3 v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
/**
* Find the cross product of two Vector3s, a Vector3 perpendicular to the
* plane containing v1 and v2.
*
* @param v1 the first Vector3
* @param v2 the second Vector3
* @return the cross product
*/
public static Vector3 cross(Vector3 v1, Vector3 v2) {
float nx = v1.y * v2.z - v1.z * v2.y;
float ny = v1.z * v2.x - v1.x * v2.z;
float nz = v1.x * v2.y - v1.y * v2.x;
return new Vector3(nx, ny, nz);
}
/**
* Get a Vector3 whose magnitude is 1 and whose direction is the sum of the
* directions of the given Vector3s.
*
* @param v1 the first Vector
* @param v2 the second Vector
* @return the angular sum
*/
public static Vector3 addAngular(Vector3 v1, Vector3 v2) {
Angles a1 = dir(v1);
Angles a2 = dir(v2);
return fromAngles(Angles.add(a1, a2), 1);
}
public static float angle(Vector3 v1, Vector3 v2) {
return Vector3f.angle(v1, v2);
}
/**
* Get a Vector3 whose components are equal to the components of the given
* Vector3 multiplied by the given value.
*
* @param v the Vector3 to multiply
* @param scale the multiplier
* @return the scaled Vector3
*/
public static Vector3 multiply(Vector3 v, float scale) {
return new Vector3(v.x * scale, v.y * scale, v.z * scale);
}
public static Vector3 divide(Vector3 v, float scale) {
return new Vector3(v.x / scale, v.y / scale, v.z / scale);
}
public static Vector3 divide(float scale, Vector3 v) {
return new Vector3(scale / v.x, scale / v.y, scale / v.z);
}
/**
* Get a Vector3 whose direction is equal to the direction of the given
* Vector3 and whose magnitude is 1.
*
* @param v the Vector3 to normalize
* @return the normalized Vector3
*/
public static Vector3 normalize(Vector3 v) {
if (v.equals(zero())) {
return v;
}
float distance = mag(v);
return multiply(v, 1 / distance);
}
/**
* Find the distance between the two given Vector3s.
*
* @param v1 the first Vector3
* @param v2 the second Vector3
* @return the distance between v1 and v2
*/
public static float distance(Vector3 v1, Vector3 v2) {
return (float) Math.sqrt(distance2(v1, v2));
}
public static float distance2(Vector3 v1, Vector3 v2) {
Vector3 v = v2.minus(v1);
return dot(v, v);
}
/**
* Get the magnitude of the given Vector3.
*
* @param v the Vector3
* @return the magnitude of v
*/
public static float mag(Vector3 v) {
return (float) Math.sqrt(mag2(v));
}
public static float mag2(Vector3 v) {
return dot(v, v);
}
@Override
public String toString() {
return "(" + x + ", " + y + ", " + z + ")";
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Vector3)) {
return false;
}
Vector3 v = (Vector3) obj;
return x == v.x && y == v.y && z == v.z;
}
@Override
public int hashCode() {
int hash = 5;
hash = 79 * hash + Float.floatToIntBits(this.x);
hash = 79 * hash + Float.floatToIntBits(this.y);
hash = 79 * hash + Float.floatToIntBits(this.z);
return hash;
}
public FloatBuffer getBuffer() {
FloatBuffer buffer = BufferUtils.createFloatBuffer(3);
store(buffer);
buffer.clear();
return buffer;
}
}