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}