Speed3d.java

  1. package org.opentrafficsim.core.math;

  2. import java.io.Serializable;
  3. import java.util.Locale;

  4. import org.djunits.unit.DirectionUnit;
  5. import org.djunits.unit.SpeedUnit;
  6. import org.djunits.value.ValueRuntimeException;
  7. import org.djunits.value.storage.StorageType;
  8. import org.djunits.value.vdouble.scalar.Direction;
  9. import org.djunits.value.vdouble.scalar.Speed;
  10. import org.djunits.value.vdouble.vector.SpeedVector;
  11. import org.djunits.value.vdouble.vector.base.DoubleVector;

  12. /**
  13.  * A 3D speed vector, decomposed in X, Y, and Z-speed with easy conversion from and to a spherical coordinate system. <br>
  14.  * <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Physicists and mathematicians <strong>do not</strong>
  15.  * agree on the meaning of theta and phi.</a> In this class the convention in the physics domain is used:
  16.  * <ul>
  17.  * <li>theta is the angle from the z direction.</li>
  18.  * <li>phi is the projected angle in the xy-plane from the x direction.</li>
  19.  * </ul>
  20.  * N.B. In the geography domain yet another convention is used. <br>
  21.  * <p>
  22.  * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  23.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  24.  * </p>
  25.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  26.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  27.  */
  28. public class Speed3d implements Serializable
  29. {
  30.     /** */
  31.     private static final long serialVersionUID = 20160000L;

  32.     /** The speed in 3D (XYZ coded). */
  33.     private final SpeedVector speed;

  34.     /**
  35.      * Construct a new Speed3d from vector of strongly typed Cartesian coordinates.
  36.      * @param speed SpeedVector; the speeds in 3D (YPR coded)
  37.      * @throws ValueRuntimeException in case the vector does not have exactly three elements
  38.      */
  39.     public Speed3d(final SpeedVector speed) throws ValueRuntimeException
  40.     {
  41.         if (speed.size() != 3)
  42.         {
  43.             throw new ValueRuntimeException("Size of an RPY-speed vector should be exactly 3. Got: " + speed);
  44.         }
  45.         this.speed = speed;
  46.     }

  47.     /**
  48.      * Construct a new Speed3d from three strongly typed Cartesian coordinates.
  49.      * @param x Speed; the speed in the x-direction
  50.      * @param y Speed; the speed in the y-direction
  51.      * @param z Speed; the speed in the z-direction
  52.      * @throws ValueRuntimeException in case the units are incorrect
  53.      */
  54.     public Speed3d(final Speed x, final Speed y, final Speed z) throws ValueRuntimeException
  55.     {
  56.         this.speed = DoubleVector.instantiate(new Speed[] {x, y, z}, x.getDisplayUnit(), StorageType.DENSE);
  57.     }

  58.     /**
  59.      * Construct a new Speed3d from three double Cartesian coordinates and a speed unit.
  60.      * @param x double; the speed in the x-direction
  61.      * @param y double; the speed in the y-direction
  62.      * @param z double; the speed in the z-direction
  63.      * @param unit SpeedUnit; the unit of the xyz parameters
  64.      * @throws ValueRuntimeException in case the units are incorrect
  65.      */
  66.     public Speed3d(final double x, final double y, final double z, final SpeedUnit unit) throws ValueRuntimeException
  67.     {
  68.         this.speed = DoubleVector.instantiate(new double[] {x, y, z}, unit, StorageType.DENSE);
  69.     }

  70.     /**
  71.      * Construct a new Speed3d from a strongly typed speed and polar coordinates.
  72.      * @param speed Speed; the speed in the direction of the angle along the vector
  73.      * @param theta Direction; the angle from the z direction
  74.      * @param phi Direction; the projected angle in the xy-plane from the x direction
  75.      * @throws ValueRuntimeException in case the vector does not have exactly three elements
  76.      */
  77.     public Speed3d(final Speed speed, final Direction theta, final Direction phi) throws ValueRuntimeException
  78.     {
  79.         double[] xyz = Scalar3d.polarToCartesian(speed.getInUnit(), theta.si, phi.si);
  80.         this.speed = DoubleVector.instantiate(xyz, speed.getDisplayUnit(), StorageType.DENSE);
  81.     }

  82.     /**
  83.      * Retrieve the x-component of this Speed3d.
  84.      * @return the speed in the x-direction.
  85.      */
  86.     public final Speed getX()
  87.     {
  88.         try
  89.         {
  90.             return this.speed.get(0);
  91.         }
  92.         catch (ValueRuntimeException exception)
  93.         {
  94.             // should be impossible as we constructed the vector always with three elements
  95.             throw new RuntimeException(
  96.                     "getX() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
  97.         }
  98.     }

  99.     /**
  100.      * Retrieve the y-component of this Speed3d.
  101.      * @return the speed in the y-direction.
  102.      */
  103.     public final Speed getY()
  104.     {
  105.         try
  106.         {
  107.             return this.speed.get(1);
  108.         }
  109.         catch (ValueRuntimeException exception)
  110.         {
  111.             // should be impossible as we constructed the vector always with three elements
  112.             throw new RuntimeException(
  113.                     "getY() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
  114.         }
  115.     }

  116.     /**
  117.      * Retrieve the z-component of this Speed3d.
  118.      * @return the speed in the z-direction.
  119.      */
  120.     public final Speed getZ()
  121.     {
  122.         try
  123.         {
  124.             return this.speed.get(2);
  125.         }
  126.         catch (ValueRuntimeException exception)
  127.         {
  128.             // should be impossible as we constructed the vector always with three elements
  129.             throw new RuntimeException(
  130.                     "getZ() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
  131.         }
  132.     }

  133.     /**
  134.      * Retrieve the theta of this Speed3d.
  135.      * @return the angle of direction perpendicular to the xy-plane
  136.      */
  137.     public final Direction getTheta()
  138.     {
  139.         return Scalar3d.cartesianToTheta(getX().si, getY().si, getZ().si);
  140.     }

  141.     /**
  142.      * Retrieve the phi of this Speed3d.
  143.      * @return the projected angle of direction in the xy-plane
  144.      */
  145.     public final Direction getPhi()
  146.     {
  147.         return Scalar3d.cartesianToPhi(getX().si, getY().si);
  148.     }

  149.     /**
  150.      * Retrieve the norm of this Speed3d.
  151.      * @return the combined speed in the direction of the angle
  152.      */
  153.     public final Speed getSpeed()
  154.     {
  155.         return new Speed(Scalar3d.cartesianToRadius(getX().si, getY().si, getZ().si), SpeedUnit.SI);
  156.     }

  157.     /** {@inheritDoc} */
  158.     @Override
  159.     public final String toString()
  160.     {
  161.         return String.format(Locale.US, "Speed3d %s (%s, theta %s, phi %s)", this.speed, getSpeed(),
  162.                 new Direction(getTheta().getInUnit(DirectionUnit.EAST_DEGREE), DirectionUnit.EAST_DEGREE),
  163.                 new Direction(getPhi().getInUnit(DirectionUnit.EAST_DEGREE), DirectionUnit.EAST_DEGREE));
  164.     }

  165. }