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