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