Segment.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.twod;

  18. import org.apache.commons.geometry.core.RegionLocation;
  19. import org.apache.commons.geometry.core.Transform;
  20. import org.apache.commons.geometry.core.partitioning.Split;
  21. import org.apache.commons.numbers.core.Precision;

  22. /** Class representing a line segment in 2D Euclidean space. A line segment is a portion of
  23.  * a line with finite start and end points.
  24.  *
  25.  * <p>Instances of this class are guaranteed to be immutable.</p>
  26.  * @see Lines
  27.  * @see <a href="https://en.wikipedia.org/wiki/Line_segment">Line Segment</a>
  28.  */
  29. public final class Segment extends LineConvexSubset {

  30.     /** Start point for the segment. */
  31.     private final Vector2D startPoint;

  32.     /** End point for the segment. */
  33.     private final Vector2D endPoint;

  34.     /** Construct a new instance from a line and two points on the line. Callers are responsible for
  35.      * ensuring that the given points lie on the line and are in order of increasing abscissa.
  36.      * No validation is performed.
  37.      * @param line line for the segment
  38.      * @param startPoint segment start point
  39.      * @param endPoint segment end point
  40.      */
  41.     Segment(final Line line, final Vector2D startPoint, final Vector2D endPoint) {
  42.         super(line);

  43.         this.startPoint = startPoint;
  44.         this.endPoint = endPoint;
  45.     }

  46.     /** {@inheritDoc}
  47.      *
  48.      * <p>This method always returns {@code false}.</p>
  49.      */
  50.     @Override
  51.     public boolean isFull() {
  52.         return false;
  53.     }

  54.     /** {@inheritDoc}
  55.      *
  56.      * <p>This method always returns {@code false}.</p>
  57.      */
  58.     @Override
  59.     public boolean isInfinite() {
  60.         return false;
  61.     }

  62.     /** {@inheritDoc}
  63.      *
  64.      * <p>This method always returns {@code true}.</p>
  65.      */
  66.     @Override
  67.     public boolean isFinite() {
  68.         return true;
  69.     }

  70.     /** {@inheritDoc} */
  71.     @Override
  72.     public double getSize() {
  73.         return startPoint.distance(endPoint);
  74.     }

  75.     /** {@inheritDoc} */
  76.     @Override
  77.     public Vector2D getCentroid() {
  78.         return startPoint.lerp(endPoint, 0.5);
  79.     }

  80.     /** {@inheritDoc} */
  81.     @Override
  82.     public Vector2D getStartPoint() {
  83.         return startPoint;
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public double getSubspaceStart() {
  88.         return getLine().abscissa(startPoint);
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public Vector2D getEndPoint() {
  93.         return endPoint;
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public double getSubspaceEnd() {
  98.         return getLine().abscissa(endPoint);
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public Bounds2D getBounds() {
  103.         return Bounds2D.builder()
  104.                 .add(startPoint)
  105.                 .add(endPoint)
  106.                 .build();
  107.     }

  108.     /** {@inheritDoc} */
  109.     @Override
  110.     public Segment transform(final Transform<Vector2D> transform) {
  111.         final Vector2D t1 = transform.apply(getStartPoint());
  112.         final Vector2D t2 = transform.apply(getEndPoint());

  113.         final Line tLine = getLine().transform(transform);

  114.         return new Segment(tLine, t1, t2);
  115.     }

  116.     /** {@inheritDoc} */
  117.     @Override
  118.     public Segment reverse() {
  119.         return new Segment(getLine().reverse(), endPoint, startPoint);
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public String toString() {
  124.         final StringBuilder sb = new StringBuilder();
  125.         sb.append(getClass().getSimpleName())
  126.             .append("[startPoint= ")
  127.             .append(getStartPoint())
  128.             .append(", endPoint= ")
  129.             .append(getEndPoint())
  130.             .append(']');

  131.         return sb.toString();
  132.     }

  133.     /** {@inheritDoc} */
  134.     @Override
  135.     RegionLocation classifyAbscissa(final double abscissa) {
  136.         final Precision.DoubleEquivalence precision = getPrecision();
  137.         final int startCmp = precision.compare(abscissa, getSubspaceStart());
  138.         if (startCmp > 0) {
  139.             final int endCmp = precision.compare(abscissa, getSubspaceEnd());
  140.             if (endCmp < 0) {
  141.                 return RegionLocation.INSIDE;
  142.             } else if (endCmp == 0) {
  143.                 return RegionLocation.BOUNDARY;
  144.             }
  145.         } else if (startCmp == 0) {
  146.             return RegionLocation.BOUNDARY;
  147.         }

  148.         return RegionLocation.OUTSIDE;
  149.     }

  150.     /** {@inheritDoc} */
  151.     @Override
  152.     double closestAbscissa(final double abscissa) {
  153.         return Math.max(getSubspaceStart(), Math.min(getSubspaceEnd(), abscissa));
  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     Split<LineConvexSubset> splitOnIntersection(final Line splitter, final Vector2D intersection) {
  158.         final Line line = getLine();

  159.         final Precision.DoubleEquivalence splitterPrecision = splitter.getPrecision();

  160.         final int startCmp = splitterPrecision.compare(splitter.offset(startPoint), 0.0);
  161.         final int endCmp = splitterPrecision.compare(splitter.offset(endPoint), 0.0);

  162.         if (startCmp == 0 && endCmp == 0) {
  163.             // the entire segment is directly on the splitter line
  164.             return new Split<>(null, null);
  165.         } else if (startCmp < 1 && endCmp < 1) {
  166.             // the entire segment is on the minus side
  167.             return new Split<>(this, null);
  168.         } else if (startCmp > -1 && endCmp > -1) {
  169.             // the entire segment is on the plus side
  170.             return new Split<>(null, this);
  171.         }

  172.         // we need to split the line
  173.         final Segment startSegment = new Segment(line, startPoint, intersection);
  174.         final Segment endSegment = new Segment(line, intersection, endPoint);

  175.         final Segment minus = (startCmp > 0) ? endSegment : startSegment;
  176.         final Segment plus = (startCmp > 0) ? startSegment : endSegment;

  177.         return new Split<>(minus, plus);
  178.     }
  179. }