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.threed.line; 18 19 import java.text.MessageFormat; 20 21 import org.apache.commons.geometry.euclidean.oned.Interval; 22 import org.apache.commons.geometry.euclidean.oned.Vector1D; 23 import org.apache.commons.geometry.euclidean.threed.Vector3D; 24 import org.apache.commons.numbers.core.Precision; 25 26 /** Class containing factory methods for constructing {@link Line3D} and {@link LineSubset3D} instances. 27 */ 28 public final class Lines3D { 29 30 /** Utility class; no instantiation. */ 31 private Lines3D() { 32 } 33 34 /** Create a new line instance from two points that lie on the line. The line 35 * direction points from the first point to the second point. 36 * @param p1 first point on the line 37 * @param p2 second point on the line 38 * @param precision floating point precision context 39 * @return a new line instance that contains both of the given point and that has 40 * a direction going from the first point to the second point 41 * @throws IllegalArgumentException if the points lie too close to create a non-zero direction vector 42 */ 43 public static Line3D fromPoints(final Vector3D p1, final Vector3D p2, 44 final Precision.DoubleEquivalence precision) { 45 return fromPointAndDirection(p1, p1.vectorTo(p2), precision); 46 } 47 48 /** Create a new line instance from a point and a direction. 49 * @param pt a point lying on the line 50 * @param dir the direction of the line 51 * @param precision floating point precision context 52 * @return a new line instance that contains the given point and points in the 53 * given direction 54 * @throws IllegalArgumentException if {@code dir} has zero length, as evaluated by the 55 * given precision context 56 */ 57 public static Line3D fromPointAndDirection(final Vector3D pt, final Vector3D dir, 58 final Precision.DoubleEquivalence precision) { 59 if (dir.isZero(precision)) { 60 throw new IllegalArgumentException("Line direction cannot be zero"); 61 } 62 63 final Vector3D normDirection = dir.normalize(); 64 final Vector3D origin = pt.reject(normDirection); 65 66 return new Line3D(origin, normDirection, precision); 67 } 68 69 /** Construct a ray from a start point and a direction. 70 * @param startPoint ray start point 71 * @param direction ray direction 72 * @param precision precision context used for floating point comparisons 73 * @return a new ray instance with the given start point and direction 74 * @throws IllegalArgumentException If {@code direction} has zero length, as evaluated by the 75 * given precision context 76 * @see Lines3D#fromPointAndDirection(Vector3D, Vector3D, Precision.DoubleEquivalence) 77 */ 78 public static Ray3D rayFromPointAndDirection(final Vector3D startPoint, final Vector3D direction, 79 final Precision.DoubleEquivalence precision) { 80 final Line3D line = Lines3D.fromPointAndDirection(startPoint, direction, precision); 81 82 return new Ray3D(line, startPoint); 83 } 84 85 /** Construct a ray starting at the given point and continuing to infinity in the direction 86 * of {@code line}. The given point is projected onto the line. 87 * @param line line for the ray 88 * @param startPoint start point for the ray 89 * @return a new ray instance starting at the given point and continuing in the direction of 90 * {@code line} 91 * @throws IllegalArgumentException if any coordinate in {@code startPoint} is NaN or infinite 92 */ 93 public static Ray3D rayFromPoint(final Line3D line, final Vector3D startPoint) { 94 return rayFromLocation(line, line.abscissa(startPoint)); 95 } 96 97 /** Construct a ray starting at the given 1D location on {@code line} and continuing in the 98 * direction of the line to infinity. 99 * @param line line for the ray 100 * @param startLocation 1D location of the ray start point 101 * @return a new ray instance starting at the given 1D location and continuing to infinity 102 * along {@code line} 103 * @throws IllegalArgumentException if {@code startLocation} is NaN or infinite 104 */ 105 public static Ray3D rayFromLocation(final Line3D line, final double startLocation) { 106 if (!Double.isFinite(startLocation)) { 107 throw new IllegalArgumentException("Invalid ray start location: " + startLocation); 108 } 109 110 return new Ray3D(line, startLocation); 111 } 112 113 /** Construct a reverse ray from an end point and a line direction. 114 * @param endPoint instance end point 115 * @param lineDirection line direction 116 * @param precision precision context used for floating point comparisons 117 * @return a new reverse ray with the given end point and line direction 118 * @throws IllegalArgumentException If {@code lineDirection} has zero length, as evaluated by the 119 * given precision context 120 * @see Lines3D#fromPointAndDirection(Vector3D, Vector3D, Precision.DoubleEquivalence) 121 */ 122 public static ReverseRay3D reverseRayFromPointAndDirection(final Vector3D endPoint, final Vector3D lineDirection, 123 final Precision.DoubleEquivalence precision) { 124 final Line3D line = Lines3D.fromPointAndDirection(endPoint, lineDirection, precision); 125 126 return new ReverseRay3D(line, endPoint); 127 } 128 129 /** Construct a reverse ray starting at infinity and continuing in the direction of {@code line} 130 * to the given end point. The point is projected onto the line. 131 * @param line line for the instance 132 * @param endPoint end point for the instance 133 * @return a new reverse ray starting at infinity and continuing along the line to {@code endPoint} 134 * @throws IllegalArgumentException if any coordinate in {@code endPoint} is NaN or infinite 135 */ 136 public static ReverseRay3D reverseRayFromPoint(final Line3D line, final Vector3D endPoint) { 137 return reverseRayFromLocation(line, line.abscissa(endPoint)); 138 } 139 140 /** Construct a reverse ray starting at infinity and continuing in the direction of {@code line} 141 * to the given 1D end location. 142 * @param line line for the instance 143 * @param endLocation 1D location of the instance end point 144 * @return a new reverse ray starting infinity and continuing in the direction of {@code line} 145 * to the given 1D end location 146 * @throws IllegalArgumentException if {@code endLocation} is NaN or infinite 147 */ 148 public static ReverseRay3D reverseRayFromLocation(final Line3D line, final double endLocation) { 149 if (!Double.isFinite(endLocation)) { 150 throw new IllegalArgumentException("Invalid reverse ray end location: " + endLocation); 151 } 152 153 return new ReverseRay3D(line, endLocation); 154 } 155 156 /** Construct a new line segment from two points. A new line is created for the segment and points in the 157 * direction from {@code startPoint} to {@code endPoint}. 158 * @param startPoint segment start point 159 * @param endPoint segment end point 160 * @param precision precision context to use for floating point comparisons 161 * @return a new line segment instance with the given start and end points 162 * @throws IllegalArgumentException If the vector between {@code startPoint} and {@code endPoint} has zero length, 163 * as evaluated by the given precision context 164 * @see Lines3D#fromPoints(Vector3D, Vector3D, Precision.DoubleEquivalence) 165 */ 166 public static Segment3D segmentFromPoints(final Vector3D startPoint, final Vector3D endPoint, 167 final Precision.DoubleEquivalence precision) { 168 final Line3D line = Lines3D.fromPoints(startPoint, endPoint, precision); 169 170 // we know that the points lie on the line and are in increasing abscissa order 171 // since they were used to create the line 172 return new Segment3D(line, startPoint, endPoint); 173 } 174 175 /** Construct a new line segment from a line and a pair of points. The returned segment represents 176 * all points on the line between the projected locations of {@code a} and {@code b}. The points may 177 * be given in any order. 178 * @param line line forming the base of the segment 179 * @param a first point 180 * @param b second point 181 * @return a new line segment representing the points between the projected locations of {@code a} 182 * and {@code b} on the given line 183 * @throws IllegalArgumentException if either point contains NaN or infinite coordinate values) 184 */ 185 public static Segment3D segmentFromPoints(final Line3D line, final Vector3D a, final Vector3D b) { 186 return segmentFromLocations(line, line.abscissa(a), line.abscissa(b)); 187 } 188 189 /** Construct a new line segment from a pair of 1D locations on a line. The returned line 190 * segment consists of all points between the two locations, regardless of the order the 191 * arguments are given. 192 * @param line line forming the base of the segment 193 * @param a first 1D location on the line 194 * @param b second 1D location on the line 195 * @return a new line segment representing the points between {@code a} and {@code b} on 196 * the given line 197 * @throws IllegalArgumentException if either of the locations is NaN or infinite 198 */ 199 public static Segment3D segmentFromLocations(final Line3D line, final double a, final double b) { 200 201 if (Double.isFinite(a) && Double.isFinite(b)) { 202 final double min = Math.min(a, b); 203 final double max = Math.max(a, b); 204 205 return new Segment3D(line, min, max); 206 } 207 208 throw new IllegalArgumentException( 209 MessageFormat.format("Invalid line segment locations: {0}, {1}", 210 Double.toString(a), Double.toString(b))); 211 } 212 213 /** Create a {@link LineConvexSubset3D} spanning the entire line. In other words, the returned 214 * subset is infinite and contains all points on the given line. 215 * @param line the line to span 216 * @return a convex subset spanning the entire line 217 */ 218 public static LineConvexSubset3D span(final Line3D line) { 219 return new LineSpanningSubset3D(line); 220 } 221 222 /** Create a line convex subset from a line and a 1D interval on the line. 223 * @param line the line containing the subset 224 * @param interval 1D interval on the line 225 * @return a line convex subset defined by the given line and interval 226 */ 227 public static LineConvexSubset3D subsetFromInterval(final Line3D line, final Interval interval) { 228 return subsetFromInterval(line, interval.getMin(), interval.getMax()); 229 } 230 231 /** Create a line convex subset from a line and a 1D interval on the line. 232 * @param line the line containing the subset 233 * @param a first 1D location on the line 234 * @param b second 1D location on the line 235 * @return a line convex subset defined by the given line and interval 236 */ 237 public static LineConvexSubset3D subsetFromInterval(final Line3D line, final double a, final double b) { 238 final double min = Math.min(a, b); 239 final double max = Math.max(a, b); 240 241 final boolean hasMin = Double.isFinite(min); 242 final boolean hasMax = Double.isFinite(max); 243 244 if (hasMin) { 245 if (hasMax) { 246 // has both 247 return new Segment3D(line, min, max); 248 } 249 // min only 250 return new Ray3D(line, min); 251 } else if (hasMax) { 252 // max only 253 return new ReverseRay3D(line, max); 254 } else if (Double.isInfinite(min) && Double.isInfinite(max) && Double.compare(min, max) < 0) { 255 return new LineSpanningSubset3D(line); 256 } 257 258 throw new IllegalArgumentException(MessageFormat.format( 259 "Invalid line convex subset interval: {0}, {1}", Double.toString(a), Double.toString(b))); 260 } 261 262 /** Create a line convex subset from a line and a 1D interval on the line. 263 * @param line the line containing the subset 264 * @param a first 1D point on the line; must not be null 265 * @param b second 1D point on the line; must not be null 266 * @return a line convex subset defined by the given line and interval 267 */ 268 public static LineConvexSubset3D subsetFromInterval(final Line3D line, final Vector1D a, final Vector1D b) { 269 return subsetFromInterval(line, a.getX(), b.getX()); 270 } 271 }