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