FieldLine.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.CalculusFieldElement;
  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.util.FastMath;
  26. import org.hipparchus.util.Precision;

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

  28.  * <p>Each oriented line is intrinsically associated with an abscissa
  29.  * which is a coordinate on the line. The point at abscissa 0 is the
  30.  * orthogonal projection of the origin on the line, another equivalent
  31.  * way to express this is to say that it is the point of the line
  32.  * which is closest to the origin. Abscissa increases in the line
  33.  * direction.</p>
  34.  * @param <T> the type of the field elements
  35.  */
  36. public class FieldLine<T extends CalculusFieldElement<T>> {

  37.     /** Line direction. */
  38.     private FieldVector3D<T> direction;

  39.     /** Line point closest to the origin. */
  40.     private FieldVector3D<T> zero;

  41.     /** Tolerance below which points are considered identical. */
  42.     private final double tolerance;

  43.     /** Build a line from two points.
  44.      * @param p1 first point belonging to the line (this can be any point)
  45.      * @param p2 second point belonging to the line (this can be any point, different from p1)
  46.      * @param tolerance tolerance below which points are considered identical
  47.      * @exception MathIllegalArgumentException if the points are equal
  48.      */
  49.     public FieldLine(final FieldVector3D<T> p1, final FieldVector3D<T> p2, final double tolerance)
  50.         throws MathIllegalArgumentException {
  51.         reset(p1, p2);
  52.         this.tolerance = tolerance;
  53.     }

  54.     /** Copy constructor.
  55.      * <p>The created instance is completely independent from the
  56.      * original instance, it is a deep copy.</p>
  57.      * @param line line to copy
  58.      */
  59.     public FieldLine(final FieldLine<T> line) {
  60.         this.direction = line.direction;
  61.         this.zero      = line.zero;
  62.         this.tolerance = line.tolerance;
  63.     }

  64.     /** Reset the instance as if built from two points.
  65.      * @param p1 first point belonging to the line (this can be any point)
  66.      * @param p2 second point belonging to the line (this can be any point, different from p1)
  67.      * @exception MathIllegalArgumentException if the points are equal
  68.      */
  69.     public void reset(final FieldVector3D<T> p1, final FieldVector3D<T> p2)
  70.         throws MathIllegalArgumentException {
  71.         final FieldVector3D<T> delta = p2.subtract(p1);
  72.         final T norm2 = delta.getNormSq();
  73.         if (norm2.getReal() == 0.0) {
  74.             throw new MathIllegalArgumentException(LocalizedCoreFormats.ZERO_NORM);
  75.         }
  76.         this.direction = new FieldVector3D<>(norm2.sqrt().reciprocal(), delta);
  77.         zero = new FieldVector3D<>(norm2.getField().getOne(), p1,
  78.                                    p1.dotProduct(delta).negate().divide(norm2), delta);
  79.     }

  80.     /** Get the tolerance below which points are considered identical.
  81.      * @return tolerance below which points are considered identical
  82.      */
  83.     public double getTolerance() {
  84.         return tolerance;
  85.     }

  86.     /** Get a line with reversed direction.
  87.      * @return a new instance, with reversed direction
  88.      */
  89.     public FieldLine<T> revert() {
  90.         final FieldLine<T> reverted = new FieldLine<>(this);
  91.         reverted.direction = reverted.direction.negate();
  92.         return reverted;
  93.     }

  94.     /** Get the normalized direction vector.
  95.      * @return normalized direction vector
  96.      */
  97.     public FieldVector3D<T> getDirection() {
  98.         return direction;
  99.     }

  100.     /** Get the line point closest to the origin.
  101.      * @return line point closest to the origin
  102.      */
  103.     public FieldVector3D<T> getOrigin() {
  104.         return zero;
  105.     }

  106.     /** Get the abscissa of a point with respect to the line.
  107.      * <p>The abscissa is 0 if the projection of the point and the
  108.      * projection of the frame origin on the line are the same
  109.      * point.</p>
  110.      * @param point point to check
  111.      * @return abscissa of the point
  112.      */
  113.     public T getAbscissa(final FieldVector3D<T> point) {
  114.         return point.subtract(zero).dotProduct(direction);
  115.     }

  116.     /** Get the abscissa of a point with respect to the line.
  117.      * <p>The abscissa is 0 if the projection of the point and the
  118.      * projection of the frame origin on the line are the same
  119.      * point.</p>
  120.      * @param point point to check
  121.      * @return abscissa of the point
  122.      */
  123.     public T getAbscissa(final Vector3D point) {
  124.         return zero.subtract(point).dotProduct(direction).negate();
  125.     }

  126.     /** Get one point from the line.
  127.      * @param abscissa desired abscissa for the point
  128.      * @return one point belonging to the line, at specified abscissa
  129.      */
  130.     public FieldVector3D<T> pointAt(final T abscissa) {
  131.         return new FieldVector3D<>(abscissa.getField().getOne(), zero,
  132.                 abscissa, direction);
  133.     }

  134.     /** Get one point from the line.
  135.      * @param abscissa desired abscissa for the point
  136.      * @return one point belonging to the line, at specified abscissa
  137.      */
  138.     public FieldVector3D<T> pointAt(final double abscissa) {
  139.         return new FieldVector3D<>(1, zero, abscissa, direction);
  140.     }

  141.     /** Check if the instance is similar to another line.
  142.      * <p>Lines are considered similar if they contain the same
  143.      * points. This does not mean they are equal since they can have
  144.      * opposite directions.</p>
  145.      * @param line line to which instance should be compared
  146.      * @return true if the lines are similar
  147.      */
  148.     public boolean isSimilarTo(final FieldLine<T> line) {
  149.         final double angle = FieldVector3D.angle(direction, line.direction).getReal();
  150.         return ((angle < tolerance) || (angle > (FastMath.PI - tolerance))) && contains(line.zero);
  151.     }

  152.     /** Check if the instance contains a point.
  153.      * @param p point to check
  154.      * @return true if p belongs to the line
  155.      */
  156.     public boolean contains(final FieldVector3D<T> p) {
  157.         return distance(p).getReal() < tolerance;
  158.     }

  159.     /** Check if the instance contains a point.
  160.      * @param p point to check
  161.      * @return true if p belongs to the line
  162.      */
  163.     public boolean contains(final Vector3D p) {
  164.         return distance(p).getReal() < tolerance;
  165.     }

  166.     /** Compute the distance between the instance and a point.
  167.      * @param p to check
  168.      * @return distance between the instance and the point
  169.      */
  170.     public T distance(final FieldVector3D<T> p) {
  171.         final FieldVector3D<T> d = p.subtract(zero);
  172.         final FieldVector3D<T> n = new FieldVector3D<>(zero.getX().getField().getOne(), d,
  173.                                                        d.dotProduct(direction).negate(), direction);
  174.         return n.getNorm();
  175.     }

  176.     /** Compute the distance between the instance and a point.
  177.      * @param p to check
  178.      * @return distance between the instance and the point
  179.      */
  180.     public T distance(final Vector3D p) {
  181.         final FieldVector3D<T> d = zero.subtract(p).negate();
  182.         final FieldVector3D<T> n = new FieldVector3D<>(zero.getX().getField().getOne(), d,
  183.                                                        d.dotProduct(direction).negate(), direction);
  184.         return n.getNorm();
  185.     }

  186.     /** Compute the shortest distance between the instance and another line.
  187.      * @param line line to check against the instance
  188.      * @return shortest distance between the instance and the line
  189.      */
  190.     public T distance(final FieldLine<T> line) {

  191.         final FieldVector3D<T> normal = FieldVector3D.crossProduct(direction, line.direction);
  192.         final T n = normal.getNorm();
  193.         if (n.getReal() < Precision.SAFE_MIN) {
  194.             // lines are parallel
  195.             return distance(line.zero);
  196.         }

  197.         // signed separation of the two parallel planes that contains the lines
  198.         final T offset = line.zero.subtract(zero).dotProduct(normal).divide(n);

  199.         return offset.abs();

  200.     }

  201.     /** Compute the point of the instance closest to another line.
  202.      * @param line line to check against the instance
  203.      * @return point of the instance closest to another line
  204.      */
  205.     public FieldVector3D<T> closestPoint(final FieldLine<T> line) {

  206.         final T cos = direction.dotProduct(line.direction);
  207.         final T n = cos.multiply(cos).subtract(1).negate();
  208.         if (n.getReal() < Precision.EPSILON) {
  209.             // the lines are parallel
  210.             return zero;
  211.         }

  212.         final FieldVector3D<T> delta0 = line.zero.subtract(zero);
  213.         final T a                     = delta0.dotProduct(direction);
  214.         final T b                     = delta0.dotProduct(line.direction);

  215.         return new FieldVector3D<>(a.getField().getOne(), zero,
  216.                 a.subtract(b.multiply(cos)).divide(n), direction);

  217.     }

  218.     /** Get the intersection point of the instance and another line.
  219.      * @param line other line
  220.      * @return intersection point of the instance and the other line
  221.      * or null if there are no intersection points
  222.      */
  223.     public FieldVector3D<T> intersection(final FieldLine<T> line) {
  224.         final FieldVector3D<T> closest = closestPoint(line);
  225.         return line.contains(closest) ? closest : null;
  226.     }

  227. }