View Javadoc
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 }