/* * __ .__ .__ ._____. * _/ |_ _______ __|__| ____ | | |__\_ |__ ______ * \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/ * | | ( <_> > <| \ \___| |_| || \_\ \\___ \ * |__| \____/__/\_ \__|\___ >____/__||___ /____ > * \/ \/ \/ \/ * * Copyright (c) 2006-2011 Karsten Schmidt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * http://creativecommons.org/licenses/LGPL/2.1/ * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ package toxi.geom; import java.util.Arrays; import toxi.math.InterpolateStrategy; import toxi.math.MathUtils; /** * A double precision, general, dynamically-resizable, one-dimensional vector * class. */ public class GVector implements java.lang.Cloneable, java.io.Serializable { private int length; /** * */ public double[] values; static final long serialVersionUID = 1L; /** * 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 length elements (i.e., * vector.length >= length. 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) { this.length = length; values = new double[length]; System.arraycopy(vector, 0, values, 0, length); } /** * 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) { length = vector.length; values = new double[vector.length]; System.arraycopy(vector, 0, values, 0, length); } /** * 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) { values = new double[vector.length]; length = vector.length; System.arraycopy(vector.values, 0, values, 0, length); } /** * 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) { this.length = length; values = new double[length]; for (int i = 0; i < length; i++) { values[i] = 0.0; } } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param v the source for the new GVector's initial values */ public GVector(ReadonlyVec2D v) { values = new double[]{ v.x(), v.y() }; length = 2; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param v the source for the new GVector's initial values */ public GVector(ReadonlyVec3D v) { values = new double[]{ v.x(), v.y(), v.z() }; length = 3; } /** * Constructs a new GVector and copies the initial values from the specified * tuple. * * @param v the source for the new GVector's initial values */ public GVector(ReadonlyVec4D v) { values = new double[]{ v.x(), v.y(), v.z(), v.w() }; length = 4; } /** * Creates the vector sum of this vector and the given one (must be equal * sized). Returns result as new vector. * * @param v * @return new vector */ public final GVector add(GVector v) { if (length != v.length) { throw new MatrixSizeException(); } double[] tmp = new double[length]; for (int i = 0; i < length; i++) { tmp[i] = values[i] + v.values[i]; } return new GVector(tmp); } /** * Sets the value of this vector to sum of itself and the specified vector * * @param vector the second vector * @return itself */ public final GVector addSelf(GVector vector) { if (length != vector.length) { throw new MatrixSizeException(); } for (int i = 0; i < length; i++) { this.values[i] += vector.values[i]; } return this; } /** * 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 v The other vector * @return The angle in radians in the range [0,PI] */ public final double angleBetween(GVector v) { return (Math.acos(this.dot(v) / (this.magnitude() * v.magnitude()))); } /** * 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 backSolveLUD(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.size()]; int i, j; if (LU.nRow != b.size()) { throw new MatrixSizeException(); } if (LU.nRow != permutation.size()) { throw new MatrixSizeException(); } if (LU.nRow != LU.nCol) { throw new MatrixSizeException(); } 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 < 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.backSubstituteLU(LU.nRow, temp, row_perm, result); for (i = 0; i < LU.nRow; i++) { this.values[i] = result[i * LU.nCol]; } } /** * 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 backSolveSVD(GMatrix U, GMatrix W, GMatrix V, GVector b) { if (!(U.nRow == b.size() && U.nRow == U.nCol && U.nRow == W.nRow)) { throw new MatrixSizeException(); } if (!(W.nCol == values.length && W.nCol == V.nCol && W.nCol == V.nRow)) { throw new MatrixSizeException(); } GMatrix tmp = new GMatrix(U.nRow, W.nCol); tmp.mul(U, V); tmp.mulTransposeRight(U, W); tmp.invert(); mul(tmp, b); } /** * Creates a new object of the same class as this object. * * @return a clone of this instance. * @exception OutOfMemoryError if there is not enough memory. * @see java.lang.Cloneable * @since vecmath 1.3 */ // public Object clone() { // GVector v = null; // try { // v = (GVector) super.clone(); // } catch (CloneNotSupportedException e) { // throw new InternalError(); // } // v.values = new double[length]; // System.arraycopy(values, 0, v.values, 0, length); // return v; // } /** * Returns the dot product of this vector and vector v. * * @param v the other vector * @return the dot product of this and v */ public final double dot(GVector v) { if (length != v.length) { throw new MatrixSizeException(); } double result = 0.0; for (int i = 0; i < length; i++) { result += values[i] * v.values[i]; } return result; } /** * 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 e) { 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 */ @Override public boolean equals(Object o1) { if (o1 instanceof GVector) { 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; } return false; } /** * 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 */ @Override public int hashCode() { int hash = 5; hash = 43 * hash + this.length; hash = 43 * hash + Arrays.hashCode(this.values); return hash; } /** * Returns true if the L-infinite distance between this vector and vector v * is less than or equal to the tolerance parameter, otherwise returns * false. The L-infinite distance is equal to MAX[abs(x1-x2), abs(y1-y2), . * . . ]. * * @param v The vector to be compared to this vector * @param tolerance the threshold value * @return */ public boolean equalsWithTolerance(GVector v, double tolerance) { if (v instanceof GVector) { double diff; if (length != v.length) { return false; } for (int i = 0; i < length; i++) { diff = values[i] - v.values[i]; if ((diff < 0 ? -diff : diff) > tolerance) { return false; } } return true; } return false; } /** * 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 get(int index) { return values[index]; } /** * Linearly interpolates this vector to the target vector and places the * result into a new instance: result = this + (target-this)*alpha. The * target vector needs to be equal sized. * * @param v the target vector * @param alpha the alpha interpolation parameter * @return result as new vector */ public final GVector interpolateTo(GVector v, double alpha) { if (length != v.length) { throw new MatrixSizeException(); } return new GVector(this).interpolateToSelf(v, alpha); } /** * Interpolates the vector towards the given target vector, using the given * {@link InterpolateStrategy}. The target vector needs to be equal sized. * * @param v target vector * @param alpha interpolation factor (should be in the range 0..1) * @param strategy InterpolateStrategy instance * * @return result as new vector */ public final GVector interpolateTo(GVector v, double alpha, InterpolateStrategy strategy) { if (length != v.length) { throw new MatrixSizeException(); } return new GVector(this).interpolateToSelf(v, alpha, strategy); } /** * Linearly interpolates this vector to the target vector and places result * in this vector. result = this + (target-this)*alpha. The target vector * needs to be equal sized. * * @param v the target vector * @param alpha the alpha interpolation parameter * @return */ public final GVector interpolateToSelf(GVector v, double alpha) { if (v.length != length) { throw new MatrixSizeException(); } for (int i = 0; i < length; i++) { values[i] += (v.values[i] - values[i]) * alpha; } return this; } /** * Interpolates the vector towards the given target vector, using the given * {@link InterpolateStrategy}. The target vector needs to be equal sized. * * @param v target vector * @param alpha interpolation factor (should be in the range 0..1) * @param strategy InterpolateStrategy instance * * @return itself, result overrides current vector */ public final GVector interpolateToSelf(GVector v, double alpha, InterpolateStrategy strategy) { if (v.length != length) { throw new MatrixSizeException(); } for (int i = 0; i < length; i++) { values[i] = strategy.interpolate(values[i], v.values[i], alpha); } return this; } /** * Negates the value of this vector: this = -this. */ public final void invert() { for (int i = 0; i < length; i++) { this.values[i] *= -1.0; } } /** * 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 magnitude() { double sq = 0.0; for (int 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 magSquared() { double sq = 0.0; for (int i = 0; i < length; i++) { sq += values[i] * values[i]; } return sq; } /** * 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 MatrixSizeException(); } if (length != m1.getNumRow()) { throw new MatrixSizeException(); } double v[]; if (v1 != this) { v = v1.values; } else { v = values.clone(); } 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 MatrixSizeException(); } if (length != m1.getNumCol()) { throw new MatrixSizeException(); } double v[]; if (v1 != this) { v = v1.values; } else { v = values.clone(); } 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]; } } } /** * Normalizes this vector in place. */ public final void normalize() { double mag = magnitude(); if (mag > MathUtils.EPS) { double invMag = 1.0 / mag; for (int i = 0; i < length; i++) { values[i] = values[i] * invMag; } } } /** * Scales this vector by the scale factor s and returns result as new * vector. * * @param s the scalar value * @return new vector */ public final GVector scale(double s) { double[] tmp = new double[length]; for (int i = 0; i < length; i++) { tmp[i] = values[i] * s; } return new GVector(tmp); } /** * Scales the values of this vector with the values of the given vector * vector (this = this * vector). Returns result as new vector. * * @param v scale vector * @return new vector */ public final GVector scale(GVector v) { if (length != v.length) { throw new MatrixSizeException(); } double[] tmp = new double[length]; for (int i = 0; i < length; i++) { tmp[i] = values[i] * v.values[i]; } return new GVector(tmp); } /** * Scales this vector by the scale factor s. * * @param s the scalar value * @return itself */ public final GVector scaleSelf(double s) { for (int i = 0; i < length; i++) { values[i] = values[i] * s; } return this; } /** * Scales the values of this vector with the values of the given vector * vector (this = this * vector). * * @param v scale vector * @return itself */ public final GVector scaleSelf(GVector v) { if (length != v.length) { throw new MatrixSizeException(); } for (int i = 0; i < length; i++) { this.values[i] *= v.values[i]; } return this; } /** * Sets the values of this vector to the values found in the array * parameter. If the array is shorter than the number of values in this * vector the remaining values are zeroed. If the array is longer, only the * first values up to to the vector length are copied. * * @param vector the source array * @return */ public final GVector set(double[] vector) { int i; if (vector.length >= length) { for (i = 0; i < length; i++) { values[i] = vector[i]; } } else { for (i = 0; i < vector.length; i++) { values[i] = vector[i]; } for (i = vector.length; i < length; i++) { values[i] = 0.0; } } return this; } /** * Sets the value of this vector to the values found in vector vector. * * @param vector the source vector * @return */ public final GVector set(GVector vector) { return set(vector.values); } /** * Sets the value of this vector to the values in tuple * * @param tuple the source for the new GVector's new values * @return */ public final GVector set(ReadonlyVec2D tuple) { return set(new double[]{ tuple.x(), tuple.y() }); } /** * Sets the value of this vector to the values in tuple * * @param tuple the source for the new GVector's new values * @return */ public final GVector set(ReadonlyVec3D tuple) { return set(new double[]{ tuple.x(), tuple.y(), tuple.z() }); } /** * Sets the value of this vector to the values in tuple * * @param tuple the source for the new GVector's new values * @return itself */ public final GVector set(ReadonlyVec4D tuple) { return set(new double[]{ tuple.x(), tuple.y(), tuple.w() }); } /** * 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 * @return */ public final GVector setElement(int index, double value) { values[index] = value; return this; } /** * 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 * @return */ public final GVector setSize(int length) { double[] tmp = new double[length]; int max; if (this.length < length) { max = this.length; } else { max = length; } System.arraycopy(values, 0, tmp, 0, max); this.length = length; values = tmp; return this; } /** * Returns the number of elements in this vector. * * @return number of elements in this vector */ public final int size() { return values.length; } /** * Creates the vector difference of this vector and the given one (must be * equal sized). Returns result as new vector. * * @param v * @return new vector */ public final GVector sub(GVector v) { if (length != v.length) { throw new MatrixSizeException(); } double[] tmp = new double[length]; for (int i = 0; i < length; i++) { tmp[i] = values[i] - v.values[i]; } return new GVector(tmp); } /** * Sets the value of this vector to the vector difference of itself and * vector (this = this - vector). * * @param vector the other vector * @return */ public final GVector subSelf(GVector vector) { if (length != vector.length) { throw new MatrixSizeException(); } for (int i = 0; i < length; i++) { this.values[i] -= vector.values[i]; } return this; } /** * Returns a string that contains the values of this GVector. * * @return the String representation */ @Override public String toString() { StringBuilder buffer = new StringBuilder(length * 8); for (int i = 0; i < length; i++) { buffer.append(values[i]).append(" "); } return buffer.toString(); } /** * Sets all the values in this vector to zero. * * @return */ public final GVector zero() { for (int i = 0; i < length; i++) { this.values[i] = 0.0; } return this; } }