001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.euclidean.twod;
018
019import org.apache.commons.geometry.core.RegionLocation;
020import org.apache.commons.geometry.core.Transform;
021import org.apache.commons.geometry.core.partitioning.Split;
022import org.apache.commons.numbers.core.Precision;
023
024/** Class representing a portion of a line in 2D Euclidean space that starts at infinity and
025 * continues in the direction of the line up to a single end point. This is equivalent to taking a
026 * {@link Ray} and reversing the line direction.
027 *
028 * <p>Instances of this class are guaranteed to be immutable.</p>
029 * @see Ray
030 * @see Lines
031 */
032public final class ReverseRay extends LineConvexSubset {
033
034    /** The end point of the reverse ray. */
035    private final Vector2D endPoint;
036
037    /** Construct a new instance from the given line and end point. Callers are responsible for ensuring that
038     * the given end point lies on the line. No validation is performed.
039     * @param line line for the instance
040     * @param endPoint end point for the instance
041     */
042    ReverseRay(final Line line, final Vector2D endPoint) {
043        super(line);
044
045        this.endPoint = endPoint;
046    }
047
048    /** {@inheritDoc}
049     *
050     * <p>This method always returns {@code false}.</p>
051     */
052    @Override
053    public boolean isFull() {
054        return false;
055    }
056
057    /** {@inheritDoc}
058     *
059     * <p>This method always returns {@code true}.</p>
060     */
061    @Override
062    public boolean isInfinite() {
063        return true;
064    }
065
066    /** {@inheritDoc}
067     *
068     * <p>This method always returns {@code false}.</p>
069     */
070    @Override
071    public boolean isFinite() {
072        return false;
073    }
074
075    /** {@inheritDoc}
076     *
077     * <p>This method always returns {@link Double#POSITIVE_INFINITY}.</p>
078     */
079    @Override
080    public double getSize() {
081        return Double.POSITIVE_INFINITY;
082    }
083
084    /** {@inheritDoc}
085     *
086     * <p>This method always returns {@code null}.</p>
087     */
088    @Override
089    public Vector2D getCentroid() {
090        return null;
091    }
092
093    /** {@inheritDoc}
094     *
095     * <p>This method always returns {@code null}.</p>
096     */
097    @Override
098    public Vector2D getStartPoint() {
099        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}