Line.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.geometry.euclidean.threed;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.geometry.euclidean.oned.Euclidean1D;
  25. import org.hipparchus.geometry.euclidean.oned.IntervalsSet;
  26. import org.hipparchus.geometry.euclidean.oned.Vector1D;
  27. import org.hipparchus.geometry.partitioning.Embedding;
  28. import org.hipparchus.util.FastMath;
  29. import org.hipparchus.util.Precision;

  30. /** The class represent lines in a three dimensional space.

  31.  * <p>Each oriented line is intrinsically associated with an abscissa
  32.  * which is a coordinate on the line. The point at abscissa 0 is the
  33.  * orthogonal projection of the origin on the line, another equivalent
  34.  * way to express this is to say that it is the point of the line
  35.  * which is closest to the origin. Abscissa increases in the line
  36.  * direction.</p>
  37.  *
  38.  * @see #fromDirection(Vector3D, Vector3D, double)
  39.  * @see #Line(Vector3D, Vector3D, double)
  40.  */
  41. public class Line implements Embedding<Euclidean3D, Vector3D, Euclidean1D, Vector1D> {

  42.     /** Line direction. */
  43.     private Vector3D direction;

  44.     /** Line point closest to the origin. */
  45.     private Vector3D zero;

  46.     /** Tolerance below which points are considered identical. */
  47.     private final double tolerance;

  48.     /** Build a line from two points.
  49.      * @param p1 first point belonging to the line (this can be any point)
  50.      * @param p2 second point belonging to the line (this can be any point, different from p1)
  51.      * @param tolerance tolerance below which points are considered identical
  52.      * @exception MathIllegalArgumentException if the points are equal
  53.      * @see #fromDirection(Vector3D, Vector3D, double)
  54.      */
  55.     public Line(final Vector3D p1, final Vector3D p2, final double tolerance)
  56.         throws MathIllegalArgumentException {
  57.         this(tolerance);
  58.         reset(p1, p2);
  59.     }

  60.     /** Copy constructor.
  61.      * <p>The created instance is completely independent from the
  62.      * original instance, it is a deep copy.</p>
  63.      * @param line line to copy
  64.      */
  65.     public Line(final Line line) {
  66.         this(line.tolerance);
  67.         this.direction = line.direction;
  68.         this.zero      = line.zero;
  69.     }

  70.     /**
  71.      * Private constructor. Just sets the tolerance.
  72.      *
  73.      * @param tolerance below which points are considered identical.
  74.      */
  75.     private Line(final double tolerance) {
  76.         this.tolerance = tolerance;
  77.     }

  78.     /**
  79.      * Create a line from a point and a direction. Line = {@code point} + t * {@code
  80.      * direction}, where t is any real number.
  81.      *
  82.      * @param point     on the line. Can be any point.
  83.      * @param direction of the line. Must not be the zero vector.
  84.      * @param tolerance below which points are considered identical.
  85.      * @return a new Line with the given point and direction.
  86.      * @throws MathIllegalArgumentException if {@code direction} is the zero vector.
  87.      * @see #Line(Vector3D, Vector3D, double)
  88.      */
  89.     public static Line fromDirection(final Vector3D point,
  90.                                      final Vector3D direction,
  91.                                      final double tolerance) {
  92.         final Line line = new Line(tolerance);
  93.         line.resetWithDirection(point, direction);
  94.         return line;
  95.     }

  96.     /** Reset the instance as if built from two points.
  97.      * @param p1 first point belonging to the line (this can be any point)
  98.      * @param p2 second point belonging to the line (this can be any point, different from p1)
  99.      * @exception MathIllegalArgumentException if the points are equal
  100.      */
  101.     public void reset(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
  102.         resetWithDirection(p1, p2.subtract(p1));
  103.     }

  104.     /**
  105.      * Reset the instance as if built from a point and direction.
  106.      *
  107.      * @param p1    point belonging to the line (this can be any point).
  108.      * @param delta direction of the line.
  109.      * @throws MathIllegalArgumentException if {@code delta} is the zero vector.
  110.      */
  111.     private void resetWithDirection(final Vector3D p1, final Vector3D delta) {
  112.         final double norm2 = delta.getNormSq();
  113.         if (norm2 == 0.0) {
  114.             throw new MathIllegalArgumentException(LocalizedCoreFormats.ZERO_NORM);
  115.         }
  116.         this.direction = new Vector3D(1.0 / FastMath.sqrt(norm2), delta);
  117.         zero = new Vector3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta);
  118.     }

  119.     /** Get the tolerance below which points are considered identical.
  120.      * @return tolerance below which points are considered identical
  121.      */
  122.     public double getTolerance() {
  123.         return tolerance;
  124.     }

  125.     /** Get a line with reversed direction.
  126.      * @return a new instance, with reversed direction
  127.      */
  128.     public Line revert() {
  129.         final Line reverted = new Line(this);
  130.         reverted.direction = reverted.direction.negate();
  131.         return reverted;
  132.     }

  133.     /** Get the normalized direction vector.
  134.      * @return normalized direction vector
  135.      */
  136.     public Vector3D getDirection() {
  137.         return direction;
  138.     }

  139.     /** Get the line point closest to the origin.
  140.      * @return line point closest to the origin
  141.      */
  142.     public Vector3D getOrigin() {
  143.         return zero;
  144.     }

  145.     /** Get the abscissa of a point with respect to the line.
  146.      * <p>The abscissa is 0 if the projection of the point and the
  147.      * projection of the frame origin on the line are the same
  148.      * point.</p>
  149.      * @param point point to check
  150.      * @return abscissa of the point
  151.      */
  152.     public double getAbscissa(final Vector3D point) {
  153.         return point.subtract(zero).dotProduct(direction);
  154.     }

  155.     /** Get one point from the line.
  156.      * @param abscissa desired abscissa for the point
  157.      * @return one point belonging to the line, at specified abscissa
  158.      */
  159.     public Vector3D pointAt(final double abscissa) {
  160.         return new Vector3D(1.0, zero, abscissa, direction);
  161.     }

  162.     /** {@inheritDoc}
  163.      * @see #getAbscissa(Vector3D)
  164.      */
  165.     @Override
  166.     public Vector1D toSubSpace(final Vector3D point) {
  167.         return new Vector1D(getAbscissa(point));
  168.     }

  169.     /** {@inheritDoc}
  170.      * @see #pointAt(double)
  171.      */
  172.     @Override
  173.     public Vector3D toSpace(final Vector1D point) {
  174.         return pointAt(point.getX());
  175.     }

  176.     /** Check if the instance is similar to another line.
  177.      * <p>Lines are considered similar if they contain the same
  178.      * points. This does not mean they are equal since they can have
  179.      * opposite directions.</p>
  180.      * @param line line to which instance should be compared
  181.      * @return true if the lines are similar
  182.      */
  183.     public boolean isSimilarTo(final Line line) {
  184.         final double angle = Vector3D.angle(direction, line.direction);
  185.         return ((angle < tolerance) || (angle > (FastMath.PI - tolerance))) && contains(line.zero);
  186.     }

  187.     /** Check if the instance contains a point.
  188.      * @param p point to check
  189.      * @return true if p belongs to the line
  190.      */
  191.     public boolean contains(final Vector3D p) {
  192.         return distance(p) < tolerance;
  193.     }

  194.     /** Compute the distance between the instance and a point.
  195.      * @param p to check
  196.      * @return distance between the instance and the point
  197.      */
  198.     public double distance(final Vector3D p) {
  199.         final Vector3D d = p.subtract(zero);
  200.         final Vector3D n = new Vector3D(1.0, d, -d.dotProduct(direction), direction);
  201.         return n.getNorm();
  202.     }

  203.     /** Compute the shortest distance between the instance and another line.
  204.      * @param line line to check against the instance
  205.      * @return shortest distance between the instance and the line
  206.      */
  207.     public double distance(final Line line) {

  208.         final Vector3D normal = Vector3D.crossProduct(direction, line.direction);
  209.         final double n = normal.getNorm();
  210.         if (n < Precision.SAFE_MIN) {
  211.             // lines are parallel
  212.             return distance(line.zero);
  213.         }

  214.         // signed separation of the two parallel planes that contains the lines
  215.         final double offset = line.zero.subtract(zero).dotProduct(normal) / n;

  216.         return FastMath.abs(offset);

  217.     }

  218.     /** Compute the point of the instance closest to another line.
  219.      * @param line line to check against the instance
  220.      * @return point of the instance closest to another line
  221.      */
  222.     public Vector3D closestPoint(final Line line) {

  223.         final double cos = direction.dotProduct(line.direction);
  224.         final double n = 1 - cos * cos;
  225.         if (n < Precision.EPSILON) {
  226.             // the lines are parallel
  227.             return zero;
  228.         }

  229.         final Vector3D delta0 = line.zero.subtract(zero);
  230.         final double a        = delta0.dotProduct(direction);
  231.         final double b        = delta0.dotProduct(line.direction);

  232.         return new Vector3D(1, zero, (a - b * cos) / n, direction);

  233.     }

  234.     /** Get the intersection point of the instance and another line.
  235.      * @param line other line
  236.      * @return intersection point of the instance and the other line
  237.      * or null if there are no intersection points
  238.      */
  239.     public Vector3D intersection(final Line line) {
  240.         final Vector3D closest = closestPoint(line);
  241.         return line.contains(closest) ? closest : null;
  242.     }

  243.     /** Build a sub-line covering the whole line.
  244.      * @return a sub-line covering the whole line
  245.      */
  246.     public SubLine wholeLine() {
  247.         return new SubLine(this, new IntervalsSet(tolerance));
  248.     }

  249. }