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