/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.Embedding;
import org.apache.commons.math.util.FastMath;
/** The class represent lines in a three dimensional space.
* <p>Each oriented line is intrinsically associated with an abscissa
* wich is a coordinate on the line. The point at abscissa 0 is the
* orthogonal projection of the origin on the line, another equivalent
* way to express this is to say that it is the point of the line
* which is closest to the origin. Abscissa increases in the line
* direction.</p>
* @version $Id: Line.java 1131229 2011-06-03 20:49:25Z luc $
* @since 3.0
*/
public class Line implements Embedding<Euclidean3D, Euclidean1D> {
/** Line direction. */
private Vector3D direction;
/** Line point closest to the origin. */
private Vector3D zero;
/** Build a line from a point and a direction.
* @param p point belonging to the line (this can be any point)
* @param direction direction of the line
* @exception IllegalArgumentException if the direction norm is too small
*/
public Line(final Vector3D p, final Vector3D direction) {
reset(p, direction);
}
/** Reset the instance as if built from a point and a normal.
* @param p point belonging to the line (this can be any point)
* @param dir direction of the line
* @exception IllegalArgumentException if the direction norm is too small
*/
public void reset(final Vector3D p, final Vector3D dir) {
final double norm = dir.getNorm();
if (norm == 0.0) {
throw new IllegalArgumentException("null norm");
}
this.direction = new Vector3D(1.0 / norm, dir);
zero = new Vector3D(1.0, p, -Vector3D.dotProduct(p, this.direction), this.direction);
}
/** Get a line with reversed direction.
* @return a new instance, with reversed direction
*/
public Line revert() {
return new Line(zero, direction.negate());
}
/** Get the normalized direction vector.
* @return normalized direction vector
*/
public Vector3D getDirection() {
return direction;
}
/** Get the line point closest to the origin.
* @return line point closest to the origin
*/
public Vector3D getOrigin() {
return zero;
}
/** Get the abscissa of a point with respect to the line.
* <p>The abscissa is 0 if the projection of the point and the
* projection of the frame origin on the line are the same
* point.</p>
* @param point point to check (must be a {@link Vector3D Vector3D}
* instance)
* @return abscissa of the point (really a
* {org.apache.commons.math.geometry.euclidean.oned.Vector1D Vector1D} instance)
*/
public Vector1D toSubSpace(final Vector<Euclidean3D> point) {
Vector3D p3 = (Vector3D) point;
return new Vector1D(Vector3D.dotProduct(p3.subtract(zero), direction));
}
/** Get one point from the line.
* @param point desired abscissa for the point (must be a
* {org.apache.commons.math.geometry.euclidean.oned.Vector1D Vector1D} instance)
* @return one point belonging to the line, at specified abscissa
* (really a {@link Vector3D Vector3D} instance)
*/
public Vector3D toSpace(final Vector<Euclidean1D> point) {
Vector1D p1 = (Vector1D) point;
return new Vector3D(1.0, zero, p1.getX(), direction);
}
/** Check if the instance is similar to another line.
* <p>Lines are considered similar if they contain the same
* points. This does not mean they are equal since they can have
* opposite directions.</p>
* @param line line to which instance should be compared
* @return true if the lines are similar
*/
public boolean isSimilarTo(final Line line) {
final double angle = Vector3D.angle(direction, line.direction);
return ((angle < 1.0e-10) || (angle > (FastMath.PI - 1.0e-10))) && contains(line.zero);
}
/** Check if the instance contains a point.
* @param p point to check
* @return true if p belongs to the line
*/
public boolean contains(final Vector3D p) {
return distance(p) < 1.0e-10;
}
/** Compute the distance between the instance and a point.
* @param p to check
* @return distance between the instance and the point
*/
public double distance(final Vector3D p) {
final Vector3D d = p.subtract(zero);
final Vector3D n = new Vector3D(1.0, d, -Vector3D.dotProduct(d, direction), direction);
return n.getNorm();
}
/** Compute the shortest distance between the instance and another line.
* @param line line to check agains the instance
* @return shortest distance between the instance and the line
*/
public double distance(final Line line) {
final Vector3D normal = Vector3D.crossProduct(direction, line.direction);
if (normal.getNorm() < 1.0e-10) {
// lines are parallel
return distance(line.zero);
}
// separating middle plane
final Plane middle = new Plane(new Vector3D(0.5, zero, 0.5, line.zero), normal);
// the lines are at the same distance on either side of the plane
return 2 * FastMath.abs(middle.getOffset(zero));
}
}