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 }