Lines3D.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.  *      http://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. package org.apache.commons.geometry.euclidean.threed.line;

  18. import java.text.MessageFormat;

  19. import org.apache.commons.geometry.euclidean.oned.Interval;
  20. import org.apache.commons.geometry.euclidean.oned.Vector1D;
  21. import org.apache.commons.geometry.euclidean.threed.Vector3D;
  22. import org.apache.commons.numbers.core.Precision;

  23. /** Class containing factory methods for constructing {@link Line3D} and {@link LineSubset3D} instances.
  24.  */
  25. public final class Lines3D {

  26.     /** Utility class; no instantiation. */
  27.     private Lines3D() {
  28.     }

  29.     /** Create a new line instance from two points that lie on the line. The line
  30.      * direction points from the first point to the second point.
  31.      * @param p1 first point on the line
  32.      * @param p2 second point on the line
  33.      * @param precision floating point precision context
  34.      * @return a new line instance that contains both of the given point and that has
  35.      *      a direction going from the first point to the second point
  36.      * @throws IllegalArgumentException if the points lie too close to create a non-zero direction vector
  37.      */
  38.     public static Line3D fromPoints(final Vector3D p1, final Vector3D p2,
  39.             final Precision.DoubleEquivalence precision) {
  40.         return fromPointAndDirection(p1, p1.vectorTo(p2), precision);
  41.     }

  42.     /** Create a new line instance from a point and a direction.
  43.      * @param pt a point lying on the line
  44.      * @param dir the direction of the line
  45.      * @param precision floating point precision context
  46.      * @return a new line instance that contains the given point and points in the
  47.      *      given direction
  48.      * @throws IllegalArgumentException if {@code dir} has zero length, as evaluated by the
  49.      *      given precision context
  50.      */
  51.     public static Line3D fromPointAndDirection(final Vector3D pt, final Vector3D dir,
  52.             final Precision.DoubleEquivalence precision) {
  53.         if (dir.isZero(precision)) {
  54.             throw new IllegalArgumentException("Line direction cannot be zero");
  55.         }

  56.         final Vector3D normDirection = dir.normalize();
  57.         final Vector3D origin = pt.reject(normDirection);

  58.         return new Line3D(origin, normDirection, precision);
  59.     }

  60.     /** Construct a ray from a start point and a direction.
  61.      * @param startPoint ray start point
  62.      * @param direction ray direction
  63.      * @param precision precision context used for floating point comparisons
  64.      * @return a new ray instance with the given start point and direction
  65.      * @throws IllegalArgumentException If {@code direction} has zero length, as evaluated by the
  66.      *      given precision context
  67.      * @see Lines3D#fromPointAndDirection(Vector3D, Vector3D, Precision.DoubleEquivalence)
  68.      */
  69.     public static Ray3D rayFromPointAndDirection(final Vector3D startPoint, final Vector3D direction,
  70.             final Precision.DoubleEquivalence precision) {
  71.         final Line3D line = Lines3D.fromPointAndDirection(startPoint, direction, precision);

  72.         return new Ray3D(line, startPoint);
  73.     }

  74.     /** Construct a ray starting at the given point and continuing to infinity in the direction
  75.      * of {@code line}. The given point is projected onto the line.
  76.      * @param line line for the ray
  77.      * @param startPoint start point for the ray
  78.      * @return a new ray instance starting at the given point and continuing in the direction of
  79.      *      {@code line}
  80.      * @throws IllegalArgumentException if any coordinate in {@code startPoint} is NaN or infinite
  81.      */
  82.     public static Ray3D rayFromPoint(final Line3D line, final Vector3D startPoint) {
  83.         return rayFromLocation(line, line.abscissa(startPoint));
  84.     }

  85.     /** Construct a ray starting at the given 1D location on {@code line} and continuing in the
  86.      * direction of the line to infinity.
  87.      * @param line line for the ray
  88.      * @param startLocation 1D location of the ray start point
  89.      * @return a new ray instance starting at the given 1D location and continuing to infinity
  90.      *      along {@code line}
  91.      * @throws IllegalArgumentException if {@code startLocation} is NaN or infinite
  92.      */
  93.     public static Ray3D rayFromLocation(final Line3D line, final double startLocation) {
  94.         if (!Double.isFinite(startLocation)) {
  95.             throw new IllegalArgumentException("Invalid ray start location: " + startLocation);
  96.         }

  97.         return new Ray3D(line, startLocation);
  98.     }

  99.     /** Construct a reverse ray from an end point and a line direction.
  100.      * @param endPoint instance end point
  101.      * @param lineDirection line direction
  102.      * @param precision precision context used for floating point comparisons
  103.      * @return a new reverse ray with the given end point and line direction
  104.      * @throws IllegalArgumentException If {@code lineDirection} has zero length, as evaluated by the
  105.      *      given precision context
  106.      * @see Lines3D#fromPointAndDirection(Vector3D, Vector3D, Precision.DoubleEquivalence)
  107.      */
  108.     public static ReverseRay3D reverseRayFromPointAndDirection(final Vector3D endPoint, final Vector3D lineDirection,
  109.             final Precision.DoubleEquivalence precision) {
  110.         final Line3D line = Lines3D.fromPointAndDirection(endPoint, lineDirection, precision);

  111.         return new ReverseRay3D(line, endPoint);
  112.     }

  113.     /** Construct a reverse ray starting at infinity and continuing in the direction of {@code line}
  114.      * to the given end point. The point is projected onto the line.
  115.      * @param line line for the instance
  116.      * @param endPoint end point for the instance
  117.      * @return a new reverse ray starting at infinity and continuing along the line to {@code endPoint}
  118.      * @throws IllegalArgumentException if any coordinate in {@code endPoint} is NaN or infinite
  119.      */
  120.     public static ReverseRay3D reverseRayFromPoint(final Line3D line, final Vector3D endPoint) {
  121.         return reverseRayFromLocation(line, line.abscissa(endPoint));
  122.     }

  123.     /** Construct a reverse ray starting at infinity and continuing in the direction of {@code line}
  124.      * to the given 1D end location.
  125.      * @param line line for the instance
  126.      * @param endLocation 1D location of the instance end point
  127.      * @return a new reverse ray starting infinity and continuing in the direction of {@code line}
  128.      *      to the given 1D end location
  129.      * @throws IllegalArgumentException if {@code endLocation} is NaN or infinite
  130.      */
  131.     public static ReverseRay3D reverseRayFromLocation(final Line3D line, final double endLocation) {
  132.         if (!Double.isFinite(endLocation)) {
  133.             throw new IllegalArgumentException("Invalid reverse ray end location: " + endLocation);
  134.         }

  135.         return new ReverseRay3D(line, endLocation);
  136.     }

  137.     /** Construct a new line segment from two points. A new line is created for the segment and points in the
  138.      * direction from {@code startPoint} to {@code endPoint}.
  139.      * @param startPoint segment start point
  140.      * @param endPoint segment end point
  141.      * @param precision precision context to use for floating point comparisons
  142.      * @return a new line segment instance with the given start and end points
  143.      * @throws IllegalArgumentException If the vector between {@code startPoint} and {@code endPoint} has zero length,
  144.      *      as evaluated by the given precision context
  145.      * @see Lines3D#fromPoints(Vector3D, Vector3D, Precision.DoubleEquivalence)
  146.      */
  147.     public static Segment3D segmentFromPoints(final Vector3D startPoint, final Vector3D endPoint,
  148.             final Precision.DoubleEquivalence precision) {
  149.         final Line3D line = Lines3D.fromPoints(startPoint, endPoint, precision);

  150.         // we know that the points lie on the line and are in increasing abscissa order
  151.         // since they were used to create the line
  152.         return new Segment3D(line, startPoint, endPoint);
  153.     }

  154.     /** Construct a new line segment from a line and a pair of points. The returned segment represents
  155.      * all points on the line between the projected locations of {@code a} and {@code b}. The points may
  156.      * be given in any order.
  157.      * @param line line forming the base of the segment
  158.      * @param a first point
  159.      * @param b second point
  160.      * @return a new line segment representing the points between the projected locations of {@code a}
  161.      *      and {@code b} on the given line
  162.      * @throws IllegalArgumentException if either point contains NaN or infinite coordinate values)
  163.      */
  164.     public static Segment3D segmentFromPoints(final Line3D line, final Vector3D a, final Vector3D b) {
  165.         return segmentFromLocations(line, line.abscissa(a), line.abscissa(b));
  166.     }

  167.     /** Construct a new line segment from a pair of 1D locations on a line. The returned line
  168.      * segment consists of all points between the two locations, regardless of the order the
  169.      * arguments are given.
  170.      * @param line line forming the base of the segment
  171.      * @param a first 1D location on the line
  172.      * @param b second 1D location on the line
  173.      * @return a new line segment representing the points between {@code a} and {@code b} on
  174.      *      the given line
  175.      * @throws IllegalArgumentException if either of the locations is NaN or infinite
  176.      */
  177.     public static Segment3D segmentFromLocations(final Line3D line, final double a, final double b) {

  178.         if (Double.isFinite(a) && Double.isFinite(b)) {
  179.             final double min = Math.min(a, b);
  180.             final double max = Math.max(a, b);

  181.             return new Segment3D(line, min, max);
  182.         }

  183.         throw new IllegalArgumentException(
  184.                 MessageFormat.format("Invalid line segment locations: {0}, {1}",
  185.                         Double.toString(a), Double.toString(b)));
  186.     }

  187.     /** Create a {@link LineConvexSubset3D} spanning the entire line. In other words, the returned
  188.      * subset is infinite and contains all points on the given line.
  189.      * @param line the line to span
  190.      * @return a convex subset spanning the entire line
  191.      */
  192.     public static LineConvexSubset3D span(final Line3D line) {
  193.         return new LineSpanningSubset3D(line);
  194.     }

  195.     /** Create a line convex subset from a line and a 1D interval on the line.
  196.      * @param line the line containing the subset
  197.      * @param interval 1D interval on the line
  198.      * @return a line convex subset defined by the given line and interval
  199.      */
  200.     public static LineConvexSubset3D subsetFromInterval(final Line3D line, final Interval interval) {
  201.         return subsetFromInterval(line, interval.getMin(), interval.getMax());
  202.     }

  203.     /** Create a line convex subset from a line and a 1D interval on the line.
  204.      * @param line the line containing the subset
  205.      * @param a first 1D location on the line
  206.      * @param b second 1D location on the line
  207.      * @return a line convex subset defined by the given line and interval
  208.      */
  209.     public static LineConvexSubset3D subsetFromInterval(final Line3D line, final double a, final double b) {
  210.         final double min = Math.min(a, b);
  211.         final double max = Math.max(a, b);

  212.         final boolean hasMin = Double.isFinite(min);
  213.         final boolean hasMax = Double.isFinite(max);

  214.         if (hasMin) {
  215.             if (hasMax) {
  216.                 // has both
  217.                 return new Segment3D(line, min, max);
  218.             }
  219.             // min only
  220.             return new Ray3D(line, min);
  221.         } else if (hasMax) {
  222.             // max only
  223.             return new ReverseRay3D(line, max);
  224.         } else if (Double.isInfinite(min) && Double.isInfinite(max) && Double.compare(min, max) < 0) {
  225.             return new LineSpanningSubset3D(line);
  226.         }

  227.         throw new IllegalArgumentException(MessageFormat.format(
  228.                 "Invalid line convex subset interval: {0}, {1}", Double.toString(a), Double.toString(b)));
  229.     }

  230.     /** Create a line convex subset from a line and a 1D interval on the line.
  231.      * @param line the line containing the subset
  232.      * @param a first 1D point on the line; must not be null
  233.      * @param b second 1D point on the line; must not be null
  234.      * @return a line convex subset defined by the given line and interval
  235.      */
  236.     public static LineConvexSubset3D subsetFromInterval(final Line3D line, final Vector1D a, final Vector1D b) {
  237.         return subsetFromInterval(line, a.getX(), b.getX());
  238.     }
  239. }