001/*
002 * Shredzone Commons - suncalc
003 *
004 * Copyright (C) 2017 Richard "Shred" Körber
005 *   http://commons.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.commons.suncalc.util;
015
016import static java.lang.Math.*;
017import static org.shredzone.commons.suncalc.util.ExtendedMath.PI2;
018import static org.shredzone.commons.suncalc.util.ExtendedMath.isZero;
019
020import edu.umd.cs.findbugs.annotations.Nullable;
021
022/**
023 * A three dimensional vector.
024 * <p>
025 * Objects are is immutable and threadsafe.
026 */
027public class Vector {
028
029    private final double x;
030    private final double y;
031    private final double z;
032    private final Polar polar = new Polar();
033
034    /**
035     * Creates a new {@link Vector} of the given cartesian coordinates.
036     *
037     * @param x
038     *            X coordinate
039     * @param y
040     *            Y coordinate
041     * @param z
042     *            Z coordinate
043     */
044    public Vector(double x, double y, double z) {
045        this.x = x;
046        this.y = y;
047        this.z = z;
048    }
049
050    /**
051     * Creates a new {@link Vector} of the given cartesian coordinates.
052     *
053     * @param d
054     *            Array of coordinates, must have 3 elements
055     */
056    public Vector(double[] d) {
057        if (d.length != 3) {
058            throw new IllegalArgumentException("invalid vector length");
059        }
060        this.x = d[0];
061        this.y = d[1];
062        this.z = d[2];
063    }
064
065    /**
066     * Creates a new {@link Vector} of the given polar coordinates, with a radial distance
067     * of 1.
068     *
069     * @param φ
070     *            Azimuthal Angle
071     * @param θ
072     *            Polar Angle
073     * @return Created {@link Vector}
074     */
075    public static Vector ofPolar(double φ, double θ) {
076        return ofPolar(φ, θ, 1.0);
077    }
078
079    /**
080     * Creates a new {@link Vector} of the given polar coordinates.
081     *
082     * @param φ
083     *            Azimuthal Angle
084     * @param θ
085     *            Polar Angle
086     * @param r
087     *            Radial Distance
088     * @return Created {@link Vector}
089     */
090    public static Vector ofPolar(double φ, double θ, double r) {
091        double cosθ = cos(θ);
092        Vector result = new Vector(
093            r * cos(φ) * cosθ,
094            r * sin(φ) * cosθ,
095            r *          sin(θ)
096        );
097        result.polar.setPolar(φ, θ, r);
098        return result;
099    }
100
101    /**
102     * Returns the cartesian X coordinate.
103     */
104    public double getX() {
105        return x;
106    }
107
108    /**
109     * Returns the cartesian Y coordinate.
110     */
111    public double getY() {
112        return y;
113    }
114
115    /**
116     * Returns the cartesian Z coordinate.
117     */
118    public double getZ() {
119        return z;
120    }
121
122    /**
123     * Returns the azimuthal angle (φ) in radians.
124     */
125    public double getPhi() {
126        return polar.getPhi();
127    }
128
129    /**
130     * Returns the polar angle (θ) in radians.
131     */
132    public double getTheta() {
133        return polar.getTheta();
134    }
135
136    /**
137     * Returns the polar radial distance (r).
138     */
139    public double getR() {
140        return polar.getR();
141    }
142
143    /**
144     * Returns a {@link Vector} that is the sum of this {@link Vector} and the given
145     * {@link Vector}.
146     *
147     * @param vec
148     *            {@link Vector} to add
149     * @return Resulting {@link Vector}
150     */
151    public Vector add(Vector vec) {
152        return new Vector(
153            x + vec.x,
154            y + vec.y,
155            z + vec.z
156        );
157    }
158
159    /**
160     * Returns a {@link Vector} that is the difference of this {@link Vector} and the
161     * given {@link Vector}.
162     *
163     * @param vec
164     *            {@link Vector} to subtract
165     * @return Resulting {@link Vector}
166     */
167    public Vector subtract(Vector vec) {
168        return new Vector(
169            x - vec.x,
170            y - vec.y,
171            z - vec.z
172        );
173    }
174
175    /**
176     * Returns a {@link Vector} that is the scalar product of this {@link Vector} and the
177     * given scalar.
178     *
179     * @param scalar
180     *            Scalar to multiply
181     * @return Resulting {@link Vector}
182     */
183    public Vector multiply(double scalar) {
184        return new Vector(
185            x * scalar,
186            y * scalar,
187            z * scalar
188        );
189    }
190
191    /**
192     * Returns the negation of this {@link Vector}.
193     *
194     * @return Resulting {@link Vector}
195     */
196    public Vector negate() {
197        return new Vector(
198            -x,
199            -y,
200            -z
201        );
202    }
203
204    /**
205     * Returns a {@link Vector} that is the cross product of this {@link Vector} and the
206     * given {@link Vector}.
207     *
208     * @param right
209     *            {@link Vector} to multiply
210     * @return Resulting {@link Vector}
211     */
212    public Vector cross(Vector right) {
213        return new Vector(
214            y * right.z - z * right.y,
215            z * right.x - x * right.z,
216            x * right.y - y * right.x
217        );
218    }
219
220    /**
221     * Returns the dot product of this {@link Vector} and the given {@link Vector}.
222     *
223     * @param right
224     *            {@link Vector} to multiply
225     * @return Resulting dot product
226     */
227    public double dot(Vector right) {
228        return x * right.x + y * right.y + z * right.z;
229    }
230
231    /**
232     * Returns the norm of this {@link Vector}.
233     *
234     * @return Norm of this vector
235     */
236    public double norm() {
237        return sqrt(dot(this));
238    }
239
240    @Override
241    public boolean equals(Object obj) {
242        if (obj == null || !(obj instanceof Vector)) {
243            return false;
244        }
245
246        Vector vec = (Vector) obj;
247        return Double.compare(x, vec.x) == 0
248            && Double.compare(y, vec.y) == 0
249            && Double.compare(z, vec.z) == 0;
250    }
251
252    @Override
253    public int hashCode() {
254        return Double.valueOf(x).hashCode()
255            ^  Double.valueOf(y).hashCode()
256            ^  Double.valueOf(z).hashCode();
257    }
258
259    @Override
260    public String toString() {
261        return "(x=" + x + ", y=" + y + ", z=" + z + ")";
262    }
263
264    /**
265     * Helper class for lazily computing the polar coordinates in an immutable Vector
266     * object.
267     */
268    private class Polar {
269        private @Nullable Double φ = null;
270        private @Nullable Double θ = null;
271        private @Nullable Double r = null;
272
273        /**
274         * Sets polar coordinates.
275         *
276         * @param φ
277         *            Phi
278         * @param θ
279         *            Theta
280         * @param r
281         *            R
282         */
283        public synchronized void setPolar(double φ, double θ, double r) {
284            this.φ = φ;
285            this.θ = θ;
286            this.r = r;
287        }
288
289        public synchronized double getPhi() {
290            if (φ == null) {
291                if (isZero(x) && isZero(y)) {
292                    φ = 0.0;
293                } else {
294                    φ = atan2(y, x);
295                }
296
297                if (φ < 0.0) {
298                    φ += PI2;
299                }
300            }
301            return φ;
302        }
303
304        public synchronized double getTheta() {
305            if (θ == null) {
306                double ρSqr = x * x + y * y;
307
308                if (isZero(z) && isZero(ρSqr)) {
309                    θ = 0.0;
310                } else {
311                    θ = atan2(z, sqrt(ρSqr));
312                }
313            }
314            return θ;
315        }
316
317        public synchronized double getR() {
318            if (r == null) {
319                r = sqrt(x * x + y * y + z * z);
320            }
321            return r;
322        }
323    }
324
325}