/* * $RCSfile: GVector.java,v $ * * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * * $Revision: 1.7 $ * $Date: 2008/02/28 20:18:50 $ * $State: Exp $ */ package javax.vecmath; /** * A double precision, general, dynamically-resizable, one-dimensional vector * class. Index numbering begins with zero. */ public class GVector implements java.io.Serializable, Cloneable { private int length; double[] values; // Compatible with 1.1 static final long serialVersionUID = 1398850036893875112L; /** * Constructs a new GVector of the specified length with all vector elements * initialized to 0. * * @param length * the number of elements in this GVector. */ public GVector(int length) { int i; this.length = length; values = new double[length]; for (i = 0; i < length; i++) values[i] = 0.0; } /** * Constructs a new GVector from the specified array elements. The length of * this GVector is set to the length of the specified array. The array * elements are copied into this new GVector. * * @param vector * the values for the new GVector. */ public GVector(double[] vector) { int i; length = vector.length; values = new double[vector.length]; for (i = 0; i < length; i++) values[i] = vector[i]; } /** * Constructs a new GVector from the specified vector. The vector elements are * copied into this new GVector. * * @param vector * the source GVector for this new GVector. */ public GVector(GVector vector) { int i; values = new double[vector.length]; length = vector.length; for (i = 0; i < length; i++) values[i] = vector.values[i]; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param tuple * the source for the new GVector's initial values */ public GVector(Tuple2f tuple) { values = new double[2]; values[0] = (double) tuple.x; values[1] = (double) tuple.y; length = 2; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param tuple * the source for the new GVector's initial values */ public GVector(Tuple3f tuple) { values = new double[3]; values[0] = (double) tuple.x; values[1] = (double) tuple.y; values[2] = (double) tuple.z; length = 3; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param tuple * the source for the new GVector's initial values */ public GVector(Tuple3d tuple) { values = new double[3]; values[0] = tuple.x; values[1] = tuple.y; values[2] = tuple.z; length = 3; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param tuple * the source for the new GVector's initial values */ public GVector(Tuple4f tuple) { values = new double[4]; values[0] = (double) tuple.x; values[1] = (double) tuple.y; values[2] = (double) tuple.z; values[3] = (double) tuple.w; length = 4; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param tuple * the source for the new GVector's initial values */ public GVector(Tuple4d tuple) { values = new double[4]; values[0] = tuple.x; values[1] = tuple.y; values[2] = tuple.z; values[3] = tuple.w; length = 4; } /** * Constructs a new GVector of the specified length and initializes it by * copying the specified number of elements from the specified array. The * array must contain at least <code>length</code> elements (i.e., * <code>vector.length</code> >= <code>length</code>. The length of this new * GVector is set to the specified length. * * @param vector * The array from which the values will be copied. * @param length * The number of values copied from the array. */ public GVector(double vector[], int length) { int i; this.length = length; values = new double[length]; for (i = 0; i < length; i++) { values[i] = vector[i]; } } /** * Returns the square root of the sum of the squares of this vector (its * length in n-dimensional space). * * @return length of this vector */ public final double norm() { double sq = 0.0; int i; for (i = 0; i < length; i++) { sq += values[i] * values[i]; } return (Math.sqrt(sq)); } /** * Returns the sum of the squares of this vector (its length squared in * n-dimensional space). * * @return length squared of this vector */ public final double normSquared() { double sq = 0.0; int i; for (i = 0; i < length; i++) { sq += values[i] * values[i]; } return (sq); } /** * Sets the value of this vector to the normalization of vector v1. * * @param v1 * the un-normalized vector */ public final void normalize(GVector v1) { double sq = 0.0; int i; if (length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector0")); for (i = 0; i < length; i++) { sq += v1.values[i] * v1.values[i]; } double invMag; invMag = 1.0 / Math.sqrt(sq); for (i = 0; i < length; i++) { values[i] = v1.values[i] * invMag; } } /** * Normalizes this vector in place. */ public final void normalize() { double sq = 0.0; int i; for (i = 0; i < length; i++) { sq += values[i] * values[i]; } double invMag; invMag = 1.0 / Math.sqrt(sq); for (i = 0; i < length; i++) { values[i] = values[i] * invMag; } } /** * Sets the value of this vector to the scalar multiplication of the scale * factor with the vector v1. * * @param s * the scalar value * @param v1 * the source vector */ public final void scale(double s, GVector v1) { int i; if (length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector1")); for (i = 0; i < length; i++) { values[i] = v1.values[i] * s; } } /** * Scales this vector by the scale factor s. * * @param s * the scalar value */ public final void scale(double s) { int i; for (i = 0; i < length; i++) { values[i] = values[i] * s; } } /** * Sets the value of this vector to the scalar multiplication by s of vector * v1 plus vector v2 (this = s*v1 + v2). * * @param s * the scalar value * @param v1 * the vector to be multiplied * @param v2 * the vector to be added */ public final void scaleAdd(double s, GVector v1, GVector v2) { int i; if (v2.length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector2")); if (length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector3")); for (i = 0; i < length; i++) { values[i] = v1.values[i] * s + v2.values[i]; } } /** * Sets the value of this vector to sum of itself and the specified vector * * @param vector * the second vector */ public final void add(GVector vector) { int i; if (length != vector.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector4")); for (i = 0; i < length; i++) { this.values[i] += vector.values[i]; } } /** * Sets the value of this vector to the vector sum of vectors vector1 and * vector2. * * @param vector1 * the first vector * @param vector2 * the second vector */ public final void add(GVector vector1, GVector vector2) { int i; if (vector1.length != vector2.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector5")); if (length != vector1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector6")); for (i = 0; i < length; i++) this.values[i] = vector1.values[i] + vector2.values[i]; } /** * Sets the value of this vector to the vector difference of itself and vector * (this = this - vector). * * @param vector * the other vector */ public final void sub(GVector vector) { int i; if (length != vector.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector7")); for (i = 0; i < length; i++) { this.values[i] -= vector.values[i]; } } /** * Sets the value of this vector to the vector difference of vectors vector1 * and vector2 (this = vector1 - vector2). * * @param vector1 * the first vector * @param vector2 * the second vector */ public final void sub(GVector vector1, GVector vector2) { int i; if (vector1.length != vector2.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector8")); if (length != vector1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector9")); for (i = 0; i < length; i++) this.values[i] = vector1.values[i] - vector2.values[i]; } /** * Multiplies matrix m1 times Vector v1 and places the result into this vector * (this = m1*v1). * * @param m1 * The matrix in the multiplication * @param v1 * The vector that is multiplied */ public final void mul(GMatrix m1, GVector v1) { if (m1.getNumCol() != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector10")); if (length != m1.getNumRow()) throw new MismatchedSizeException(VecMathI18N.getString("GVector11")); double v[]; if (v1 != this) { v = v1.values; } else { v = new double[values.length]; for (int i = 0; i < values.length; i++) { v[i] = values[i]; } } for (int j = length - 1; j >= 0; j--) { values[j] = 0.0; for (int i = v1.length - 1; i >= 0; i--) { values[j] += m1.values[j][i] * v[i]; } } } /** * Multiplies the transpose of vector v1 (ie, v1 becomes a row vector with * respect to the multiplication) times matrix m1 and places the result into * this vector (this = transpose(v1)*m1). The result is technically a row * vector, but the GVector class only knows about column vectors, and so the * result is stored as a column vector. * * @param m1 * The matrix in the multiplication * @param v1 * The vector that is temporarily transposed */ public final void mul(GVector v1, GMatrix m1) { if (m1.getNumRow() != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector12")); if (length != m1.getNumCol()) throw new MismatchedSizeException(VecMathI18N.getString("GVector13")); double v[]; if (v1 != this) { v = v1.values; } else { v = new double[values.length]; for (int i = 0; i < values.length; i++) { v[i] = values[i]; } } for (int j = length - 1; j >= 0; j--) { values[j] = 0.0; for (int i = v1.length - 1; i >= 0; i--) { values[j] += m1.values[i][j] * v[i]; } } } /** * Negates the value of this vector: this = -this. */ public final void negate() { for (int i = length - 1; i >= 0; i--) { this.values[i] *= -1.0; } } /** * Sets all the values in this vector to zero. */ public final void zero() { for (int i = 0; i < this.length; i++) { this.values[i] = 0.0; } } /** * Changes the size of this vector dynamically. If the size is increased no * data values will be lost. If the size is decreased, only those data values * whose vector positions were eliminated will be lost. * * @param length * number of desired elements in this vector */ public final void setSize(int length) { double[] tmp = new double[length]; int i, max; if (this.length < length) max = this.length; else max = length; for (i = 0; i < max; i++) { tmp[i] = values[i]; } this.length = length; values = tmp; } /** * Sets the value of this vector to the values found in the array parameter. * The array should be at least equal in length to the number of elements in * the vector. * * @param vector * the source array */ public final void set(double[] vector) { for (int i = length - 1; i >= 0; i--) values[i] = vector[i]; } /** * Sets the value of this vector to the values found in vector vector. * * @param vector * the source vector */ public final void set(GVector vector) { int i; if (length < vector.length) { length = vector.length; values = new double[length]; for (i = 0; i < length; i++) values[i] = vector.values[i]; } else { for (i = 0; i < vector.length; i++) values[i] = vector.values[i]; for (i = vector.length; i < length; i++) values[i] = 0.0; } } /** * Sets the value of this vector to the values in tuple * * @param tuple * the source for the new GVector's new values */ public final void set(Tuple2f tuple) { if (length < 2) { length = 2; values = new double[2]; } values[0] = (double) tuple.x; values[1] = (double) tuple.y; for (int i = 2; i < length; i++) values[i] = 0.0; } /** * Sets the value of this vector to the values in tuple * * @param tuple * the source for the new GVector's new values */ public final void set(Tuple3f tuple) { if (length < 3) { length = 3; values = new double[3]; } values[0] = (double) tuple.x; values[1] = (double) tuple.y; values[2] = (double) tuple.z; for (int i = 3; i < length; i++) values[i] = 0.0; } /** * Sets the value of this vector to the values in tuple * * @param tuple * the source for the new GVector's new values */ public final void set(Tuple3d tuple) { if (length < 3) { length = 3; values = new double[3]; } values[0] = tuple.x; values[1] = tuple.y; values[2] = tuple.z; for (int i = 3; i < length; i++) values[i] = 0.0; } /** * Sets the value of this vector to the values in tuple * * @param tuple * the source for the new GVector's new values */ public final void set(Tuple4f tuple) { if (length < 4) { length = 4; values = new double[4]; } values[0] = (double) tuple.x; values[1] = (double) tuple.y; values[2] = (double) tuple.z; values[3] = (double) tuple.w; for (int i = 4; i < length; i++) values[i] = 0.0; } /** * Sets the value of this vector to the values in tuple * * @param tuple * the source for the new GVector's new values */ public final void set(Tuple4d tuple) { if (length < 4) { length = 4; values = new double[4]; } values[0] = tuple.x; values[1] = tuple.y; values[2] = tuple.z; values[3] = tuple.w; for (int i = 4; i < length; i++) values[i] = 0.0; } /** * Returns the number of elements in this vector. * * @return number of elements in this vector */ public final int getSize() { return values.length; } /** * Retrieves the value at the specified index value of this vector. * * @param index * the index of the element to retrieve (zero indexed) * @return the value at the indexed element */ public final double getElement(int index) { return values[index]; } /** * Modifies the value at the specified index of this vector. * * @param index * the index if the element to modify (zero indexed) * @param value * the new vector element value */ public final void setElement(int index, double value) { values[index] = value; } /** * Returns a string that contains the values of this GVector. * * @return the String representation */ public String toString() { StringBuffer buffer = new StringBuffer(length * 8); int i; for (i = 0; i < length; i++) { buffer.append(values[i]).append(" "); } return buffer.toString(); } /** * Returns a hash code value based on the data values in this object. Two * different GVector objects with identical data values (i.e., GVector.equals * returns true) will return the same hash number. Two GVector objects with * different data members may return the same hash value, although this is not * likely. * * @return the integer hash code value */ public int hashCode() { long bits = 1L; for (int i = 0; i < length; i++) { bits = 31L * bits + VecMathUtil.doubleToLongBits(values[i]); } return (int) (bits ^ (bits >> 32)); } /** * Returns true if all of the data members of GVector vector1 are equal to the * corresponding data members in this GVector. * * @param vector1 * The vector with which the comparison is made. * @return true or false */ public boolean equals(GVector vector1) { try { if (length != vector1.length) return false; for (int i = 0; i < length; i++) { if (values[i] != vector1.values[i]) return false; } return true; } catch (NullPointerException e2) { return false; } } /** * Returns true if the Object o1 is of type GMatrix and all of the data * members of o1 are equal to the corresponding data members in this GMatrix. * * @param o1 * The object with which the comparison is made. * @return true or false */ public boolean equals(Object o1) { try { GVector v2 = (GVector) o1; if (length != v2.length) return false; for (int i = 0; i < length; i++) { if (values[i] != v2.values[i]) return false; } return true; } catch (ClassCastException e1) { return false; } catch (NullPointerException e2) { return false; } } /** * Returns true if the L-infinite distance between this vector and vector v1 * is less than or equal to the epsilon parameter, otherwise returns false. * The L-infinite distance is equal to MAX[abs(x1-x2), abs(y1-y2), . . . ]. * * @param v1 * The vector to be compared to this vector * @param epsilon * the threshold value */ public boolean epsilonEquals(GVector v1, double epsilon) { double diff; if (length != v1.length) return false; for (int i = 0; i < length; i++) { diff = values[i] - v1.values[i]; if ((diff < 0 ? -diff : diff) > epsilon) return false; } return true; } /** * Returns the dot product of this vector and vector v1. * * @param v1 * the other vector * @return the dot product of this and v1 */ public final double dot(GVector v1) { if (length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector14")); double result = 0.0; for (int i = 0; i < length; i++) { result += values[i] * v1.values[i]; } return result; } /** * Solves for x in Ax = b, where x is this vector (nx1), A is mxn, b is mx1, * and A = U*W*transpose(V); U,W,V must be precomputed and can be found by * taking the singular value decomposition (SVD) of A using the method SVD * found in the GMatrix class. * * @param U * The U matrix produced by the GMatrix method SVD * @param W * The W matrix produced by the GMatrix method SVD * @param V * The V matrix produced by the GMatrix method SVD * @param b * The b vector in the linear equation Ax = b */ public final void SVDBackSolve(GMatrix U, GMatrix W, GMatrix V, GVector b) { if (!(U.nRow == b.getSize() && U.nRow == U.nCol && U.nRow == W.nRow)) { throw new MismatchedSizeException(VecMathI18N.getString("GVector15")); } if (!(W.nCol == values.length && W.nCol == V.nCol && W.nCol == V.nRow)) { throw new MismatchedSizeException(VecMathI18N.getString("GVector23")); } GMatrix tmp = new GMatrix(U.nRow, W.nCol); tmp.mul(U, V); tmp.mulTransposeRight(U, W); tmp.invert(); mul(tmp, b); } /** * LU Decomposition Back Solve; this method takes the LU matrix and the * permutation vector produced by the GMatrix method LUD and solves the * equation (LU)*x = b by placing the solution vector x into this vector. This * vector should be the same length or longer than b. * * @param LU * The matrix into which the lower and upper decompostions have been * placed * @param b * The b vector in the equation (LU)*x = b * @param permutation * The row permuations that were necessary to produce the LU matrix * parameter */ public final void LUDBackSolve(GMatrix LU, GVector b, GVector permutation) { int size = LU.nRow * LU.nCol; double[] temp = new double[size]; double[] result = new double[size]; int[] row_perm = new int[b.getSize()]; int i, j; if (LU.nRow != b.getSize()) { throw new MismatchedSizeException(VecMathI18N.getString("GVector16")); } if (LU.nRow != permutation.getSize()) { throw new MismatchedSizeException(VecMathI18N.getString("GVector24")); } if (LU.nRow != LU.nCol) { throw new MismatchedSizeException(VecMathI18N.getString("GVector25")); } for (i = 0; i < LU.nRow; i++) { for (j = 0; j < LU.nCol; j++) { temp[i * LU.nCol + j] = LU.values[i][j]; } } for (i = 0; i < size; i++) result[i] = 0.0; for (i = 0; i < LU.nRow; i++) result[i * LU.nCol] = b.values[i]; for (i = 0; i < LU.nCol; i++) row_perm[i] = (int) permutation.values[i]; GMatrix.luBacksubstitution(LU.nRow, temp, row_perm, result); for (i = 0; i < LU.nRow; i++) this.values[i] = result[i * LU.nCol]; } /** * Returns the (n-space) angle in radians between this vector and the vector * parameter; the return value is constrained to the range [0,PI]. * * @param v1 * The other vector * @return The angle in radians in the range [0,PI] */ public final double angle(GVector v1) { return (Math.acos(this.dot(v1) / (this.norm() * v1.norm()))); } /** * @deprecated Use interpolate(GVector, GVector, double) instead */ public final void interpolate(GVector v1, GVector v2, float alpha) { interpolate(v1, v2, (double) alpha); } /** * @deprecated Use interpolate(GVector, double) instead */ public final void interpolate(GVector v1, float alpha) { interpolate(v1, (double) alpha); } /** * Linearly interpolates between vectors v1 and v2 and places the result into * this tuple: this = (1-alpha)*v1 + alpha*v2. * * @param v1 * the first vector * @param v2 * the second vector * @param alpha * the alpha interpolation parameter */ public final void interpolate(GVector v1, GVector v2, double alpha) { if (v2.length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector20")); if (length != v1.length) throw new MismatchedSizeException(VecMathI18N.getString("GVector21")); for (int i = 0; i < length; i++) { values[i] = (1 - alpha) * v1.values[i] + alpha * v2.values[i]; } } /** * Linearly interpolates between this vector and vector v1 and places the * result into this tuple: this = (1-alpha)*this + alpha*v1. * * @param v1 * the first vector * @param alpha * the alpha interpolation parameter */ public final void interpolate(GVector v1, double alpha) { if (v1.length != length) throw new MismatchedSizeException(VecMathI18N.getString("GVector22")); for (int i = 0; i < length; i++) { values[i] = (1 - alpha) * values[i] + alpha * v1.values[i]; } } }