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}