    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    }