OTSPoint3D.java

  1. package org.opentrafficsim.core.geometry;

  2. import java.awt.geom.Point2D;
  3. import java.io.Serializable;

  4. import javax.media.j3d.BoundingSphere;
  5. import javax.media.j3d.Bounds;
  6. import javax.vecmath.Point3d;

  7. import org.djunits.unit.LengthUnit;
  8. import org.djunits.value.vdouble.scalar.Length;

  9. import com.vividsolutions.jts.geom.Coordinate;
  10. import com.vividsolutions.jts.geom.Point;

  11. import nl.tudelft.simulation.dsol.animation.Locatable;
  12. import nl.tudelft.simulation.language.d3.CartesianPoint;
  13. import nl.tudelft.simulation.language.d3.DirectedPoint;

  14. /**
  15.  * An OTSPoint3D implements a 3D-coordinate for OTS. X, y and z are stored as doubles, but it is assumed that the scale is in SI
  16.  * units, i.e. in meters. A distance between two points is therefore also in meters.
  17.  * <p>
  18.  * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  19.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  20.  * <p>
  21.  * $LastChangedDate: 2015-07-16 10:20:53 +0200 (Thu, 16 Jul 2015) $, @version $Revision: 1124 $, by $Author: pknoppers $,
  22.  * initial version Jul 22, 2015 <br>
  23.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  24.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  25.  * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
  26.  */
  27. public class OTSPoint3D implements Locatable, Serializable
  28. {
  29.     /** */
  30.     private static final long serialVersionUID = 20150722L;

  31.     /** The internal representation of the point; x-coordinate. */
  32.     @SuppressWarnings("checkstyle:visibilitymodifier")
  33.     public final double x;

  34.     /** The internal representation of the point; y-coordinate. */
  35.     @SuppressWarnings("checkstyle:visibilitymodifier")
  36.     public final double y;

  37.     /** The internal representation of the point; z-coordinate. */
  38.     @SuppressWarnings("checkstyle:visibilitymodifier")
  39.     public final double z;

  40.     /**
  41.      * The x, y and z in the point are assumed to be in meters relative to an origin.
  42.      * @param x x-coordinate
  43.      * @param y y-coordinate
  44.      * @param z z-coordinate
  45.      */
  46.     public OTSPoint3D(final double x, final double y, final double z)
  47.     {
  48.         this.x = x;
  49.         this.y = y;
  50.         this.z = z;
  51.     }

  52.     /**
  53.      * @param xyz array with three elements; x, y and z are assumed to be in meters relative to an origin.
  54.      */
  55.     public OTSPoint3D(final double[] xyz)
  56.     {
  57.         this(xyz[0], xyz[1], (xyz.length > 2) ? xyz[2] : 0.0);
  58.     }

  59.     /**
  60.      * @param point a point to "clone".
  61.      */
  62.     public OTSPoint3D(final OTSPoint3D point)
  63.     {
  64.         this(point.x, point.y, point.z);
  65.     }

  66.     /**
  67.      * @param point javax.vecmath 3D double point; the x, y and z in the point are assumed to be in meters relative to an
  68.      *            origin.
  69.      */
  70.     public OTSPoint3D(final Point3d point)
  71.     {
  72.         this(point.x, point.y, point.z);
  73.     }

  74.     /**
  75.      * @param point javax.vecmath 3D double point; the x, y and z in the point are assumed to be in meters relative to an
  76.      *            origin.
  77.      */
  78.     public OTSPoint3D(final CartesianPoint point)
  79.     {
  80.         this(point.x, point.y, point.z);
  81.     }

  82.     /**
  83.      * @param point javax.vecmath 3D double point; the x, y and z in the point are assumed to be in meters relative to an
  84.      *            origin.
  85.      */
  86.     public OTSPoint3D(final DirectedPoint point)
  87.     {
  88.         this(point.x, point.y, point.z);
  89.     }

  90.     /**
  91.      * @param point2d java.awt 2D point, z-coordinate will be zero; the x and y in the point are assumed to be in meters
  92.      *            relative to an origin.
  93.      */
  94.     public OTSPoint3D(final Point2D point2d)
  95.     {
  96.         this(point2d.getX(), point2d.getY(), 0.0);
  97.     }

  98.     /**
  99.      * @param coordinate geotools coordinate; the x, y and z in the coordinate are assumed to be in meters relative to an
  100.      *            origin.
  101.      */
  102.     public OTSPoint3D(final Coordinate coordinate)
  103.     {
  104.         this(coordinate.x, coordinate.y, Double.isNaN(coordinate.z) ? 0.0 : coordinate.z);
  105.     }

  106.     /**
  107.      * @param point geotools point; z-coordinate will be zero; the x and y in the point are assumed to be in meters relative to
  108.      *            an origin.
  109.      */
  110.     public OTSPoint3D(final Point point)
  111.     {
  112.         this(point.getX(), point.getY(), 0.0);
  113.     }

  114.     /**
  115.      * The x and y in the point are assumed to be in meters relative to an origin. z will be set to 0.
  116.      * @param x x-coordinate
  117.      * @param y y-coordinate
  118.      */
  119.     public OTSPoint3D(final double x, final double y)
  120.     {
  121.         this(x, y, 0.0);
  122.     }

  123.     /**
  124.      * Interpolate (or extrapolate) between (outside) two given points.
  125.      * @param ratio double; 0 selects the zeroValue point, 1 selects the oneValue point, 0.5 selects a point halfway, etc.
  126.      * @param zeroValue OTSPoint3D; the point that is returned when ratio equals 0
  127.      * @param oneValue OTSPoint3D; the point that is returned when ratio equals 1
  128.      * @return OTSPoint3D
  129.      */
  130.     public static OTSPoint3D interpolate(final double ratio, final OTSPoint3D zeroValue, final OTSPoint3D oneValue)
  131.     {
  132.         double complement = 1 - ratio;
  133.         return new OTSPoint3D(complement * zeroValue.x + ratio * oneValue.x, complement * zeroValue.y + ratio * oneValue.y,
  134.                 complement * zeroValue.z + ratio * oneValue.z);
  135.     }

  136.     /**
  137.      * Compute the 2D intersection of two line segments. The Z-component of the lines is ignored. Both line segments are defined
  138.      * by two points (that should be distinct).
  139.      * @param line1P1 OTSPoint3D; first point of line segment 1
  140.      * @param line1P2 OTSPoint3D; second point of line segment 1
  141.      * @param line2P1 OTSPoint3D; first point of line segment 2
  142.      * @param line2P2 OTSPoint3D; second point of line segment 2
  143.      * @return OTSPoint3D; the intersection of the two lines, or null if the lines are (almost) parallel, or do not intersect
  144.      */
  145.     public static OTSPoint3D intersectionOfLineSegments(final OTSPoint3D line1P1, final OTSPoint3D line1P2,
  146.             final OTSPoint3D line2P1, final OTSPoint3D line2P2)
  147.     {
  148.         double denominator =
  149.                 (line2P2.y - line2P1.y) * (line1P2.x - line1P1.x) - (line2P2.x - line2P1.x) * (line1P2.y - line1P1.y);
  150.         if (denominator == 0f)
  151.         {
  152.             return null; // lines are parallel (they might even be on top of each other, but we don't check that)
  153.         }
  154.         double uA =
  155.                 ((line2P2.x - line2P1.x) * (line1P1.y - line2P1.y) - (line2P2.y - line2P1.y) * (line1P1.x - line2P1.x))
  156.                         / denominator;
  157.         if ((uA < 0f) || (uA > 1f))
  158.         {
  159.             return null; // intersection outside line 1
  160.         }
  161.         double uB =
  162.                 ((line1P2.x - line1P1.x) * (line1P1.y - line2P1.y) - (line1P2.y - line1P1.y) * (line1P1.x - line2P1.x))
  163.                         / denominator;
  164.         if (uB < 0 || uB > 1)
  165.         {
  166.             return null; // intersection outside line 2
  167.         }
  168.         return new OTSPoint3D(line1P1.x + uA * (line1P2.x - line1P1.x), line1P1.y + uA * (line1P2.y - line1P1.y), 0);
  169.     }

  170.     /**
  171.      * Compute the 2D intersection of two infinite lines. The Z-component of the lines is ignored. Both lines are defined by two
  172.      * points (that should be distinct).
  173.      * @param line1P1 OTSPoint3D; first point of line 1
  174.      * @param line1P2 OTSPoint3D; second point of line 1
  175.      * @param line2P1 OTSPoint3D; first point of line 2
  176.      * @param line2P2 OTSPoint3D; second point of line 2
  177.      * @return OTSPoint3D; the intersection of the two lines, or null if the lines are (almost) parallel
  178.      */
  179.     public static OTSPoint3D intersectionOfLines(final OTSPoint3D line1P1, final OTSPoint3D line1P2, final OTSPoint3D line2P1,
  180.             final OTSPoint3D line2P2)
  181.     {
  182.         double determinant =
  183.                 (line1P1.x - line1P2.x) * (line2P1.y - line2P2.y) - (line1P1.y - line1P2.y) * (line2P1.x - line2P2.x);
  184.         if (Math.abs(determinant) < 0.0000001)
  185.         {
  186.             return null;
  187.         }
  188.         return new OTSPoint3D(
  189.                 ((line1P1.x * line1P2.y - line1P1.y * line1P2.x) * (line2P1.x - line2P2.x) - (line1P1.x - line1P2.x)
  190.                         * (line2P1.x * line2P2.y - line2P1.y * line2P2.x))
  191.                         / determinant,
  192.                 ((line1P1.x * line1P2.y - line1P1.y * line1P2.x) * (line2P1.y - line2P2.y) - (line1P1.y - line1P2.y)
  193.                         * (line2P1.x * line2P2.y - line2P1.y * line2P2.x))
  194.                         / determinant);
  195.     }

  196.     /**
  197.      * Compute the distance to a line segment (2D; Z-component is ignored). If the projection of this point onto the line lies
  198.      * outside the line segment; the distance to nearest end point of the line segment is returned. Otherwise the distance to
  199.      * the line segment is returned. <br>
  200.      * Adapted from <a href="http://paulbourke.net/geometry/pointlineplane/DistancePoint.java"> example code provided by Paul
  201.      * Bourke</a>.
  202.      * @param segmentPoint1 OTSPoint3D; start of line segment
  203.      * @param segmentPoint2 OTSPoint3D; end of line segment
  204.      * @return double; the distance of this point to (one of the end points of the line segment)
  205.      */
  206.     // public final double horizontalDistanceToLineSegment(final OTSPoint3D segmentPoint1, final OTSPoint3D segmentPoint2)
  207.     // {
  208.     // return closestPointOnSegment(segmentPoint1, segmentPoint2).horizontalDistanceSI(this);
  209.     // }

  210.     /**
  211.      * Project a point on a line segment (2D - Z-component is ignored). If the the projected points lies outside the line
  212.      * segment, the nearest end point of the line segment is returned. Otherwise the returned point lies between the end points
  213.      * of the line segment. <br>
  214.      * Adapted from <a href="http://paulbourke.net/geometry/pointlineplane/DistancePoint.java">example code provided by Paul
  215.      * Bourke</a>.
  216.      * @param segmentPoint1 OTSPoint3D; start of line segment
  217.      * @param segmentPoint2 OTSPoint3D; end of line segment
  218.      * @return Point2D.Double; either <cite>lineP1</cite>, or <cite>lineP2</cite> or a new OTSPoint3D that lies somewhere in
  219.      *         between those two. The Z-component of the result matches the Z-component of the line segment at that point
  220.      */
  221.     public final OTSPoint3D closestPointOnSegment(final OTSPoint3D segmentPoint1, final OTSPoint3D segmentPoint2)
  222.     {
  223.         double dX = segmentPoint2.x - segmentPoint1.x;
  224.         double dY = segmentPoint2.y - segmentPoint1.y;
  225.         if ((0 == dX) && (0 == dY))
  226.         {
  227.             return segmentPoint1;
  228.         }
  229.         final double u = ((this.x - segmentPoint1.x) * dX + (this.y - segmentPoint1.y) * dY) / (dX * dX + dY * dY);
  230.         if (u < 0)
  231.         {
  232.             return segmentPoint1;
  233.         }
  234.         else if (u > 1)
  235.         {
  236.             return segmentPoint2;
  237.         }
  238.         else
  239.         {
  240.             return interpolate(u, segmentPoint1, segmentPoint2);
  241.             // WAS new OTSPoint3D(segmentPoint1.x + u * dX, segmentPoint1.y + u * dY); // could use interpolate in stead
  242.         }
  243.     }

  244.     /**
  245.      * Return the closest point on an OTSLine3D.
  246.      * @param line OTSLine3D; the line
  247.      * @param useHorizontalDistance boolean; if true; the horizontal distance is used to determine the closest point; if false;
  248.      *            the 3D distance is used to determine the closest point
  249.      * @return OTSPoint3D; the Z component of the returned point matches the Z-component of hte line at that point
  250.      */
  251.     private OTSPoint3D internalClosestPointOnLine(final OTSLine3D line, final boolean useHorizontalDistance)
  252.     {
  253.         OTSPoint3D prevPoint = null;
  254.         double distance = Double.MAX_VALUE;
  255.         OTSPoint3D result = null;
  256.         for (OTSPoint3D nextPoint : line.getPoints())
  257.         {
  258.             if (null != prevPoint)
  259.             {
  260.                 OTSPoint3D closest = closestPointOnSegment(prevPoint, nextPoint);
  261.                 double thisDistance = useHorizontalDistance ? horizontalDistanceSI(closest) : distanceSI(closest);
  262.                 if (thisDistance < distance)
  263.                 {
  264.                     result = closest;
  265.                     distance = thisDistance;
  266.                 }
  267.             }
  268.             prevPoint = nextPoint;
  269.         }
  270.         return result;
  271.     }

  272.     /**
  273.      * Return the closest point on an OTSLine3D. This method takes the Z-component of this point and the line into account.
  274.      * @param line OTSLine3D; the line
  275.      * @return OTSPoint3D; the Z-component of the returned point matches the Z-component of the line at that point
  276.      */
  277.     public final OTSPoint3D closestPointOnLine(final OTSLine3D line)
  278.     {
  279.         return internalClosestPointOnLine(line, false);
  280.     }

  281.     /**
  282.      * Return the closest point on an OTSLine3D. This method ignores the Z-component of this point and the line when computing
  283.      * the distance.
  284.      * @param line OTSLine3D; the line
  285.      * @return OTSPoint3D; the Z-component of the returned point matches the Z-component of the line at that point
  286.      */
  287.     public final OTSPoint3D closestPointOnLine2D(final OTSLine3D line)
  288.     {
  289.         return internalClosestPointOnLine(line, true);
  290.     }

  291.     /**
  292.      * @param point the point to which the distance has to be calculated.
  293.      * @return the distance in 3D according to Pythagoras, expressed in the SI unit for length (meter)
  294.      */
  295.     public final double distanceSI(final OTSPoint3D point)
  296.     {
  297.         double dx = point.x - this.x;
  298.         double dy = point.y - this.y;
  299.         double dz = point.z - this.z;

  300.         return Math.sqrt(dx * dx + dy * dy + dz * dz);
  301.     }

  302.     /**
  303.      * @param point the point to which the distance has to be calculated.
  304.      * @return the distance in 3D according to Pythagoras, expressed in the SI unit for length (meter)
  305.      */
  306.     public final double horizontalDistanceSI(final OTSPoint3D point)
  307.     {
  308.         double dx = point.x - this.x;
  309.         double dy = point.y - this.y;

  310.         return Math.sqrt(dx * dx + dy * dy);
  311.     }

  312.     /**
  313.      * @param point the point to which the distance has to be calculated.
  314.      * @return the distance in 3D according to Pythagoras
  315.      */
  316.     public final Length horizontalDistance(final OTSPoint3D point)
  317.     {
  318.         return new Length(horizontalDistanceSI(point), LengthUnit.SI);
  319.     }

  320.     /**
  321.      * @param point the point to which the distance has to be calculated.
  322.      * @return the distance in 3D according to Pythagoras
  323.      */
  324.     public final Length distance(final OTSPoint3D point)
  325.     {
  326.         return new Length(distanceSI(point), LengthUnit.SI);
  327.     }

  328.     /**
  329.      * @return the equivalent geotools Coordinate of this point.
  330.      */
  331.     public final Coordinate getCoordinate()
  332.     {
  333.         return new Coordinate(this.x, this.y, this.z);
  334.     }

  335.     /**
  336.      * @return the equivalent DSOL DirectedPoint of this point. Should the result be cached?
  337.      */
  338.     public final DirectedPoint getDirectedPoint()
  339.     {
  340.         return new DirectedPoint(this.x, this.y, this.z);
  341.     }

  342.     /**
  343.      * @return a Point2D with the x and y structure.
  344.      */
  345.     public final Point2D getPoint2D()
  346.     {
  347.         return new Point2D.Double(this.x, this.y);
  348.     }

  349.     /** {@inheritDoc} */
  350.     @Override
  351.     public final DirectedPoint getLocation()
  352.     {
  353.         return getDirectedPoint();
  354.     }

  355.     /**
  356.      * This method returns a sphere with a diameter of half a meter as the default bounds for a point. {@inheritDoc}
  357.      */
  358.     @Override
  359.     public final Bounds getBounds()
  360.     {
  361.         return new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 0.5);
  362.     }

  363.     /** {@inheritDoc} */
  364.     @Override
  365.     @SuppressWarnings("checkstyle:designforextension")
  366.     public String toString()
  367.     {
  368.         return String.format("(%.3f,%.3f,%.3f)", this.x, this.y, this.z);
  369.     }

  370.     /** {@inheritDoc} */
  371.     @Override
  372.     @SuppressWarnings("checkstyle:designforextension")
  373.     public int hashCode()
  374.     {
  375.         final int prime = 31;
  376.         int result = 1;
  377.         long temp;
  378.         temp = Double.doubleToLongBits(this.x);
  379.         result = prime * result + (int) (temp ^ (temp >>> 32));
  380.         temp = Double.doubleToLongBits(this.y);
  381.         result = prime * result + (int) (temp ^ (temp >>> 32));
  382.         temp = Double.doubleToLongBits(this.z);
  383.         result = prime * result + (int) (temp ^ (temp >>> 32));
  384.         return result;
  385.     }

  386.     /** {@inheritDoc} */
  387.     @Override
  388.     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
  389.     public boolean equals(final Object obj)
  390.     {
  391.         if (this == obj)
  392.             return true;
  393.         if (obj == null)
  394.             return false;
  395.         if (getClass() != obj.getClass())
  396.             return false;
  397.         OTSPoint3D other = (OTSPoint3D) obj;
  398.         if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x))
  399.             return false;
  400.         if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y))
  401.             return false;
  402.         if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z))
  403.             return false;
  404.         return true;
  405.     }

  406. }