/*
** 2011 April 5
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
*/
package info.ata4.bsplib.vector;
import info.ata4.io.DataReader;
import info.ata4.io.DataWriter;
import java.io.IOException;
/**
* An immutable fluent interface three-dimensional vector class for float values.
*
* Original class name: unmap.Vec
* Original author: Bob (Mellish?)
* Original creation date: January 20, 2005, 7:41 PM
*
* @author Nico Bergemann <barracuda415 at yahoo.de>
*/
public final class Vector3f extends VectorXf {
public static Vector3f read(DataReader in) throws IOException {
float x = in.readFloat();
float y = in.readFloat();
float z = in.readFloat();
return new Vector3f(x, y, z);
}
public static void write(DataWriter out, Vector3f vec) throws IOException {
out.writeFloat(vec.x);
out.writeFloat(vec.y);
out.writeFloat(vec.z);
}
// frequently used pre-defined vectors
public static final Vector3f NULL = new Vector3f(0, 0, 0);
public static final Vector3f MAX_VALUE = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
public static final Vector3f MIN_VALUE = MAX_VALUE.scalar(-1); // don't use Float.MIN_VALUE here
// vector values
public final float x;
public final float y;
public final float z;
/**
* Replicating contructor
* Constructs new Vector3f by copying an existing Vector3f
*
* @param v The Vector3f to copy
*/
public Vector3f(Vector3f v) {
this(v.x, v.y, v.z);
}
/**
* Constructs a new Vector3f using the values out of an array.
* The array must have a size of at least 3.
*
* @param v float array
*/
public Vector3f(float[] v) {
this(v[0], v[1], v[2]);
}
/**
* Constructs a new Vector3f from x, y and z components
*
* @param x the vector x component
* @param y the vector y component
* @param z the vector z component
*/
public Vector3f(float x, float y, float z) {
super(3);
this.x = x;
this.y = y;
this.z = z;
}
/**
* Returns the value of the n'th component.
*
* @param index component index
* @return component value
*/
@Override
public float get(int index) {
switch (index) {
case 0:
return this.x;
case 1:
return this.y;
case 2:
return this.z;
default:
return 0;
}
}
/**
* Set the value of the n'th component.
*
* @param index component index
* @param value new component value
* @return vector with new value
*/
@Override
public Vector3f set(int index, float value) {
switch (index) {
case 0:
return new Vector3f(value, y, z);
case 1:
return new Vector3f(x, value, z);
case 2:
return new Vector3f(x, y, value);
default:
return this;
}
}
/**
* Vector dot product: this . that
*
* @param that the Vector3f to take dot product with
* @return the dot product of the two vectors
*/
public float dot(Vector3f that) {
return this.x * that.x + this.y * that.y + this.z * that.z;
}
/**
* Vector cross product: this x that
*
* @param that the vector to take a cross product
* @return the cross-product vector
*/
public Vector3f cross(Vector3f that) {
float rx = this.y * that.z - this.z * that.y;
float ry = this.z * that.x - this.x * that.z;
float rz = this.x * that.y - this.y * that.x;
return new Vector3f(rx, ry, rz);
}
/**
* Vector normalisation: ^this
*
* @return the normalised vector
*/
public Vector3f normalize() {
float len = length();
float rx = x / len;
float ry = y / len;
float rz = z / len;
return new Vector3f(rx, ry, rz);
}
/**
* Vector addition: this + that
*
* @param that The vector to add
* @return The sum of the two vectors
*/
public Vector3f add(Vector3f that) {
float rx = this.x + that.x;
float ry = this.y + that.y;
float rz = this.z + that.z;
return new Vector3f(rx, ry, rz);
}
/**
* Vector subtraction: this - that
*
* @param that The vector to subtract
* @return The difference of the two vectors
*/
public Vector3f sub(Vector3f that) {
float rx = this.x - that.x;
float ry = this.y - that.y;
float rz = this.z - that.z;
return new Vector3f(rx, ry, rz);
}
/**
* Snap vector to nearest value: round(this / value) * value
*
* @param value snap value
* @return This vector snapped to the nearest values of 'value'
*/
public Vector3f snap(float value) {
float rx = Math.round(x / value) * value;
float ry = Math.round(y / value) * value;
float rz = Math.round(z / value) * value;
return new Vector3f(rx, ry, rz);
}
/**
* Calculate the length of this vector
*
* @return length of this vector
*/
public float length() {
return (float) Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2) + Math.pow(this.z, 2));
}
/**
* Performs a scalar multiplication on this vector: this * mul
*
* @param mul multiplicator
* @return scalar multiplied vector
*/
public Vector3f scalar(float mul) {
float rx = this.x * mul;
float ry = this.y * mul;
float rz = this.z * mul;
return new Vector3f(rx, ry, rz);
}
/**
* Performs a scalar multiplication on this vector: this * that
*
* @param that multiplicator vector
* @return scalar multiplied vector
*/
public Vector3f scalar(Vector3f that) {
float rx = this.x * that.x;
float ry = this.y * that.y;
float rz = this.z * that.z;
return new Vector3f(rx, ry, rz);
}
/**
* Rotates the vector.
*
* @param angles angles for each component in degrees
* @return rotated vector
*/
public Vector3f rotate(Vector3f angles) {
if (angles.x == 0 && angles.y == 0 && angles.z == 0) {
// nothing to do here
return this;
}
double rx = x;
double ry = y;
double rz = z;
// rotate x (pitch)
if (angles.x != 0) {
Point2d p = new Point2d(ry, rz).rotate(angles.x);
ry = p.x;
rz = p.y;
}
// rotate y (yaw)
if (angles.y != 0) {
Point2d p = new Point2d(rx, rz).rotate(angles.y);
rx = p.x;
rz = p.y;
}
// rotate z (roll)
if (angles.z != 0) {
Point2d p = new Point2d(rx, ry).rotate(angles.z);
rx = p.x;
ry = p.y;
}
return new Vector3f((float)rx, (float)ry, (float)rz);
}
/**
* Returns the minima between this vector and another vector.
*
* @param that other vector to compare
* @return minima of this and that vector
*/
public Vector3f min(Vector3f that) {
float rx = Math.min(this.x, that.x);
float ry = Math.min(this.y, that.y);
float rz = Math.min(this.z, that.z);
return new Vector3f(rx, ry, rz);
}
/**
* Returns the maxima between this vector and another vector.
*
* @param that other vector to compare
* @return maxima of this and that vector
*/
public Vector3f max(Vector3f that) {
float rx = Math.max(this.x, that.x);
float ry = Math.max(this.y, that.y);
float rz = Math.max(this.z, that.z);
return new Vector3f(rx, ry, rz);
}
/**
* Private helper class for rotation
*/
private class Point2d {
private final double x;
private final double y;
private Point2d(double x, double y) {
this.x = x;
this.y = y;
}
private Point2d rotate(double angle) {
// normalize angle
angle %= 360;
// special cases
if (angle == 0) {
return this;
}
if (angle == 90) {
return new Point2d(-y, x);
}
if (angle == 180) {
return new Point2d(-x, -y);
}
if (angle == 270) {
return new Point2d(y, -x);
}
// convert degrees to radians
angle = Math.toRadians(angle);
double r = Math.hypot(x, y);
double theta = Math.atan2(y, x);
double rx = r * Math.cos(theta + angle);
double ry = r * Math.sin(theta + angle);
return new Point2d(rx, ry);
}
}
}