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.twod;
    18
    19  import org.apache.commons.geometry.core.RegionLocation;
    20  import org.apache.commons.geometry.core.Transform;
    21  import org.apache.commons.geometry.core.partitioning.Split;
    22  import org.apache.commons.numbers.core.Precision;
    23
    24  /** Class representing a ray in 2D Euclidean space. A ray is a portion of a line consisting of
    25   * a single start point and extending to infinity along the direction of the line.
    26   *
    27   * <p>Instances of this class are guaranteed to be immutable.</p>
    28   * @see ReverseRay
    29   * @see Lines
    30   */
    31  public final class Ray extends LineConvexSubset {
    32
    33      /** The start point for the ray. */
    34      private final Vector2D startPoint;
    35
    36      /** Construct a ray from a line and a start point. Callers are responsible for ensuring that the
    37       * given point lies on the line. No validation is performed.
    38       * @param line line for the ray
    39       * @param startPoint start point for the ray
    40       */
    41      Ray(final Line line, final Vector2D startPoint) {
    42          super(line);
    43
    44          this.startPoint = startPoint;
    45      }
    46
    47      /** {@inheritDoc}
    48       *
    49       * <p>This method always returns {@code false}.</p>
    50       */
    51      @Override
    52      public boolean isFull() {
    53          return false;
    54      }
    55
    56      /** {@inheritDoc}
    57      *
    58      * <p>This method always returns {@code true}.</p>
    59      */
    60      @Override
    61      public boolean isInfinite() {
    62          return true;
    63      }
    64
    65      /** {@inheritDoc}
    66      *
    67      * <p>This method always returns {@code false}.</p>
    68      */
    69      @Override
    70      public boolean isFinite() {
    71          return false;
    72      }
    73
    74      /** {@inheritDoc}
    75      *
    76      * <p>This method always returns {@link Double#POSITIVE_INFINITY}.</p>
    77      */
    78      @Override
    79      public double getSize() {
    80          return Double.POSITIVE_INFINITY;
    81      }
    82
    83      /** {@inheritDoc}
    84       *
    85       * <p>This method always returns {@code null}.</p>
    86       */
    87      @Override
    88      public Vector2D getCentroid() {
    89          return null;
    90      }
    91
    92      @Override
    93      public Vector2D getStartPoint() {
    94          return startPoint;
    95      }
    96
    97      /** {@inheritDoc} */
    98      @Override
    99      public double getSubspaceStart() {
    100         return getLine().abscissa(startPoint);
    101     }
    102
    103     /** {@inheritDoc}
    104     *
    105     * <p>This method always returns {@code null}.</p>
    106     */
    107     @Override
    108     public Vector2D getEndPoint() {
    109         return null;
    110     }
    111
    112     /** {@inheritDoc}
    113      *
    114      * <p>This method always returns {@link Double#POSITIVE_INFINITY}.</p>
    115      */
    116     @Override
    117     public double getSubspaceEnd() {
    118         return Double.POSITIVE_INFINITY;
    119     }
    120
    121     /** {@inheritDoc}
    122      *
    123      * <p>This method always returns {@code null}.</p>
    124      */
    125     @Override
    126     public Bounds2D getBounds() {
    127         return null; // infinite; no bounds
    128     }
    129
    130     /** Get the direction of the ray. This is a convenience method for {@code ray.getLine().getDirection()}.
    131      * @return the direction of the ray
    132      */
    133     public Vector2D getDirection() {
    134         return getLine().getDirection();
    135     }
    136
    137     /** {@inheritDoc} */
    138     @Override
    139     public Ray transform(final Transform<Vector2D> transform) {
    140         final Line tLine = getLine().transform(transform);
    141         final Vector2D tStart = transform.apply(getStartPoint());
    142
    143         return new Ray(tLine, tStart);
    144     }
    145
    146     /** {@inheritDoc} */
    147     @Override
    148     public ReverseRay reverse() {
    149         return new ReverseRay(getLine().reverse(), startPoint);
    150     }
    151
    152     /** {@inheritDoc} */
    153     @Override
    154     public String toString() {
    155         final StringBuilder sb = new StringBuilder();
    156         sb.append(getClass().getSimpleName())
    157             .append("[startPoint= ")
    158             .append(getStartPoint())
    159             .append(", direction= ")
    160             .append(getLine().getDirection())
    161             .append(']');
    162
    163         return sb.toString();
    164     }
    165
    166     /** {@inheritDoc} */
    167     @Override
    168     RegionLocation classifyAbscissa(final double abscissa) {
    169         final int cmp = getPrecision().compare(abscissa, getSubspaceStart());
    170         if (cmp > 0) {
    171             return RegionLocation.INSIDE;
    172         } else if (cmp == 0) {
    173             return RegionLocation.BOUNDARY;
    174         }
    175
    176         return RegionLocation.OUTSIDE;
    177     }
    178
    179     /** {@inheritDoc} */
    180     @Override
    181     double closestAbscissa(final double abscissa) {
    182         return Math.max(getSubspaceStart(), abscissa);
    183     }
    184
    185     /** {@inheritDoc} */
    186     @Override
    187     Split<LineConvexSubset> splitOnIntersection(final Line splitter, final Vector2D intersection) {
    188         final Line line = getLine();
    189         final Precision.DoubleEquivalence splitterPrecision = splitter.getPrecision();
    190
    191         final int startCmp = splitterPrecision.compare(splitter.offset(startPoint), 0.0);
    192         final boolean pointsTowardPlus = splitter.getOffsetDirection().dot(line.getDirection()) >= 0.0;
    193
    194         if (pointsTowardPlus && startCmp > -1) {
    195             // entirely on plus side
    196             return new Split<>(null, this);
    197         } else if (!pointsTowardPlus && startCmp < 1) {
    198             // entirely on minus side
    199             return new Split<>(this, null);
    200         }
    201
    202         // we're going to be split
    203         final Segment splitSeg = new Segment(line, startPoint, intersection);
    204         final Ray splitRay = new Ray(line, intersection);
    205
    206         final LineConvexSubset minus = (startCmp > 0) ? splitRay : splitSeg;
    207         final LineConvexSubset plus = (startCmp > 0) ? splitSeg : splitRay;
    208
    209         return new Split<>(minus, plus);
    210     }
    211 }