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 line segment in 2D Euclidean space. A line segment is a portion of
    25   * a line with finite start and end points.
    26   *
    27   * <p>Instances of this class are guaranteed to be immutable.</p>
    28   * @see Lines
    29   * @see <a href="https://en.wikipedia.org/wiki/Line_segment">Line Segment</a>
    30   */
    31  public final class Segment extends LineConvexSubset {
    32
    33      /** Start point for the segment. */
    34      private final Vector2D startPoint;
    35
    36      /** End point for the segment. */
    37      private final Vector2D endPoint;
    38
    39      /** Construct a new instance from a line and two points on the line. Callers are responsible for
    40       * ensuring that the given points lie on the line and are in order of increasing abscissa.
    41       * No validation is performed.
    42       * @param line line for the segment
    43       * @param startPoint segment start point
    44       * @param endPoint segment end point
    45       */
    46      Segment(final Line line, final Vector2D startPoint, final Vector2D endPoint) {
    47          super(line);
    48
    49          this.startPoint = startPoint;
    50          this.endPoint = endPoint;
    51      }
    52
    53      /** {@inheritDoc}
    54       *
    55       * <p>This method always returns {@code false}.</p>
    56       */
    57      @Override
    58      public boolean isFull() {
    59          return false;
    60      }
    61
    62      /** {@inheritDoc}
    63       *
    64       * <p>This method always returns {@code false}.</p>
    65       */
    66      @Override
    67      public boolean isInfinite() {
    68          return false;
    69      }
    70
    71      /** {@inheritDoc}
    72       *
    73       * <p>This method always returns {@code true}.</p>
    74       */
    75      @Override
    76      public boolean isFinite() {
    77          return true;
    78      }
    79
    80      /** {@inheritDoc} */
    81      @Override
    82      public double getSize() {
    83          return startPoint.distance(endPoint);
    84      }
    85
    86      /** {@inheritDoc} */
    87      @Override
    88      public Vector2D getCentroid() {
    89          return startPoint.lerp(endPoint, 0.5);
    90      }
    91
    92      /** {@inheritDoc} */
    93      @Override
    94      public Vector2D getStartPoint() {
    95          return startPoint;
    96      }
    97
    98      /** {@inheritDoc} */
    99      @Override
    100     public double getSubspaceStart() {
    101         return getLine().abscissa(startPoint);
    102     }
    103
    104     /** {@inheritDoc} */
    105     @Override
    106     public Vector2D getEndPoint() {
    107         return endPoint;
    108     }
    109
    110     /** {@inheritDoc} */
    111     @Override
    112     public double getSubspaceEnd() {
    113         return getLine().abscissa(endPoint);
    114     }
    115
    116     /** {@inheritDoc} */
    117     @Override
    118     public Bounds2D getBounds() {
    119         return Bounds2D.builder()
    120                 .add(startPoint)
    121                 .add(endPoint)
    122                 .build();
    123     }
    124
    125     /** {@inheritDoc} */
    126     @Override
    127     public Segment transform(final Transform<Vector2D> transform) {
    128         final Vector2D t1 = transform.apply(getStartPoint());
    129         final Vector2D t2 = transform.apply(getEndPoint());
    130
    131         final Line tLine = getLine().transform(transform);
    132
    133         return new Segment(tLine, t1, t2);
    134     }
    135
    136     /** {@inheritDoc} */
    137     @Override
    138     public Segment reverse() {
    139         return new Segment(getLine().reverse(), endPoint, startPoint);
    140     }
    141
    142     /** {@inheritDoc} */
    143     @Override
    144     public String toString() {
    145         final StringBuilder sb = new StringBuilder();
    146         sb.append(getClass().getSimpleName())
    147             .append("[startPoint= ")
    148             .append(getStartPoint())
    149             .append(", endPoint= ")
    150             .append(getEndPoint())
    151             .append(']');
    152
    153         return sb.toString();
    154     }
    155
    156     /** {@inheritDoc} */
    157     @Override
    158     RegionLocation classifyAbscissa(final double abscissa) {
    159         final Precision.DoubleEquivalence precision = getPrecision();
    160         final int startCmp = precision.compare(abscissa, getSubspaceStart());
    161         if (startCmp > 0) {
    162             final int endCmp = precision.compare(abscissa, getSubspaceEnd());
    163             if (endCmp < 0) {
    164                 return RegionLocation.INSIDE;
    165             } else if (endCmp == 0) {
    166                 return RegionLocation.BOUNDARY;
    167             }
    168         } else if (startCmp == 0) {
    169             return RegionLocation.BOUNDARY;
    170         }
    171
    172         return RegionLocation.OUTSIDE;
    173     }
    174
    175     /** {@inheritDoc} */
    176     @Override
    177     double closestAbscissa(final double abscissa) {
    178         return Math.max(getSubspaceStart(), Math.min(getSubspaceEnd(), abscissa));
    179     }
    180
    181     /** {@inheritDoc} */
    182     @Override
    183     Split<LineConvexSubset> splitOnIntersection(final Line splitter, final Vector2D intersection) {
    184         final Line line = getLine();
    185
    186         final Precision.DoubleEquivalence splitterPrecision = splitter.getPrecision();
    187
    188         final int startCmp = splitterPrecision.compare(splitter.offset(startPoint), 0.0);
    189         final int endCmp = splitterPrecision.compare(splitter.offset(endPoint), 0.0);
    190
    191         if (startCmp == 0 && endCmp == 0) {
    192             // the entire segment is directly on the splitter line
    193             return new Split<>(null, null);
    194         } else if (startCmp < 1 && endCmp < 1) {
    195             // the entire segment is on the minus side
    196             return new Split<>(this, null);
    197         } else if (startCmp > -1 && endCmp > -1) {
    198             // the entire segment is on the plus side
    199             return new Split<>(null, this);
    200         }
    201
    202         // we need to split the line
    203         final Segment startSegment = new Segment(line, startPoint, intersection);
    204         final Segment endSegment = new Segment(line, intersection, endPoint);
    205
    206         final Segment minus = (startCmp > 0) ? endSegment : startSegment;
    207         final Segment plus = (startCmp > 0) ? startSegment : endSegment;
    208
    209         return new Split<>(minus, plus);
    210     }
    211 }