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 * https://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 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.geometry.euclidean.threed; 23 24 import org.hipparchus.exception.LocalizedCoreFormats; 25 import org.hipparchus.exception.MathIllegalArgumentException; 26 import org.hipparchus.geometry.euclidean.oned.Euclidean1D; 27 import org.hipparchus.geometry.euclidean.oned.IntervalsSet; 28 import org.hipparchus.geometry.euclidean.oned.Vector1D; 29 import org.hipparchus.geometry.partitioning.Embedding; 30 import org.hipparchus.util.FastMath; 31 import org.hipparchus.util.Precision; 32 33 /** The class represent lines in a three dimensional space. 34 35 * <p>Each oriented line is intrinsically associated with an abscissa 36 * which is a coordinate on the line. The point at abscissa 0 is the 37 * orthogonal projection of the origin on the line, another equivalent 38 * way to express this is to say that it is the point of the line 39 * which is closest to the origin. Abscissa increases in the line 40 * direction.</p> 41 * 42 * @see #fromDirection(Vector3D, Vector3D, double) 43 * @see #Line(Vector3D, Vector3D, double) 44 */ 45 public class Line implements Embedding<Euclidean3D, Vector3D, Euclidean1D, Vector1D> { 46 47 /** Line direction. */ 48 private Vector3D direction; 49 50 /** Line point closest to the origin. */ 51 private Vector3D zero; 52 53 /** Tolerance below which points are considered identical. */ 54 private final double tolerance; 55 56 /** Build a line from two points. 57 * @param p1 first point belonging to the line (this can be any point) 58 * @param p2 second point belonging to the line (this can be any point, different from p1) 59 * @param tolerance tolerance below which points are considered identical 60 * @exception MathIllegalArgumentException if the points are equal 61 * @see #fromDirection(Vector3D, Vector3D, double) 62 */ 63 public Line(final Vector3D p1, final Vector3D p2, final double tolerance) 64 throws MathIllegalArgumentException { 65 this(tolerance); 66 reset(p1, p2); 67 } 68 69 /** Copy constructor. 70 * <p>The created instance is completely independent from the 71 * original instance, it is a deep copy.</p> 72 * @param line line to copy 73 */ 74 public Line(final Line line) { 75 this(line.tolerance); 76 this.direction = line.direction; 77 this.zero = line.zero; 78 } 79 80 /** 81 * Private constructor. Just sets the tolerance. 82 * 83 * @param tolerance below which points are considered identical. 84 */ 85 private Line(final double tolerance) { 86 this.tolerance = tolerance; 87 } 88 89 /** 90 * Create a line from a point and a direction. Line = {@code point} + t * {@code 91 * direction}, where t is any real number. 92 * 93 * @param point on the line. Can be any point. 94 * @param direction of the line. Must not be the zero vector. 95 * @param tolerance below which points are considered identical. 96 * @return a new Line with the given point and direction. 97 * @throws MathIllegalArgumentException if {@code direction} is the zero vector. 98 * @see #Line(Vector3D, Vector3D, double) 99 */ 100 public static Line fromDirection(final Vector3D point, 101 final Vector3D direction, 102 final double tolerance) { 103 final Line line = new Line(tolerance); 104 line.resetWithDirection(point, direction); 105 return line; 106 } 107 108 /** Reset the instance as if built from two points. 109 * @param p1 first point belonging to the line (this can be any point) 110 * @param p2 second point belonging to the line (this can be any point, different from p1) 111 * @exception MathIllegalArgumentException if the points are equal 112 */ 113 public void reset(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException { 114 resetWithDirection(p1, p2.subtract(p1)); 115 } 116 117 /** 118 * Reset the instance as if built from a point and direction. 119 * 120 * @param p1 point belonging to the line (this can be any point). 121 * @param delta direction of the line. 122 * @throws MathIllegalArgumentException if {@code delta} is the zero vector. 123 */ 124 private void resetWithDirection(final Vector3D p1, final Vector3D delta) { 125 final double norm2 = delta.getNormSq(); 126 if (norm2 == 0.0) { 127 throw new MathIllegalArgumentException(LocalizedCoreFormats.ZERO_NORM); 128 } 129 this.direction = new Vector3D(1.0 / FastMath.sqrt(norm2), delta); 130 zero = new Vector3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta); 131 } 132 133 /** Get the tolerance below which points are considered identical. 134 * @return tolerance below which points are considered identical 135 */ 136 public double getTolerance() { 137 return tolerance; 138 } 139 140 /** Get a line with reversed direction. 141 * @return a new instance, with reversed direction 142 */ 143 public Line revert() { 144 final Line reverted = new Line(this); 145 reverted.direction = reverted.direction.negate(); 146 return reverted; 147 } 148 149 /** Get the normalized direction vector. 150 * @return normalized direction vector 151 */ 152 public Vector3D getDirection() { 153 return direction; 154 } 155 156 /** Get the line point closest to the origin. 157 * @return line point closest to the origin 158 */ 159 public Vector3D getOrigin() { 160 return zero; 161 } 162 163 /** Get the abscissa of a point with respect to the line. 164 * <p>The abscissa is 0 if the projection of the point and the 165 * projection of the frame origin on the line are the same 166 * point.</p> 167 * @param point point to check 168 * @return abscissa of the point 169 */ 170 public double getAbscissa(final Vector3D point) { 171 return point.subtract(zero).dotProduct(direction); 172 } 173 174 /** Get one point from the line. 175 * @param abscissa desired abscissa for the point 176 * @return one point belonging to the line, at specified abscissa 177 */ 178 public Vector3D pointAt(final double abscissa) { 179 return new Vector3D(1.0, zero, abscissa, direction); 180 } 181 182 /** {@inheritDoc} 183 * @see #getAbscissa(Vector3D) 184 */ 185 @Override 186 public Vector1D toSubSpace(final Vector3D point) { 187 return new Vector1D(getAbscissa(point)); 188 } 189 190 /** {@inheritDoc} 191 * @see #pointAt(double) 192 */ 193 @Override 194 public Vector3D toSpace(final Vector1D point) { 195 return pointAt(point.getX()); 196 } 197 198 /** Check if the instance is similar to another line. 199 * <p>Lines are considered similar if they contain the same 200 * points. This does not mean they are equal since they can have 201 * opposite directions.</p> 202 * @param line line to which instance should be compared 203 * @return true if the lines are similar 204 */ 205 public boolean isSimilarTo(final Line line) { 206 final double angle = Vector3D.angle(direction, line.direction); 207 return ((angle < tolerance) || (angle > (FastMath.PI - tolerance))) && contains(line.zero); 208 } 209 210 /** Check if the instance contains a point. 211 * @param p point to check 212 * @return true if p belongs to the line 213 */ 214 public boolean contains(final Vector3D p) { 215 return distance(p) < tolerance; 216 } 217 218 /** Compute the distance between the instance and a point. 219 * @param p to check 220 * @return distance between the instance and the point 221 */ 222 public double distance(final Vector3D p) { 223 final Vector3D d = p.subtract(zero); 224 final Vector3D n = new Vector3D(1.0, d, -d.dotProduct(direction), direction); 225 return n.getNorm(); 226 } 227 228 /** Compute the shortest distance between the instance and another line. 229 * @param line line to check against the instance 230 * @return shortest distance between the instance and the line 231 */ 232 public double distance(final Line line) { 233 234 final Vector3D normal = Vector3D.crossProduct(direction, line.direction); 235 final double n = normal.getNorm(); 236 if (n < Precision.SAFE_MIN) { 237 // lines are parallel 238 return distance(line.zero); 239 } 240 241 // signed separation of the two parallel planes that contains the lines 242 final double offset = line.zero.subtract(zero).dotProduct(normal) / n; 243 244 return FastMath.abs(offset); 245 246 } 247 248 /** Compute the point of the instance closest to another line. 249 * @param line line to check against the instance 250 * @return point of the instance closest to another line 251 */ 252 public Vector3D closestPoint(final Line line) { 253 254 final double cos = direction.dotProduct(line.direction); 255 final double n = 1 - cos * cos; 256 if (n < Precision.EPSILON) { 257 // the lines are parallel 258 return zero; 259 } 260 261 final Vector3D delta0 = line.zero.subtract(zero); 262 final double a = delta0.dotProduct(direction); 263 final double b = delta0.dotProduct(line.direction); 264 265 return new Vector3D(1, zero, (a - b * cos) / n, direction); 266 267 } 268 269 /** Get the intersection point of the instance and another line. 270 * @param line other line 271 * @return intersection point of the instance and the other line 272 * or null if there are no intersection points 273 */ 274 public Vector3D intersection(final Line line) { 275 final Vector3D closest = closestPoint(line); 276 return line.contains(closest) ? closest : null; 277 } 278 279 /** Build a sub-line covering the whole line. 280 * @return a sub-line covering the whole line 281 */ 282 public SubLine wholeLine() { 283 return new SubLine(this, new IntervalsSet(tolerance)); 284 } 285 286 }