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 }