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