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 ray in 2D Euclidean space. A ray is a portion of a line consisting of 025 * a single start point and extending to infinity along the direction of the line. 026 * 027 * <p>Instances of this class are guaranteed to be immutable.</p> 028 * @see ReverseRay 029 * @see Lines 030 */ 031public final class Ray extends LineConvexSubset { 032 033 /** The start point for the ray. */ 034 private final Vector2D startPoint; 035 036 /** Construct a ray from a line and a start point. Callers are responsible for ensuring that the 037 * given point lies on the line. No validation is performed. 038 * @param line line for the ray 039 * @param startPoint start point for the ray 040 */ 041 Ray(final Line line, final Vector2D startPoint) { 042 super(line); 043 044 this.startPoint = startPoint; 045 } 046 047 /** {@inheritDoc} 048 * 049 * <p>This method always returns {@code false}.</p> 050 */ 051 @Override 052 public boolean isFull() { 053 return false; 054 } 055 056 /** {@inheritDoc} 057 * 058 * <p>This method always returns {@code true}.</p> 059 */ 060 @Override 061 public boolean isInfinite() { 062 return true; 063 } 064 065 /** {@inheritDoc} 066 * 067 * <p>This method always returns {@code false}.</p> 068 */ 069 @Override 070 public boolean isFinite() { 071 return false; 072 } 073 074 /** {@inheritDoc} 075 * 076 * <p>This method always returns {@link Double#POSITIVE_INFINITY}.</p> 077 */ 078 @Override 079 public double getSize() { 080 return Double.POSITIVE_INFINITY; 081 } 082 083 /** {@inheritDoc} 084 * 085 * <p>This method always returns {@code null}.</p> 086 */ 087 @Override 088 public Vector2D getCentroid() { 089 return null; 090 } 091 092 @Override 093 public Vector2D getStartPoint() { 094 return startPoint; 095 } 096 097 /** {@inheritDoc} */ 098 @Override 099 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}