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<>(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<>(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<>(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 }