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 }