View Javadoc
1   package org.opentrafficsim.core.math;
2   
3   import java.io.Serializable;
4   import java.util.Locale;
5   
6   import org.djunits.unit.DirectionUnit;
7   import org.djunits.unit.SpeedUnit;
8   import org.djunits.value.ValueRuntimeException;
9   import org.djunits.value.storage.StorageType;
10  import org.djunits.value.vdouble.scalar.Direction;
11  import org.djunits.value.vdouble.scalar.Speed;
12  import org.djunits.value.vdouble.vector.SpeedVector;
13  import org.djunits.value.vdouble.vector.base.DoubleVector;
14  
15  /**
16   * A 3D speed vector, decomposed in X, Y, and Z-speed with easy conversion from and to a spherical coordinate system. <br>
17   * <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Physicists and mathematicians <strong>do not</strong>
18   * agree on the meaning of theta and phi.</a> In this class the convention in the physics domain is used:
19   * <ul>
20   * <li>theta is the angle from the z direction.</li>
21   * <li>phi is the projected angle in the xy-plane from the x direction.</li>
22   * </ul>
23   * N.B. In the geography domain yet another convention is used. <br>
24   * <p>
25   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * </p>
28   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
29   * initial version Dec 10, 2015 <br>
30   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
32   */
33  public class Speed3D implements Serializable
34  {
35      /** */
36      private static final long serialVersionUID = 20160000L;
37  
38      /** The speed in 3D (XYZ coded). */
39      private final SpeedVector speed;
40  
41      /**
42       * Construct a new Speed3D from vector of strongly typed Cartesian coordinates.
43       * @param speed SpeedVector; the speeds in 3D (YPR coded)
44       * @throws ValueRuntimeException in case the vector does not have exactly three elements
45       */
46      public Speed3D(final SpeedVector speed) throws ValueRuntimeException
47      {
48          if (speed.size() != 3)
49          {
50              throw new ValueRuntimeException("Size of an RPY-speed vector should be exactly 3. Got: " + speed);
51          }
52          this.speed = speed;
53      }
54  
55      /**
56       * Construct a new Speed3D from three strongly typed Cartesian coordinates.
57       * @param x Speed; the speed in the x-direction
58       * @param y Speed; the speed in the y-direction
59       * @param z Speed; the speed in the z-direction
60       * @throws ValueRuntimeException in case the units are incorrect
61       */
62      public Speed3D(final Speed x, final Speed y, final Speed z) throws ValueRuntimeException
63      {
64          this.speed = DoubleVector.instantiate(new Speed[] {x, y, z}, x.getDisplayUnit(), StorageType.DENSE);
65      }
66  
67      /**
68       * Construct a new Speed3D from three double Cartesian coordinates and a speed unit.
69       * @param x double; the speed in the x-direction
70       * @param y double; the speed in the y-direction
71       * @param z double; the speed in the z-direction
72       * @param unit SpeedUnit; the unit of the xyz parameters
73       * @throws ValueRuntimeException in case the units are incorrect
74       */
75      public Speed3D(final double x, final double y, final double z, final SpeedUnit unit) throws ValueRuntimeException
76      {
77          this.speed = DoubleVector.instantiate(new double[] {x, y, z}, unit, StorageType.DENSE);
78      }
79  
80      /**
81       * Construct a new Speed3D from a strongly typed speed and polar coordinates.
82       * @param speed Speed; the speed in the direction of the angle along the vector
83       * @param theta Direction; the angle from the z direction
84       * @param phi Direction; the projected angle in the xy-plane from the x direction
85       * @throws ValueRuntimeException in case the vector does not have exactly three elements
86       */
87      public Speed3D(final Speed speed, final Direction theta, final Direction phi) throws ValueRuntimeException
88      {
89          double[] xyz = Scalar3D.polarToCartesian(speed.getInUnit(), theta.si, phi.si);
90          this.speed = DoubleVector.instantiate(xyz, speed.getDisplayUnit(), StorageType.DENSE);
91      }
92  
93      /**
94       * Retrieve the x-component of this Speed3D.
95       * @return the speed in the x-direction.
96       */
97      public final Speed getX()
98      {
99          try
100         {
101             return this.speed.get(0);
102         }
103         catch (ValueRuntimeException exception)
104         {
105             // should be impossible as we constructed the vector always with three elements
106             throw new RuntimeException(
107                     "getX() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
108         }
109     }
110 
111     /**
112      * Retrieve the y-component of this Speed3D.
113      * @return the speed in the y-direction.
114      */
115     public final Speed getY()
116     {
117         try
118         {
119             return this.speed.get(1);
120         }
121         catch (ValueRuntimeException exception)
122         {
123             // should be impossible as we constructed the vector always with three elements
124             throw new RuntimeException(
125                     "getY() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
126         }
127     }
128 
129     /**
130      * Retrieve the z-component of this Speed3D.
131      * @return the speed in the z-direction.
132      */
133     public final Speed getZ()
134     {
135         try
136         {
137             return this.speed.get(2);
138         }
139         catch (ValueRuntimeException exception)
140         {
141             // should be impossible as we constructed the vector always with three elements
142             throw new RuntimeException(
143                     "getZ() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
144         }
145     }
146 
147     /**
148      * Retrieve the theta of this Speed3D.
149      * @return the angle of direction perpendicular to the xy-plane
150      */
151     public final Direction getTheta()
152     {
153         return Scalar3D.cartesianToTheta(getX().si, getY().si, getZ().si);
154     }
155 
156     /**
157      * Retrieve the phi of this Speed3D.
158      * @return the projected angle of direction in the xy-plane
159      */
160     public final Direction getPhi()
161     {
162         return Scalar3D.cartesianToPhi(getX().si, getY().si);
163     }
164 
165     /**
166      * Retrieve the norm of this Speed3D.
167      * @return the combined speed in the direction of the angle
168      */
169     public final Speed getSpeed()
170     {
171         return new Speed(Scalar3D.cartesianToRadius(getX().si, getY().si, getZ().si), SpeedUnit.SI);
172     }
173 
174     /** {@inheritDoc} */
175     @Override
176     public final String toString()
177     {
178         return String.format(Locale.US, "Speed3D %s (%s, theta %s, phi %s)", this.speed, getSpeed(),
179                 new Direction(getTheta().getInUnit(DirectionUnit.EAST_DEGREE), DirectionUnit.EAST_DEGREE),
180                 new Direction(getPhi().getInUnit(DirectionUnit.EAST_DEGREE), DirectionUnit.EAST_DEGREE));
181     }
182 
183 }