/** * Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.onebusaway.geospatial.model; import java.util.Arrays; public class PointVector { private double[] _vector; public static <T extends Point> PointVector create(T origin, T direction) { double[] vector = new double[origin.getDimensions()]; for (int i = 0; i < vector.length; i++) vector[i] = direction.getOrdinate(i) - origin.getOrdinate(i); return new PointVector(vector); } public PointVector(double... vector) { _vector = vector; } public int getDimensions() { return _vector.length; } public double getOrdinate(int dimension) { return _vector[dimension]; } public double[] getOrdinates() { double[] ord = new double[_vector.length]; System.arraycopy(_vector, 0, ord, 0, ord.length); return ord; } public double getX() { return _vector[0]; } public double getY() { return _vector[1]; } public double getZ() { return _vector[2]; } /******************************************************************************************************************* * Properties ******************************************************************************************************************/ public double length() { double sum = 0.0; for (int i = 0; i < _vector.length; i++) sum += _vector[i] * _vector[i]; return Math.sqrt(sum); } public double dotProduct(PointVector v) { double sum = 0.0; for (int i = 0; i < _vector.length; i++) sum += _vector[i] * v.getOrdinate(i); return sum; } public PointVector getCrossProduct(PointVector v) { if (getDimensions() != 3) throw new IllegalStateException("We only handle 3D case at the moment"); double[] a = _vector; double[] b = v._vector; double x = a[1] * b[2] - a[2] * b[1]; double y = a[2] * b[0] - a[0] * b[2]; double z = a[0] * b[1] - a[1] * b[0]; return new PointVector(x, y, z); } public double getCosAngle(PointVector v) { return dotProduct(v) / (length() * v.length()); } public double getSinAngle(PointVector v) { PointVector xp = getCrossProduct(v); double len = xp.length(); len /= (length() * v.length()); return len; } /** * * @param v the other vector * @return the angle between two vectors */ public double getAngle(PointVector v) { double cosTheta = getCosAngle(v); return Math.acos(cosTheta); } public double getAngle() { return Math.atan2(getY(), getX()); } /******************************************************************************************************************* * Operations ******************************************************************************************************************/ public PointVector getScaled(double factor) { double[] ord = getOrdinates(); for (int i = 0; i < ord.length; i++) ord[i] *= factor; return new PointVector(ord); } public PointVector getUnitVector() { return getAsLength(1.0); } /** * Project a vector v onto this vector * * @param v the vector to project * @return the projection of v onto this vector */ public PointVector getProjection(PointVector v) { double dp = dotProduct(v) / length(); return getAsLength(dp); } /** * Scale our vector such that it has the specified length * * @param length - the target length * @return a vector in the same direction but with the specified length */ public PointVector getAsLength(double length) { return getScaled(length / length()); } public PointVector rotate(double angle) { return rotate(angle, 0, 1); } public PointVector rotate(double angle, int dimensionA, int dimensionB) { double[] ord = getOrdinates(); ord[dimensionA] = _vector[dimensionA] * Math.cos(angle) + _vector[dimensionB] * Math.sin(angle); ord[dimensionB] = _vector[dimensionB] * Math.cos(angle) - _vector[dimensionA] * Math.sin(angle); return new PointVector(ord); } /** * * @param <T> the position type * @param origin point to translate * @return a new position equal to the specified shifted by the vector */ @SuppressWarnings("unchecked") public <T extends Point> T addToPoint(T origin) { return (T) origin.translate(_vector); } /******************************************************************************************************************* * {@link Object} Interface ******************************************************************************************************************/ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof PointVector)) return false; PointVector v = (PointVector) obj; return Arrays.equals(_vector, v._vector); } @Override public int hashCode() { return Arrays.hashCode(_vector); } @Override public String toString() { return Arrays.toString(_vector); } }