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 */ 017package org.apache.commons.math3.geometry.euclidean.threed; 018 019import org.apache.commons.math3.exception.MathIllegalArgumentException; 020import org.apache.commons.math3.exception.util.LocalizedFormats; 021import org.apache.commons.math3.geometry.Point; 022import org.apache.commons.math3.geometry.Vector; 023import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D; 024import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet; 025import org.apache.commons.math3.geometry.euclidean.oned.Vector1D; 026import org.apache.commons.math3.geometry.partitioning.Embedding; 027import org.apache.commons.math3.util.FastMath; 028import org.apache.commons.math3.util.Precision; 029 030/** The class represent lines in a three dimensional space. 031 032 * <p>Each oriented line is intrinsically associated with an abscissa 033 * which is a coordinate on the line. The point at abscissa 0 is the 034 * orthogonal projection of the origin on the line, another equivalent 035 * way to express this is to say that it is the point of the line 036 * which is closest to the origin. Abscissa increases in the line 037 * direction.</p> 038 039 * @since 3.0 040 */ 041public class Line implements Embedding<Euclidean3D, Euclidean1D> { 042 043 /** Default value for tolerance. */ 044 private static final double DEFAULT_TOLERANCE = 1.0e-10; 045 046 /** Line direction. */ 047 private Vector3D direction; 048 049 /** Line point closest to the origin. */ 050 private Vector3D zero; 051 052 /** Tolerance below which points are considered identical. */ 053 private final double tolerance; 054 055 /** Build a line from two points. 056 * @param p1 first point belonging to the line (this can be any point) 057 * @param p2 second point belonging to the line (this can be any point, different from p1) 058 * @param tolerance tolerance below which points are considered identical 059 * @exception MathIllegalArgumentException if the points are equal 060 * @since 3.3 061 */ 062 public Line(final Vector3D p1, final Vector3D p2, final double tolerance) 063 throws MathIllegalArgumentException { 064 reset(p1, p2); 065 this.tolerance = tolerance; 066 } 067 068 /** Copy constructor. 069 * <p>The created instance is completely independent from the 070 * original instance, it is a deep copy.</p> 071 * @param line line to copy 072 */ 073 public Line(final Line line) { 074 this.direction = line.direction; 075 this.zero = line.zero; 076 this.tolerance = line.tolerance; 077 } 078 079 /** Build a line from two points. 080 * @param p1 first point belonging to the line (this can be any point) 081 * @param p2 second point belonging to the line (this can be any point, different from p1) 082 * @exception MathIllegalArgumentException if the points are equal 083 * @deprecated as of 3.3, replaced with {@link #Line(Vector3D, Vector3D, double)} 084 */ 085 @Deprecated 086 public Line(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException { 087 this(p1, p2, DEFAULT_TOLERANCE); 088 } 089 090 /** Reset the instance as if built from two points. 091 * @param p1 first point belonging to the line (this can be any point) 092 * @param p2 second point belonging to the line (this can be any point, different from p1) 093 * @exception MathIllegalArgumentException if the points are equal 094 */ 095 public void reset(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException { 096 final Vector3D delta = p2.subtract(p1); 097 final double norm2 = delta.getNormSq(); 098 if (norm2 == 0.0) { 099 throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM); 100 } 101 this.direction = new Vector3D(1.0 / FastMath.sqrt(norm2), delta); 102 zero = new Vector3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta); 103 } 104 105 /** Get the tolerance below which points are considered identical. 106 * @return tolerance below which points are considered identical 107 * @since 3.3 108 */ 109 public double getTolerance() { 110 return tolerance; 111 } 112 113 /** Get a line with reversed direction. 114 * @return a new instance, with reversed direction 115 */ 116 public Line revert() { 117 final Line reverted = new Line(this); 118 reverted.direction = reverted.direction.negate(); 119 return reverted; 120 } 121 122 /** Get the normalized direction vector. 123 * @return normalized direction vector 124 */ 125 public Vector3D getDirection() { 126 return direction; 127 } 128 129 /** Get the line point closest to the origin. 130 * @return line point closest to the origin 131 */ 132 public Vector3D getOrigin() { 133 return zero; 134 } 135 136 /** Get the abscissa of a point with respect to the line. 137 * <p>The abscissa is 0 if the projection of the point and the 138 * projection of the frame origin on the line are the same 139 * point.</p> 140 * @param point point to check 141 * @return abscissa of the point 142 */ 143 public double getAbscissa(final Vector3D point) { 144 return point.subtract(zero).dotProduct(direction); 145 } 146 147 /** Get one point from the line. 148 * @param abscissa desired abscissa for the point 149 * @return one point belonging to the line, at specified abscissa 150 */ 151 public Vector3D pointAt(final double abscissa) { 152 return new Vector3D(1.0, zero, abscissa, direction); 153 } 154 155 /** Transform a space point into a sub-space point. 156 * @param vector n-dimension point of the space 157 * @return (n-1)-dimension point of the sub-space corresponding to 158 * the specified space point 159 */ 160 public Vector1D toSubSpace(Vector<Euclidean3D> vector) { 161 return toSubSpace((Point<Euclidean3D>) vector); 162 } 163 164 /** Transform a sub-space point into a space point. 165 * @param vector (n-1)-dimension point of the sub-space 166 * @return n-dimension point of the space corresponding to the 167 * specified sub-space point 168 */ 169 public Vector3D toSpace(Vector<Euclidean1D> vector) { 170 return toSpace((Point<Euclidean1D>) vector); 171 } 172 173 /** {@inheritDoc} 174 * @see #getAbscissa(Vector3D) 175 */ 176 public Vector1D toSubSpace(final Point<Euclidean3D> point) { 177 return new Vector1D(getAbscissa((Vector3D) point)); 178 } 179 180 /** {@inheritDoc} 181 * @see #pointAt(double) 182 */ 183 public Vector3D toSpace(final Point<Euclidean1D> point) { 184 return pointAt(((Vector1D) point).getX()); 185 } 186 187 /** Check if the instance is similar to another line. 188 * <p>Lines are considered similar if they contain the same 189 * points. This does not mean they are equal since they can have 190 * opposite directions.</p> 191 * @param line line to which instance should be compared 192 * @return true if the lines are similar 193 */ 194 public boolean isSimilarTo(final Line line) { 195 final double angle = Vector3D.angle(direction, line.direction); 196 return ((angle < tolerance) || (angle > (FastMath.PI - tolerance))) && contains(line.zero); 197 } 198 199 /** Check if the instance contains a point. 200 * @param p point to check 201 * @return true if p belongs to the line 202 */ 203 public boolean contains(final Vector3D p) { 204 return distance(p) < tolerance; 205 } 206 207 /** Compute the distance between the instance and a point. 208 * @param p to check 209 * @return distance between the instance and the point 210 */ 211 public double distance(final Vector3D p) { 212 final Vector3D d = p.subtract(zero); 213 final Vector3D n = new Vector3D(1.0, d, -d.dotProduct(direction), direction); 214 return n.getNorm(); 215 } 216 217 /** Compute the shortest distance between the instance and another line. 218 * @param line line to check against the instance 219 * @return shortest distance between the instance and the line 220 */ 221 public double distance(final Line line) { 222 223 final Vector3D normal = Vector3D.crossProduct(direction, line.direction); 224 final double n = normal.getNorm(); 225 if (n < Precision.SAFE_MIN) { 226 // lines are parallel 227 return distance(line.zero); 228 } 229 230 // signed separation of the two parallel planes that contains the lines 231 final double offset = line.zero.subtract(zero).dotProduct(normal) / n; 232 233 return FastMath.abs(offset); 234 235 } 236 237 /** Compute the point of the instance closest to another line. 238 * @param line line to check against the instance 239 * @return point of the instance closest to another line 240 */ 241 public Vector3D closestPoint(final Line line) { 242 243 final double cos = direction.dotProduct(line.direction); 244 final double n = 1 - cos * cos; 245 if (n < Precision.EPSILON) { 246 // the lines are parallel 247 return zero; 248 } 249 250 final Vector3D delta0 = line.zero.subtract(zero); 251 final double a = delta0.dotProduct(direction); 252 final double b = delta0.dotProduct(line.direction); 253 254 return new Vector3D(1, zero, (a - b * cos) / n, direction); 255 256 } 257 258 /** Get the intersection point of the instance and another line. 259 * @param line other line 260 * @return intersection point of the instance and the other line 261 * or null if there are no intersection points 262 */ 263 public Vector3D intersection(final Line line) { 264 final Vector3D closest = closestPoint(line); 265 return line.contains(closest) ? closest : null; 266 } 267 268 /** Build a sub-line covering the whole line. 269 * @return a sub-line covering the whole line 270 */ 271 public SubLine wholeLine() { 272 return new SubLine(this, new IntervalsSet(tolerance)); 273 } 274 275}