/*
Copyright (C) 2001, 2006 United States Government as represented by
the Administrator of the National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.geom;
import gov.nasa.worldwind.util.Logging;
/**
* @author dcollins
* @version $Id: Vec4.java 3295 2007-10-15 22:44:21Z dcollins $
*/
public class Vec4
{
public static final Vec4 INFINITY =
new Vec4(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0);
public static final Vec4 ZERO = new Vec4(0, 0, 0, 1);
public static final Vec4 ONE = new Vec4(1, 1, 1, 1);
public static final Vec4 UNIT_X = new Vec4(1, 0, 0, 0);
public static final Vec4 UNIT_NEGATIVE_X = new Vec4(-1, 0, 0, 0);
public static final Vec4 UNIT_Y = new Vec4(0, 1, 0, 0);
public static final Vec4 UNIT_NEGATIVE_Y = new Vec4(0, -1, 0, 0);
public static final Vec4 UNIT_Z = new Vec4(0, 0, 1, 0);
public static final Vec4 UNIT_NEGATIVE_Z = new Vec4(0, 0, -1, 0);
public static final Vec4 UNIT_W = new Vec4(0, 0, 0, 1);
public static final Vec4 UNIT_NEGATIVE_W = new Vec4(0, 0, 0, -1);
public final double x;
public final double y;
public final double z;
public final double w;
// Default W-component will be 1 to handle Matrix transformation.
private static final double DEFAULT_W = 1.0;
// 4 values in a 4-component vector.
private static final int NUM_ELEMENTS = 4;
// Cached computations.
private int hashCode;
public Vec4(double value)
{
this(value, value, value);
}
public Vec4(double x, double y, double z)
{
this(x, y, z, DEFAULT_W);
}
public Vec4(double x, double y, double z, double w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public final boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || obj.getClass() != this.getClass())
return false;
Vec4 that = (Vec4) obj;
return (this.x == that.x)
&& (this.y == that.y)
&& (this.z == that.z)
&& (this.w == that.w);
}
public final int hashCode()
{
if (this.hashCode == 0)
{
int result;
long tmp;
tmp = Double.doubleToLongBits(this.x);
result = (int) (tmp ^ (tmp >>> 32));
tmp = Double.doubleToLongBits(this.y);
result = 29 * result + (int) (tmp ^ (tmp >>> 32));
tmp = Double.doubleToLongBits(this.z);
result = 29 * result + (int) (tmp ^ (tmp >>> 32));
tmp = Double.doubleToLongBits(this.w);
result = 29 * result + (int) (tmp ^ (tmp >>> 32));
this.hashCode = result;
}
return this.hashCode;
}
public static Vec4 fromArray3(double[] compArray, int offset)
{
if (compArray == null)
{
String msg = Logging.getMessage("nullValue.ArrayIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if ((compArray.length - offset) < (NUM_ELEMENTS - 1))
{
String msg = Logging.getMessage("generic.ArrayInvalidLength", compArray.length);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
//noinspection PointlessArithmeticExpression
return new Vec4(
compArray[0 + offset],
compArray[1 + offset],
compArray[2 + offset]);
}
public static Vec4 fromArray4(double[] compArray, int offset)
{
if (compArray == null)
{
String msg = Logging.getMessage("nullValue.ArrayIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if ((compArray.length - offset) < NUM_ELEMENTS)
{
String msg = Logging.getMessage("generic.ArrayInvalidLength", compArray.length);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
//noinspection PointlessArithmeticExpression
return new Vec4(
compArray[0 + offset],
compArray[1 + offset],
compArray[2 + offset],
compArray[3 + offset]);
}
public final double[] toArray3(double[] compArray, int offset)
{
if (compArray == null)
{
String msg = Logging.getMessage("nullValue.ArrayIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if ((compArray.length - offset) < (NUM_ELEMENTS - 1))
{
String msg = Logging.getMessage("generic.ArrayInvalidLength", compArray.length);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
//noinspection PointlessArithmeticExpression
compArray[0 + offset] = this.x;
compArray[1 + offset] = this.y;
compArray[2 + offset] = this.z;
return compArray;
}
public final double[] toArray4(double[] compArray, int offset)
{
if (compArray == null)
{
String msg = Logging.getMessage("nullValue.ArrayIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if ((compArray.length - offset) < NUM_ELEMENTS)
{
String msg = Logging.getMessage("generic.ArrayInvalidLength", compArray.length);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
//noinspection PointlessArithmeticExpression
compArray[0 + offset] = this.x;
compArray[1 + offset] = this.y;
compArray[2 + offset] = this.z;
compArray[3 + offset] = this.w;
return compArray;
}
public final String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("(");
sb.append(this.x).append(", ");
sb.append(this.y).append(", ");
sb.append(this.z).append(", ");
sb.append(this.w);
sb.append(")");
return sb.toString();
}
public final double getX()
{
return this.x;
}
public final double getY()
{
return this.y;
}
public final double getZ()
{
return this.z;
}
public final double getW()
{
return this.w;
}
public final double x()
{
return this.x;
}
public final double y()
{
return this.y;
}
public final double z()
{
return this.z;
}
public final double w()
{
return this.w;
}
// ============== Factory Functions ======================= //
// ============== Factory Functions ======================= //
// ============== Factory Functions ======================= //
public static Vec4 fromLine3(Vec4 origin, double t, Vec4 direction)
{
if (origin == null || direction == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
origin.x + (direction.x * t),
origin.y + (direction.y * t),
origin.z + (direction.z * t));
// return fromLine3(
// origin.x, origin.y, origin.z,
// direction.x, direction.y, direction.z,
// t,
// true);
}
//
// private static Vec4 fromLine3(
// double px, double py, double pz,
// double dx, double dy, double dz,
// double t,
// boolean normalize)
// {
// if (normalize)
// {
// double dLength = Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
// if (!isZero(dLength) && (dLength != 1.0))
// {
// dx /= dLength;
// dy /= dLength;
// dz /= dLength;
// }
// }
//
// return new Vec4(
// px + (dx * t),
// py + (dy * t),
// pz + (dz * t));
// }
// ============== Arithmetic Functions ======================= //
// ============== Arithmetic Functions ======================= //
// ============== Arithmetic Functions ======================= //
public final Vec4 add3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
this.x + vec4.x,
this.y + vec4.y,
this.z + vec4.z,
this.w);
}
public final Vec4 subtract3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
this.x - vec4.x,
this.y - vec4.y,
this.z - vec4.z,
this.w);
}
public final Vec4 multiply3(double value)
{
return new Vec4(
this.x * value,
this.y * value,
this.z * value,
this.w);
}
public final Vec4 multiply3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
this.x * vec4.x,
this.y * vec4.y,
this.z * vec4.z,
this.w);
}
public final Vec4 divide3(double value)
{
if (isZero(value))
{
String msg = Logging.getMessage("generic.ArgumentOutOfRange", value);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
this.x / value,
this.y / value,
this.z / value,
this.w);
}
public final Vec4 divide3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
this.x / vec4.x,
this.y / vec4.y,
this.z / vec4.z,
this.w);
}
public final Vec4 getNegative3()
{
return new Vec4(
0.0 - this.x,
0.0 - this.y,
0.0 - this.z,
this.w);
}
// ============== Geometric Functions ======================= //
// ============== Geometric Functions ======================= //
// ============== Geometric Functions ======================= //
public final double getLength3()
{
return Math.sqrt(this.getLengthSquared3());
}
public final double getLengthSquared3()
{
return (this.x * this.x)
+ (this.y * this.y)
+ (this.z * this.z);
}
public final Vec4 normalize3()
{
double length = this.getLength3();
// Vector has zero length.
if (isZero(length))
{
return this;
}
else
{
return new Vec4(
this.x / length,
this.y / length,
this.z / length);
}
}
public final double distanceTo3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return Math.sqrt(this.distanceToSquared3(vec4));
}
public final double distanceToSquared3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double tmp;
double result = 0.0;
tmp = this.x - vec4.x;
result += tmp * tmp;
tmp = this.y - vec4.y;
result += tmp * tmp;
tmp = this.z - vec4.z;
result += tmp * tmp;
return result;
}
public final double dot3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return (this.x * vec4.x) + (this.y * vec4.y) + (this.z * vec4.z);
}
public final double dot4(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return (this.x * vec4.x) + (this.y * vec4.y) + (this.z * vec4.z) + (this.w * vec4.w);
}
public final Vec4 cross3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
(this.y * vec4.z) - (this.z * vec4.y),
(this.z * vec4.x) - (this.x * vec4.z),
(this.x * vec4.y) - (this.y * vec4.x));
}
public final Angle angleBetween3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double a_dot_b = this.dot3(vec4);
// Compute the sum of magnitudes.
double length = this.getLength3() * vec4.getLength3();
// Normalize the dot product, if necessary.
if (!isZero(length) && (length != 1.0))
a_dot_b /= length;
// Return null when dot-product is outside range [-1, 1].
if ((a_dot_b < -1.0) || (a_dot_b > 1.0))
return null;
// Angle is arc-cosine of normalized dot product.
return Angle.fromRadians(Math.acos(a_dot_b));
}
/**
* Compute the angle and rotation axis required to rotate one vector to align with another.
*
* @param v1 The base vector.
* @param v2 The vector to rotate into alignment with <code>v1</code>.
* @param result A reference to an array in which to return the computed axis. May not be null.
* @return The rotation angle.
* @throws IllegalArgumentException if any parameter is null.
*/
public static Angle axisAngle(Vec4 v1, Vec4 v2, Vec4[] result)
{
if (v1 == null || v2 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (result == null)
{
String msg = Logging.getMessage("nullValue.ArrayIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
// Compute rotation angle
Vec4 u1 = v1.normalize3();
Vec4 u0 = v2.normalize3();
Angle angle = Angle.fromRadians(Math.acos(u0.x * u1.x + u0.y * u1.y + u0.z * u1.z));
// Compute rotation axis
double A = (u0.y * u1.z) - (u0.z * u1.y);
double B = (u0.z * u1.x) - (u0.x * u1.z);
double C = (u0.x * u1.y) - (u0.y * u1.x);
double L = Math.sqrt(A * A + B * B + C * C);
result[0] = new Vec4(A / L, B / L, C / L);
return angle;
}
public final Vec4 projectOnto3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double dot = this.dot3(vec4);
double length = vec4.getLength3();
// Normalize the dot product, if necessary.
if (!isZero(length) && (length != 1.0))
dot /= (length * length);
return vec4.multiply3(dot);
}
public final Vec4 perpendicularTo3(Vec4 vec4)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return this.subtract3(projectOnto3(vec4));
}
public final Vec4 transformBy3(Matrix matrix)
{
if (matrix == null)
{
String msg = Logging.getMessage("nullValue.MatrixIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
(matrix.m11 * this.x) + (matrix.m12 * this.y) + (matrix.m13 * this.z),
(matrix.m21 * this.x) + (matrix.m22 * this.y) + (matrix.m23 * this.z),
(matrix.m31 * this.x) + (matrix.m32 * this.y) + (matrix.m33 * this.z));
}
public final Vec4 transformBy3(Quaternion quaternion)
{
if (quaternion == null)
{
String msg = Logging.getMessage("nullValue.QuaternionIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
Quaternion tmp = new Quaternion(this.x, this.y, this.z, 0.0);
tmp = quaternion.multiply(tmp);
tmp = tmp.multiply(quaternion.getInverse());
return new Vec4(tmp.x, tmp.y, tmp.z, 0.0);
}
public final Vec4 transformBy4(Matrix matrix)
{
if (matrix == null)
{
String msg = Logging.getMessage("nullValue.MatrixIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
(matrix.m11 * this.x) + (matrix.m12 * this.y) + (matrix.m13 * this.z) + (matrix.m14 * this.w),
(matrix.m21 * this.x) + (matrix.m22 * this.y) + (matrix.m23 * this.z) + (matrix.m24 * this.w),
(matrix.m31 * this.x) + (matrix.m32 * this.y) + (matrix.m33 * this.z) + (matrix.m34 * this.w),
(matrix.m41 * this.x) + (matrix.m42 * this.y) + (matrix.m43 * this.z) + (matrix.m44 * this.w));
}
// ============== Mixing Functions ======================= //
// ============== Mixing Functions ======================= //
// ============== Mixing Functions ======================= //
public static Vec4 min3(Vec4 value1, Vec4 value2)
{
if ((value1 == null) || (value2 == null))
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
(value1.x < value2.x) ? value1.x : value2.x,
(value1.y < value2.y) ? value1.y : value2.y,
(value1.x < value2.z) ? value1.z : value2.z);
}
public static Vec4 max3(Vec4 value1, Vec4 value2)
{
if ((value1 == null) || (value2 == null))
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
(value1.x > value2.x) ? value1.x : value2.x,
(value1.y > value2.y) ? value1.y : value2.y,
(value1.x > value2.z) ? value1.z : value2.z);
}
public static Vec4 clamp3(Vec4 vec4, double min, double max)
{
if (vec4 == null)
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return new Vec4(
(vec4.x < min) ? min : ((vec4.x > max) ? max : vec4.x),
(vec4.y < min) ? min : ((vec4.y > max) ? max : vec4.y),
(vec4.z < min) ? min : ((vec4.z > max) ? max : vec4.z));
}
public static Vec4 mix3(double amount, Vec4 value1, Vec4 value2)
{
if ((value1 == null) || (value2 == null))
{
String msg = Logging.getMessage("nullValue.Vec4IsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (amount < 0.0)
return value1;
else if (amount > 1.0)
return value2;
double t1 = 1.0 - amount;
return new Vec4(
(value1.x * t1) + (value2.x * amount),
(value1.y * t1) + (value2.y * amount),
(value1.z * t1) + (value2.z * amount));
}
// ============== Helper Functions ======================= //
// ============== Helper Functions ======================= //
// ============== Helper Functions ======================= //
private static final Double POSITIVE_ZERO = +0.0d;
private static final Double NEGATIVE_ZERO = -0.0d;
private static boolean isZero(double value)
{
return (POSITIVE_ZERO.compareTo(value) == 0)
|| (NEGATIVE_ZERO.compareTo(value) == 0);
}
}