001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.geometry.euclidean.twod;
018
019import java.text.NumberFormat;
020
021import org.apache.commons.math3.exception.DimensionMismatchException;
022import org.apache.commons.math3.exception.MathArithmeticException;
023import org.apache.commons.math3.exception.util.LocalizedFormats;
024import org.apache.commons.math3.geometry.Point;
025import org.apache.commons.math3.geometry.Space;
026import org.apache.commons.math3.geometry.Vector;
027import org.apache.commons.math3.util.FastMath;
028import org.apache.commons.math3.util.MathArrays;
029import org.apache.commons.math3.util.MathUtils;
030
031/** This class represents a 2D vector.
032 * <p>Instances of this class are guaranteed to be immutable.</p>
033 * @since 3.0
034 */
035public class Vector2D implements Vector<Euclidean2D> {
036
037    /** Origin (coordinates: 0, 0). */
038    public static final Vector2D ZERO   = new Vector2D(0, 0);
039
040    // CHECKSTYLE: stop ConstantName
041    /** A vector with all coordinates set to NaN. */
042    public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
043    // CHECKSTYLE: resume ConstantName
044
045    /** A vector with all coordinates set to positive infinity. */
046    public static final Vector2D POSITIVE_INFINITY =
047        new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
048
049    /** A vector with all coordinates set to negative infinity. */
050    public static final Vector2D NEGATIVE_INFINITY =
051        new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
052
053    /** Serializable UID. */
054    private static final long serialVersionUID = 266938651998679754L;
055
056    /** Abscissa. */
057    private final double x;
058
059    /** Ordinate. */
060    private final double y;
061
062    /** Simple constructor.
063     * Build a vector from its coordinates
064     * @param x abscissa
065     * @param y ordinate
066     * @see #getX()
067     * @see #getY()
068     */
069    public Vector2D(double x, double y) {
070        this.x = x;
071        this.y = y;
072    }
073
074    /** Simple constructor.
075     * Build a vector from its coordinates
076     * @param v coordinates array
077     * @exception DimensionMismatchException if array does not have 2 elements
078     * @see #toArray()
079     */
080    public Vector2D(double[] v) throws DimensionMismatchException {
081        if (v.length != 2) {
082            throw new DimensionMismatchException(v.length, 2);
083        }
084        this.x = v[0];
085        this.y = v[1];
086    }
087
088    /** Multiplicative constructor
089     * Build a vector from another one and a scale factor.
090     * The vector built will be a * u
091     * @param a scale factor
092     * @param u base (unscaled) vector
093     */
094    public Vector2D(double a, Vector2D u) {
095        this.x = a * u.x;
096        this.y = a * u.y;
097    }
098
099    /** Linear constructor
100     * Build a vector from two other ones and corresponding scale factors.
101     * The vector built will be a1 * u1 + a2 * u2
102     * @param a1 first scale factor
103     * @param u1 first base (unscaled) vector
104     * @param a2 second scale factor
105     * @param u2 second base (unscaled) vector
106     */
107    public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2) {
108        this.x = a1 * u1.x + a2 * u2.x;
109        this.y = a1 * u1.y + a2 * u2.y;
110    }
111
112    /** Linear constructor
113     * Build a vector from three other ones and corresponding scale factors.
114     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
115     * @param a1 first scale factor
116     * @param u1 first base (unscaled) vector
117     * @param a2 second scale factor
118     * @param u2 second base (unscaled) vector
119     * @param a3 third scale factor
120     * @param u3 third base (unscaled) vector
121     */
122    public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
123                   double a3, Vector2D u3) {
124        this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
125        this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
126    }
127
128    /** Linear constructor
129     * Build a vector from four other ones and corresponding scale factors.
130     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
131     * @param a1 first scale factor
132     * @param u1 first base (unscaled) vector
133     * @param a2 second scale factor
134     * @param u2 second base (unscaled) vector
135     * @param a3 third scale factor
136     * @param u3 third base (unscaled) vector
137     * @param a4 fourth scale factor
138     * @param u4 fourth base (unscaled) vector
139     */
140    public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
141                   double a3, Vector2D u3, double a4, Vector2D u4) {
142        this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
143        this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y;
144    }
145
146    /** Get the abscissa of the vector.
147     * @return abscissa of the vector
148     * @see #Vector2D(double, double)
149     */
150    public double getX() {
151        return x;
152    }
153
154    /** Get the ordinate of the vector.
155     * @return ordinate of the vector
156     * @see #Vector2D(double, double)
157     */
158    public double getY() {
159        return y;
160    }
161
162    /** Get the vector coordinates as a dimension 2 array.
163     * @return vector coordinates
164     * @see #Vector2D(double[])
165     */
166    public double[] toArray() {
167        return new double[] { x, y };
168    }
169
170    /** {@inheritDoc} */
171    public Space getSpace() {
172        return Euclidean2D.getInstance();
173    }
174
175    /** {@inheritDoc} */
176    public Vector2D getZero() {
177        return ZERO;
178    }
179
180    /** {@inheritDoc} */
181    public double getNorm1() {
182        return FastMath.abs(x) + FastMath.abs(y);
183    }
184
185    /** {@inheritDoc} */
186    public double getNorm() {
187        return FastMath.sqrt (x * x + y * y);
188    }
189
190    /** {@inheritDoc} */
191    public double getNormSq() {
192        return x * x + y * y;
193    }
194
195    /** {@inheritDoc} */
196    public double getNormInf() {
197        return FastMath.max(FastMath.abs(x), FastMath.abs(y));
198    }
199
200    /** {@inheritDoc} */
201    public Vector2D add(Vector<Euclidean2D> v) {
202        Vector2D v2 = (Vector2D) v;
203        return new Vector2D(x + v2.getX(), y + v2.getY());
204    }
205
206    /** {@inheritDoc} */
207    public Vector2D add(double factor, Vector<Euclidean2D> v) {
208        Vector2D v2 = (Vector2D) v;
209        return new Vector2D(x + factor * v2.getX(), y + factor * v2.getY());
210    }
211
212    /** {@inheritDoc} */
213    public Vector2D subtract(Vector<Euclidean2D> p) {
214        Vector2D p3 = (Vector2D) p;
215        return new Vector2D(x - p3.x, y - p3.y);
216    }
217
218    /** {@inheritDoc} */
219    public Vector2D subtract(double factor, Vector<Euclidean2D> v) {
220        Vector2D v2 = (Vector2D) v;
221        return new Vector2D(x - factor * v2.getX(), y - factor * v2.getY());
222    }
223
224    /** {@inheritDoc} */
225    public Vector2D normalize() throws MathArithmeticException {
226        double s = getNorm();
227        if (s == 0) {
228            throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
229        }
230        return scalarMultiply(1 / s);
231    }
232
233    /** Compute the angular separation between two vectors.
234     * <p>This method computes the angular separation between two
235     * vectors using the dot product for well separated vectors and the
236     * cross product for almost aligned vectors. This allows to have a
237     * good accuracy in all cases, even for vectors very close to each
238     * other.</p>
239     * @param v1 first vector
240     * @param v2 second vector
241     * @return angular separation between v1 and v2
242     * @exception MathArithmeticException if either vector has a null norm
243     */
244    public static double angle(Vector2D v1, Vector2D v2) throws MathArithmeticException {
245
246        double normProduct = v1.getNorm() * v2.getNorm();
247        if (normProduct == 0) {
248            throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
249        }
250
251        double dot = v1.dotProduct(v2);
252        double threshold = normProduct * 0.9999;
253        if ((dot < -threshold) || (dot > threshold)) {
254            // the vectors are almost aligned, compute using the sine
255            final double n = FastMath.abs(MathArrays.linearCombination(v1.x, v2.y, -v1.y, v2.x));
256            if (dot >= 0) {
257                return FastMath.asin(n / normProduct);
258            }
259            return FastMath.PI - FastMath.asin(n / normProduct);
260        }
261
262        // the vectors are sufficiently separated to use the cosine
263        return FastMath.acos(dot / normProduct);
264
265    }
266
267    /** {@inheritDoc} */
268    public Vector2D negate() {
269        return new Vector2D(-x, -y);
270    }
271
272    /** {@inheritDoc} */
273    public Vector2D scalarMultiply(double a) {
274        return new Vector2D(a * x, a * y);
275    }
276
277    /** {@inheritDoc} */
278    public boolean isNaN() {
279        return Double.isNaN(x) || Double.isNaN(y);
280    }
281
282    /** {@inheritDoc} */
283    public boolean isInfinite() {
284        return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
285    }
286
287    /** {@inheritDoc} */
288    public double distance1(Vector<Euclidean2D> p) {
289        Vector2D p3 = (Vector2D) p;
290        final double dx = FastMath.abs(p3.x - x);
291        final double dy = FastMath.abs(p3.y - y);
292        return dx + dy;
293    }
294
295    /** {@inheritDoc}
296     */
297    public double distance(Vector<Euclidean2D> p) {
298        return distance((Point<Euclidean2D>) p);
299    }
300
301    /** {@inheritDoc} */
302    public double distance(Point<Euclidean2D> p) {
303        Vector2D p3 = (Vector2D) p;
304        final double dx = p3.x - x;
305        final double dy = p3.y - y;
306        return FastMath.sqrt(dx * dx + dy * dy);
307    }
308
309    /** {@inheritDoc} */
310    public double distanceInf(Vector<Euclidean2D> p) {
311        Vector2D p3 = (Vector2D) p;
312        final double dx = FastMath.abs(p3.x - x);
313        final double dy = FastMath.abs(p3.y - y);
314        return FastMath.max(dx, dy);
315    }
316
317    /** {@inheritDoc} */
318    public double distanceSq(Vector<Euclidean2D> p) {
319        Vector2D p3 = (Vector2D) p;
320        final double dx = p3.x - x;
321        final double dy = p3.y - y;
322        return dx * dx + dy * dy;
323    }
324
325    /** {@inheritDoc} */
326    public double dotProduct(final Vector<Euclidean2D> v) {
327        final Vector2D v2 = (Vector2D) v;
328        return MathArrays.linearCombination(x, v2.x, y, v2.y);
329    }
330
331    /**
332     * Compute the cross-product of the instance and the given points.
333     * <p>
334     * The cross product can be used to determine the location of a point
335     * with regard to the line formed by (p1, p2) and is calculated as:
336     * \[
337     *    P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1)
338     * \]
339     * with \(p3 = (x_3, y_3)\) being this instance.
340     * <p>
341     * If the result is 0, the points are collinear, i.e. lie on a single straight line L;
342     * if it is positive, this point lies to the left, otherwise to the right of the line
343     * formed by (p1, p2).
344     *
345     * @param p1 first point of the line
346     * @param p2 second point of the line
347     * @return the cross-product
348     *
349     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">Cross product (Wikipedia)</a>
350     */
351    public double crossProduct(final Vector2D p1, final Vector2D p2) {
352        final double x1 = p2.getX() - p1.getX();
353        final double y1 = getY() - p1.getY();
354        final double x2 = getX() - p1.getX();
355        final double y2 = p2.getY() - p1.getY();
356        return MathArrays.linearCombination(x1, y1, -x2, y2);
357    }
358
359    /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
360     * <p>Calling this method is equivalent to calling:
361     * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
362     * vector is built</p>
363     * @param p1 first vector
364     * @param p2 second vector
365     * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
366     */
367    public static double distance(Vector2D p1, Vector2D p2) {
368        return p1.distance(p2);
369    }
370
371    /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
372     * <p>Calling this method is equivalent to calling:
373     * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
374     * vector is built</p>
375     * @param p1 first vector
376     * @param p2 second vector
377     * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
378     */
379    public static double distanceInf(Vector2D p1, Vector2D p2) {
380        return p1.distanceInf(p2);
381    }
382
383    /** Compute the square of the distance between two vectors.
384     * <p>Calling this method is equivalent to calling:
385     * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
386     * vector is built</p>
387     * @param p1 first vector
388     * @param p2 second vector
389     * @return the square of the distance between p1 and p2
390     */
391    public static double distanceSq(Vector2D p1, Vector2D p2) {
392        return p1.distanceSq(p2);
393    }
394
395    /**
396     * Test for the equality of two 2D vectors.
397     * <p>
398     * If all coordinates of two 2D vectors are exactly the same, and none are
399     * <code>Double.NaN</code>, the two 2D vectors are considered to be equal.
400     * </p>
401     * <p>
402     * <code>NaN</code> coordinates are considered to affect globally the vector
403     * and be equals to each other - i.e, if either (or all) coordinates of the
404     * 2D vector are equal to <code>Double.NaN</code>, the 2D vector is equal to
405     * {@link #NaN}.
406     * </p>
407     *
408     * @param other Object to test for equality to this
409     * @return true if two 2D vector objects are equal, false if
410     *         object is null, not an instance of Vector2D, or
411     *         not equal to this Vector2D instance
412     *
413     */
414    @Override
415    public boolean equals(Object other) {
416
417        if (this == other) {
418            return true;
419        }
420
421        if (other instanceof Vector2D) {
422            final Vector2D rhs = (Vector2D)other;
423            if (rhs.isNaN()) {
424                return this.isNaN();
425            }
426
427            return (x == rhs.x) && (y == rhs.y);
428        }
429        return false;
430    }
431
432    /**
433     * Get a hashCode for the 2D vector.
434     * <p>
435     * All NaN values have the same hash code.</p>
436     *
437     * @return a hash code value for this object
438     */
439    @Override
440    public int hashCode() {
441        if (isNaN()) {
442            return 542;
443        }
444        return 122 * (76 * MathUtils.hash(x) +  MathUtils.hash(y));
445    }
446
447    /** Get a string representation of this vector.
448     * @return a string representation of this vector
449     */
450    @Override
451    public String toString() {
452        return Vector2DFormat.getInstance().format(this);
453    }
454
455    /** {@inheritDoc} */
456    public String toString(final NumberFormat format) {
457        return new Vector2DFormat(format).format(this);
458    }
459
460}