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 }