001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math3.geometry.euclidean.threed;
018
019 import org.apache.commons.math3.exception.MathIllegalArgumentException;
020 import org.apache.commons.math3.exception.util.LocalizedFormats;
021 import org.apache.commons.math3.geometry.Vector;
022 import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
023 import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
024 import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
025 import org.apache.commons.math3.geometry.partitioning.Embedding;
026 import org.apache.commons.math3.util.FastMath;
027 import org.apache.commons.math3.util.Precision;
028
029 /** The class represent lines in a three dimensional space.
030
031 * <p>Each oriented line is intrinsically associated with an abscissa
032 * which is a coordinate on the line. The point at abscissa 0 is the
033 * orthogonal projection of the origin on the line, another equivalent
034 * way to express this is to say that it is the point of the line
035 * which is closest to the origin. Abscissa increases in the line
036 * direction.</p>
037
038 * @version $Id: Line.java 1416643 2012-12-03 19:37:14Z tn $
039 * @since 3.0
040 */
041 public class Line implements Embedding<Euclidean3D, Euclidean1D> {
042
043 /** Line direction. */
044 private Vector3D direction;
045
046 /** Line point closest to the origin. */
047 private Vector3D zero;
048
049 /** Build a line from two points.
050 * @param p1 first point belonging to the line (this can be any point)
051 * @param p2 second point belonging to the line (this can be any point, different from p1)
052 * @exception MathIllegalArgumentException if the points are equal
053 */
054 public Line(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
055 reset(p1, p2);
056 }
057
058 /** Copy constructor.
059 * <p>The created instance is completely independent from the
060 * original instance, it is a deep copy.</p>
061 * @param line line to copy
062 */
063 public Line(final Line line) {
064 this.direction = line.direction;
065 this.zero = line.zero;
066 }
067
068 /** Reset the instance as if built from two points.
069 * @param p1 first point belonging to the line (this can be any point)
070 * @param p2 second point belonging to the line (this can be any point, different from p1)
071 * @exception MathIllegalArgumentException if the points are equal
072 */
073 public void reset(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
074 final Vector3D delta = p2.subtract(p1);
075 final double norm2 = delta.getNormSq();
076 if (norm2 == 0.0) {
077 throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM);
078 }
079 this.direction = new Vector3D(1.0 / FastMath.sqrt(norm2), delta);
080 zero = new Vector3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta);
081 }
082
083 /** Get a line with reversed direction.
084 * @return a new instance, with reversed direction
085 */
086 public Line revert() {
087 return new Line(zero, zero.subtract(direction));
088 }
089
090 /** Get the normalized direction vector.
091 * @return normalized direction vector
092 */
093 public Vector3D getDirection() {
094 return direction;
095 }
096
097 /** Get the line point closest to the origin.
098 * @return line point closest to the origin
099 */
100 public Vector3D getOrigin() {
101 return zero;
102 }
103
104 /** Get the abscissa of a point with respect to the line.
105 * <p>The abscissa is 0 if the projection of the point and the
106 * projection of the frame origin on the line are the same
107 * point.</p>
108 * @param point point to check
109 * @return abscissa of the point
110 */
111 public double getAbscissa(final Vector3D point) {
112 return point.subtract(zero).dotProduct(direction);
113 }
114
115 /** Get one point from the line.
116 * @param abscissa desired abscissa for the point
117 * @return one point belonging to the line, at specified abscissa
118 */
119 public Vector3D pointAt(final double abscissa) {
120 return new Vector3D(1.0, zero, abscissa, direction);
121 }
122
123 /** {@inheritDoc}
124 * @see #getAbscissa(Vector3D)
125 */
126 public Vector1D toSubSpace(final Vector<Euclidean3D> point) {
127 return new Vector1D(getAbscissa((Vector3D) point));
128 }
129
130 /** {@inheritDoc}
131 * @see #pointAt(double)
132 */
133 public Vector3D toSpace(final Vector<Euclidean1D> point) {
134 return pointAt(((Vector1D) point).getX());
135 }
136
137 /** Check if the instance is similar to another line.
138 * <p>Lines are considered similar if they contain the same
139 * points. This does not mean they are equal since they can have
140 * opposite directions.</p>
141 * @param line line to which instance should be compared
142 * @return true if the lines are similar
143 */
144 public boolean isSimilarTo(final Line line) {
145 final double angle = Vector3D.angle(direction, line.direction);
146 return ((angle < 1.0e-10) || (angle > (FastMath.PI - 1.0e-10))) && contains(line.zero);
147 }
148
149 /** Check if the instance contains a point.
150 * @param p point to check
151 * @return true if p belongs to the line
152 */
153 public boolean contains(final Vector3D p) {
154 return distance(p) < 1.0e-10;
155 }
156
157 /** Compute the distance between the instance and a point.
158 * @param p to check
159 * @return distance between the instance and the point
160 */
161 public double distance(final Vector3D p) {
162 final Vector3D d = p.subtract(zero);
163 final Vector3D n = new Vector3D(1.0, d, -d.dotProduct(direction), direction);
164 return n.getNorm();
165 }
166
167 /** Compute the shortest distance between the instance and another line.
168 * @param line line to check against the instance
169 * @return shortest distance between the instance and the line
170 */
171 public double distance(final Line line) {
172
173 final Vector3D normal = Vector3D.crossProduct(direction, line.direction);
174 final double n = normal.getNorm();
175 if (n < Precision.SAFE_MIN) {
176 // lines are parallel
177 return distance(line.zero);
178 }
179
180 // signed separation of the two parallel planes that contains the lines
181 final double offset = line.zero.subtract(zero).dotProduct(normal) / n;
182
183 return FastMath.abs(offset);
184
185 }
186
187 /** Compute the point of the instance closest to another line.
188 * @param line line to check against the instance
189 * @return point of the instance closest to another line
190 */
191 public Vector3D closestPoint(final Line line) {
192
193 final double cos = direction.dotProduct(line.direction);
194 final double n = 1 - cos * cos;
195 if (n < Precision.EPSILON) {
196 // the lines are parallel
197 return zero;
198 }
199
200 final Vector3D delta0 = line.zero.subtract(zero);
201 final double a = delta0.dotProduct(direction);
202 final double b = delta0.dotProduct(line.direction);
203
204 return new Vector3D(1, zero, (a - b * cos) / n, direction);
205
206 }
207
208 /** Get the intersection point of the instance and another line.
209 * @param line other line
210 * @return intersection point of the instance and the other line
211 * or null if there are no intersection points
212 */
213 public Vector3D intersection(final Line line) {
214 final Vector3D closest = closestPoint(line);
215 return line.contains(closest) ? closest : null;
216 }
217
218 /** Build a sub-line covering the whole line.
219 * @return a sub-line covering the whole line
220 */
221 public SubLine wholeLine() {
222 return new SubLine(this, new IntervalsSet());
223 }
224
225 }