View Javadoc

    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
    19  import java.text.MessageFormat;
    20
    21  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
    22  import org.apache.commons.geometry.euclidean.oned.Interval;
    23  import org.apache.commons.geometry.euclidean.oned.Vector1D;
    24  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    25
    26  /** Class containing factory methods for constructing {@link Line3D} and {@link LineSubset3D} instances.
    27   */
    28  public final class Lines3D {
    29
    30      /** Utility class; no instantiation. */
    31      private Lines3D() {
    32      }
    33
    34      /** Create a new line instance from two points that lie on the line. The line
    35       * direction points from the first point to the second point.
    36       * @param p1 first point on the line
    37       * @param p2 second point on the line
    38       * @param precision floating point precision context
    39       * @return a new line instance that contains both of the given point and that has
    40       *      a direction going from the first point to the second point
    41       * @throws IllegalArgumentException if the points lie too close to create a non-zero direction vector
    42       */
    43      public static Line3D fromPoints(final Vector3D p1, final Vector3D p2,
    44              final DoublePrecisionContext precision) {
    45          return fromPointAndDirection(p1, p1.vectorTo(p2), precision);
    46      }
    47
    48      /** Create a new line instance from a point and a direction.
    49       * @param pt a point lying on the line
    50       * @param dir the direction of the line
    51       * @param precision floating point precision context
    52       * @return a new line instance that contains the given point and points in the
    53       *      given direction
    54       * @throws IllegalArgumentException if {@code dir} has zero length, as evaluated by the
    55       *      given precision context
    56       */
    57      public static Line3D fromPointAndDirection(final Vector3D pt, final Vector3D dir,
    58              final DoublePrecisionContext precision) {
    59          if (dir.isZero(precision)) {
    60              throw new IllegalArgumentException("Line direction cannot be zero");
    61          }
    62
    63          final Vector3D normDirection = dir.normalize();
    64          final Vector3D origin = pt.reject(normDirection);
    65
    66          return new Line3D(origin, normDirection, precision);
    67      }
    68
    69      /** Construct a ray from a start point and a direction.
    70       * @param startPoint ray start point
    71       * @param direction ray direction
    72       * @param precision precision context used for floating point comparisons
    73       * @return a new ray instance with the given start point and direction
    74       * @throws IllegalArgumentException If {@code direction} has zero length, as evaluated by the
    75       *      given precision context
    76       * @see Lines3D#fromPointAndDirection(Vector3D, Vector3D, DoublePrecisionContext)
    77       */
    78      public static Ray3D rayFromPointAndDirection(final Vector3D startPoint, final Vector3D direction,
    79              final DoublePrecisionContext precision) {
    80          final Line3D line = Lines3D.fromPointAndDirection(startPoint, direction, precision);
    81
    82          return new Ray3D(line, startPoint);
    83      }
    84
    85      /** Construct a ray starting at the given point and continuing to infinity in the direction
    86       * of {@code line}. The given point is projected onto the line.
    87       * @param line line for the ray
    88       * @param startPoint start point for the ray
    89       * @return a new ray instance starting at the given point and continuing in the direction of
    90       *      {@code line}
    91       * @throws IllegalArgumentException if any coordinate in {@code startPoint} is NaN or infinite
    92       */
    93      public static Ray3D rayFromPoint(final Line3D line, final Vector3D startPoint) {
    94          return rayFromLocation(line, line.abscissa(startPoint));
    95      }
    96
    97      /** Construct a ray starting at the given 1D location on {@code line} and continuing in the
    98       * direction of the line to infinity.
    99       * @param line line for the ray
    100      * @param startLocation 1D location of the ray start point
    101      * @return a new ray instance starting at the given 1D location and continuing to infinity
    102      *      along {@code line}
    103      * @throws IllegalArgumentException if {@code startLocation} is NaN or infinite
    104      */
    105     public static Ray3D rayFromLocation(final Line3D line, final double startLocation) {
    106         if (!Double.isFinite(startLocation)) {
    107             throw new IllegalArgumentException("Invalid ray start location: " + startLocation);
    108         }
    109
    110         return new Ray3D(line, startLocation);
    111     }
    112
    113     /** Construct a reverse ray from an end point and a line direction.
    114      * @param endPoint instance end point
    115      * @param lineDirection line direction
    116      * @param precision precision context used for floating point comparisons
    117      * @return a new reverse ray with the given end point and line direction
    118      * @throws IllegalArgumentException If {@code lineDirection} has zero length, as evaluated by the
    119      *      given precision context
    120      * @see Lines3D#fromPointAndDirection(Vector3D, Vector3D, DoublePrecisionContext)
    121      */
    122     public static ReverseRay3D reverseRayFromPointAndDirection(final Vector3D endPoint, final Vector3D lineDirection,
    123             final DoublePrecisionContext precision) {
    124         final Line3D line = Lines3D.fromPointAndDirection(endPoint, lineDirection, precision);
    125
    126         return new ReverseRay3D(line, endPoint);
    127     }
    128
    129     /** Construct a reverse ray starting at infinity and continuing in the direction of {@code line}
    130      * to the given end point. The point is projected onto the line.
    131      * @param line line for the instance
    132      * @param endPoint end point for the instance
    133      * @return a new reverse ray starting at infinity and continuing along the line to {@code endPoint}
    134      * @throws IllegalArgumentException if any coordinate in {@code endPoint} is NaN or infinite
    135      */
    136     public static ReverseRay3D reverseRayFromPoint(final Line3D line, final Vector3D endPoint) {
    137         return reverseRayFromLocation(line, line.abscissa(endPoint));
    138     }
    139
    140     /** Construct a reverse ray starting at infinity and continuing in the direction of {@code line}
    141      * to the given 1D end location.
    142      * @param line line for the instance
    143      * @param endLocation 1D location of the instance end point
    144      * @return a new reverse ray starting infinity and continuing in the direction of {@code line}
    145      *      to the given 1D end location
    146      * @throws IllegalArgumentException if {@code endLocation} is NaN or infinite
    147      */
    148     public static ReverseRay3D reverseRayFromLocation(final Line3D line, final double endLocation) {
    149         if (!Double.isFinite(endLocation)) {
    150             throw new IllegalArgumentException("Invalid reverse ray end location: " + endLocation);
    151         }
    152
    153         return new ReverseRay3D(line, endLocation);
    154     }
    155
    156     /** Construct a new line segment from two points. A new line is created for the segment and points in the
    157      * direction from {@code startPoint} to {@code endPoint}.
    158      * @param startPoint segment start point
    159      * @param endPoint segment end point
    160      * @param precision precision context to use for floating point comparisons
    161      * @return a new line segment instance with the given start and end points
    162      * @throws IllegalArgumentException If the vector between {@code startPoint} and {@code endPoint} has zero length,
    163      *      as evaluated by the given precision context
    164      * @see Lines3D#fromPoints(Vector3D, Vector3D, DoublePrecisionContext)
    165      */
    166     public static Segment3D segmentFromPoints(final Vector3D startPoint, final Vector3D endPoint,
    167             final DoublePrecisionContext precision) {
    168         final Line3D line = Lines3D.fromPoints(startPoint, endPoint, precision);
    169
    170         // we know that the points lie on the line and are in increasing abscissa order
    171         // since they were used to create the line
    172         return new Segment3D(line, startPoint, endPoint);
    173     }
    174
    175     /** Construct a new line segment from a line and a pair of points. The returned segment represents
    176      * all points on the line between the projected locations of {@code a} and {@code b}. The points may
    177      * be given in any order.
    178      * @param line line forming the base of the segment
    179      * @param a first point
    180      * @param b second point
    181      * @return a new line segment representing the points between the projected locations of {@code a}
    182      *      and {@code b} on the given line
    183      * @throws IllegalArgumentException if either point contains NaN or infinite coordinate values)
    184      */
    185     public static Segment3D segmentFromPoints(final Line3D line, final Vector3D a, final Vector3D b) {
    186         return segmentFromLocations(line, line.abscissa(a), line.abscissa(b));
    187     }
    188
    189     /** Construct a new line segment from a pair of 1D locations on a line. The returned line
    190      * segment consists of all points between the two locations, regardless of the order the
    191      * arguments are given.
    192      * @param line line forming the base of the segment
    193      * @param a first 1D location on the line
    194      * @param b second 1D location on the line
    195      * @return a new line segment representing the points between {@code a} and {@code b} on
    196      *      the given line
    197      * @throws IllegalArgumentException if either of the locations is NaN or infinite
    198      */
    199     public static Segment3D segmentFromLocations(final Line3D line, final double a, final double b) {
    200
    201         if (Double.isFinite(a) && Double.isFinite(b)) {
    202             final double min = Math.min(a, b);
    203             final double max = Math.max(a, b);
    204
    205             return new Segment3D(line, min, max);
    206         }
    207
    208         throw new IllegalArgumentException(
    209                 MessageFormat.format("Invalid line segment locations: {0}, {1}",
    210                         Double.toString(a), Double.toString(b)));
    211     }
    212
    213     /** Create a {@link LineConvexSubset3D} spanning the entire line. In other words, the returned
    214      * subset is infinite and contains all points on the given line.
    215      * @param line the line to span
    216      * @return a convex subset spanning the entire line
    217      */
    218     public static LineConvexSubset3D span(final Line3D line) {
    219         return new LineSpanningSubset3D(line);
    220     }
    221
    222     /** Create a line convex subset from a line and a 1D interval on the line.
    223      * @param line the line containing the subset
    224      * @param interval 1D interval on the line
    225      * @return a line convex subset defined by the given line and interval
    226      */
    227     public static LineConvexSubset3D subsetFromInterval(final Line3D line, final Interval interval) {
    228         return subsetFromInterval(line, interval.getMin(), interval.getMax());
    229     }
    230
    231     /** Create a line convex subset from a line and a 1D interval on the line.
    232      * @param line the line containing the subset
    233      * @param a first 1D location on the line
    234      * @param b second 1D location on the line
    235      * @return a line convex subset defined by the given line and interval
    236      */
    237     public static LineConvexSubset3D subsetFromInterval(final Line3D line, final double a, final double b) {
    238         final double min = Math.min(a, b);
    239         final double max = Math.max(a, b);
    240
    241         final boolean hasMin = Double.isFinite(min);
    242         final boolean hasMax = Double.isFinite(max);
    243
    244         if (hasMin) {
    245             if (hasMax) {
    246                 // has both
    247                 return new Segment3D(line, min, max);
    248             }
    249             // min only
    250             return new Ray3D(line, min);
    251         } else if (hasMax) {
    252             // max only
    253             return new ReverseRay3D(line, max);
    254         } else if (Double.isInfinite(min) && Double.isInfinite(max) && Double.compare(min, max) < 0) {
    255             return new LineSpanningSubset3D(line);
    256         }
    257
    258         throw new IllegalArgumentException(MessageFormat.format(
    259                 "Invalid line convex subset interval: {0}, {1}", Double.toString(a), Double.toString(b)));
    260     }
    261
    262     /** Create a line convex subset from a line and a 1D interval on the line.
    263      * @param line the line containing the subset
    264      * @param a first 1D point on the line; must not be null
    265      * @param b second 1D point on the line; must not be null
    266      * @return a line convex subset defined by the given line and interval
    267      */
    268     public static LineConvexSubset3D subsetFromInterval(final Line3D line, final Vector1D a, final Vector1D b) {
    269         return subsetFromInterval(line, a.getX(), b.getX());
    270     }
    271 }