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    *      http://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  package org.apache.commons.math3.geometry.euclidean.threed;
18  
19  import org.apache.commons.math3.exception.MathIllegalArgumentException;
20  import org.apache.commons.math3.exception.util.LocalizedFormats;
21  import org.apache.commons.math3.geometry.Vector;
22  import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
23  import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
24  import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
25  import org.apache.commons.math3.geometry.partitioning.Embedding;
26  import org.apache.commons.math3.util.FastMath;
27  import org.apache.commons.math3.util.Precision;
28  
29  /** The class represent lines in a three dimensional space.
30  
31   * <p>Each oriented line is intrinsically associated with an abscissa
32   * which is a coordinate on the line. The point at abscissa 0 is the
33   * orthogonal projection of the origin on the line, another equivalent
34   * way to express this is to say that it is the point of the line
35   * which is closest to the origin. Abscissa increases in the line
36   * direction.</p>
37  
38   * @version $Id: Line.java 1453218 2013-03-06 08:53:28Z luc $
39   * @since 3.0
40   */
41  public class Line implements Embedding<Euclidean3D, Euclidean1D> {
42  
43      /** Line direction. */
44      private Vector3D direction;
45  
46      /** Line point closest to the origin. */
47      private Vector3D zero;
48  
49      /** Build a line from two points.
50       * @param p1 first point belonging to the line (this can be any point)
51       * @param p2 second point belonging to the line (this can be any point, different from p1)
52       * @exception MathIllegalArgumentException if the points are equal
53       */
54      public Line(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
55          reset(p1, p2);
56      }
57  
58      /** Copy constructor.
59       * <p>The created instance is completely independent from the
60       * original instance, it is a deep copy.</p>
61       * @param line line to copy
62       */
63      public Line(final Line line) {
64          this.direction = line.direction;
65          this.zero      = line.zero;
66      }
67  
68      /** Reset the instance as if built from two points.
69       * @param p1 first point belonging to the line (this can be any point)
70       * @param p2 second point belonging to the line (this can be any point, different from p1)
71       * @exception MathIllegalArgumentException if the points are equal
72       */
73      public void reset(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
74          final Vector3D delta = p2.subtract(p1);
75          final double norm2 = delta.getNormSq();
76          if (norm2 == 0.0) {
77              throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM);
78          }
79          this.direction = new Vector3D(1.0 / FastMath.sqrt(norm2), delta);
80          zero = new Vector3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta);
81      }
82  
83      /** Get a line with reversed direction.
84       * @return a new instance, with reversed direction
85       */
86      public Line revert() {
87          final Line reverted = new Line(this);
88          reverted.direction = reverted.direction.negate();
89          return reverted;
90      }
91  
92      /** Get the normalized direction vector.
93       * @return normalized direction vector
94       */
95      public Vector3D getDirection() {
96          return direction;
97      }
98  
99      /** Get the line point closest to the origin.
100      * @return line point closest to the origin
101      */
102     public Vector3D getOrigin() {
103         return zero;
104     }
105 
106     /** Get the abscissa of a point with respect to the line.
107      * <p>The abscissa is 0 if the projection of the point and the
108      * projection of the frame origin on the line are the same
109      * point.</p>
110      * @param point point to check
111      * @return abscissa of the point
112      */
113     public double getAbscissa(final Vector3D point) {
114         return point.subtract(zero).dotProduct(direction);
115     }
116 
117     /** Get one point from the line.
118      * @param abscissa desired abscissa for the point
119      * @return one point belonging to the line, at specified abscissa
120      */
121     public Vector3D pointAt(final double abscissa) {
122         return new Vector3D(1.0, zero, abscissa, direction);
123     }
124 
125     /** {@inheritDoc}
126      * @see #getAbscissa(Vector3D)
127      */
128     public Vector1D toSubSpace(final Vector<Euclidean3D> point) {
129         return new Vector1D(getAbscissa((Vector3D) point));
130     }
131 
132     /** {@inheritDoc}
133      * @see #pointAt(double)
134      */
135     public Vector3D toSpace(final Vector<Euclidean1D> point) {
136         return pointAt(((Vector1D) point).getX());
137     }
138 
139     /** Check if the instance is similar to another line.
140      * <p>Lines are considered similar if they contain the same
141      * points. This does not mean they are equal since they can have
142      * opposite directions.</p>
143      * @param line line to which instance should be compared
144      * @return true if the lines are similar
145      */
146     public boolean isSimilarTo(final Line line) {
147         final double angle = Vector3D.angle(direction, line.direction);
148         return ((angle < 1.0e-10) || (angle > (FastMath.PI - 1.0e-10))) && contains(line.zero);
149     }
150 
151     /** Check if the instance contains a point.
152      * @param p point to check
153      * @return true if p belongs to the line
154      */
155     public boolean contains(final Vector3D p) {
156         return distance(p) < 1.0e-10;
157     }
158 
159     /** Compute the distance between the instance and a point.
160      * @param p to check
161      * @return distance between the instance and the point
162      */
163     public double distance(final Vector3D p) {
164         final Vector3D d = p.subtract(zero);
165         final Vector3D n = new Vector3D(1.0, d, -d.dotProduct(direction), direction);
166         return n.getNorm();
167     }
168 
169     /** Compute the shortest distance between the instance and another line.
170      * @param line line to check against the instance
171      * @return shortest distance between the instance and the line
172      */
173     public double distance(final Line line) {
174 
175         final Vector3D normal = Vector3D.crossProduct(direction, line.direction);
176         final double n = normal.getNorm();
177         if (n < Precision.SAFE_MIN) {
178             // lines are parallel
179             return distance(line.zero);
180         }
181 
182         // signed separation of the two parallel planes that contains the lines
183         final double offset = line.zero.subtract(zero).dotProduct(normal) / n;
184 
185         return FastMath.abs(offset);
186 
187     }
188 
189     /** Compute the point of the instance closest to another line.
190      * @param line line to check against the instance
191      * @return point of the instance closest to another line
192      */
193     public Vector3D closestPoint(final Line line) {
194 
195         final double cos = direction.dotProduct(line.direction);
196         final double n = 1 - cos * cos;
197         if (n < Precision.EPSILON) {
198             // the lines are parallel
199             return zero;
200         }
201 
202         final Vector3D delta0 = line.zero.subtract(zero);
203         final double a        = delta0.dotProduct(direction);
204         final double b        = delta0.dotProduct(line.direction);
205 
206         return new Vector3D(1, zero, (a - b * cos) / n, direction);
207 
208     }
209 
210     /** Get the intersection point of the instance and another line.
211      * @param line other line
212      * @return intersection point of the instance and the other line
213      * or null if there are no intersection points
214      */
215     public Vector3D intersection(final Line line) {
216         final Vector3D closest = closestPoint(line);
217         return line.contains(closest) ? closest : null;
218     }
219 
220     /** Build a sub-line covering the whole line.
221      * @return a sub-line covering the whole line
222      */
223     public SubLine wholeLine() {
224         return new SubLine(this, new IntervalsSet());
225     }
226 
227 }