/** * */ package icy.type.geom; import icy.type.point.Point3D; import icy.type.rectangle.Rectangle3D; /** * This <code>Line3D</code> (3D equivalent to java Line2D class) represents a 3D line segment in {@code (x,y,z)} * coordinate space. * * @author Stephane */ public class Line3D implements Shape3D, Cloneable { /** * The X coordinate of the start point of the line segment. */ protected double x1; /** * The Y coordinate of the start point of the line segment. */ protected double y1; /** * The Z coordinate of the start point of the line segment. */ protected double z1; /** * The X coordinate of the end point of the line segment. */ protected double x2; /** * The Y coordinate of the end point of the line segment. */ protected double y2; /** * The Z coordinate of the end point of the line segment. */ protected double z2; /** * Constructs and initializes a Line with coordinates (0, 0, 0) -> (0, 0, 0). */ public Line3D() { super(); setLine(0d, 0d, 0d, 0d, 0d, 0d); } /** * Constructs and initializes a Line from the specified coordinates. * * @param x1 * the X coordinate of the start point * @param y1 * the Y coordinate of the start point * @param z1 * the Z coordinate of the start point * @param x2 * the X coordinate of the end point * @param y2 * the Y coordinate of the end point * @param z2 * the Z coordinate of the end point */ public Line3D(double x1, double y1, double z1, double x2, double y2, double z2) { setLine(x1, y1, z1, x2, y2, z2); } /** * Constructs and initializes a <code>Line3D</code> from the specified <code>Point3D</code> objects. * * @param p1 * the start <code>Point3D</code> of this line segment * @param p2 * the end <code>Point3D</code> of this line segment */ public Line3D(Point3D p1, Point3D p2) { setLine(p1, p2); } /** * Constructs and initializes a <code>Line3D</code> from the specified <code>Line3D</code> object. * * @param l * the <code>Line3D</code> to copy */ public Line3D(Line3D l) { setLine(l); } /** * Returns the X coordinate of the start point in double precision. * * @return the X coordinate of the start point of this {@code Line3D} object. */ public double getX1() { return x1; } /** * Returns the Y coordinate of the start point in double precision. * * @return the Y coordinate of the start point of this {@code Line3D} object. */ public double getY1() { return y1; } /** * Returns the Z coordinate of the start point in double precision. * * @return the Z coordinate of the start point of this {@code Line3D} object. */ public double getZ1() { return z1; } /** * Returns the start <code>Point3D</code> of this <code>Line3D</code>. * * @return the start <code>Point3D</code> of this <code>Line3D</code>. */ public Point3D getP1() { return new Point3D.Double(getX1(), getY1(), getZ1()); } /** * Returns the X coordinate of the end point in double precision. * * @return the X coordinate of the end point of this {@code Line3D} object. */ public double getX2() { return x2; } /** * Returns the Y coordinate of the end point in double precision. * * @return the Y coordinate of the end point of this {@code Line3D} object. */ public double getY2() { return y2; } /** * Returns the Z coordinate of the end point in double precision. * * @return the Z coordinate of the end point of this {@code Line3D} object. */ public double getZ2() { return z2; } /** * Returns the end <code>Point3D</code> of this <code>Line3D</code>. * * @return the end <code>Point3D</code> of this <code>Line3D</code>. */ public Point3D getP2() { return new Point3D.Double(getX2(), getY2(), getZ2()); } /** * Returns the vector representing this <code>Line3D</code>. * * @return the vector representing this <code>Line3D</code>. */ public Point3D getVector() { return new Point3D.Double(getX2() - getX1(), getY2() - getY1(), getZ2() - getZ1()); } /** * Sets the location of the end points of this <code>Line3D</code> to the specified double coordinates. * * @param x1 * the X coordinate of the start point * @param y1 * the Y coordinate of the start point * @param z1 * the Z coordinate of the start point * @param x2 * the X coordinate of the end point * @param y2 * the Y coordinate of the end point * @param z2 * the Z coordinate of the start point */ public void setLine(double x1, double y1, double z1, double x2, double y2, double z2) { this.x1 = x1; this.y1 = y1; this.z1 = z1; this.x2 = x2; this.y2 = y2; this.z2 = z2; } @Override public Rectangle3D getBounds() { double x, y, z; double sizeX, sizeY, sizeZ; if (x1 < x2) { x = x1; sizeX = x2 - x1; } else { x = x2; sizeX = x1 - x2; } if (y1 < y2) { y = y1; sizeY = y2 - y1; } else { y = y2; sizeY = y1 - y2; } if (z1 < z2) { z = z1; sizeZ = z2 - z1; } else { z = z2; sizeZ = z1 - z2; } return new Rectangle3D.Double(x, y, z, sizeX, sizeY, sizeZ); } /** * Sets the location of the end points of this <code>Line3D</code> to * the specified <code>Point3D</code> coordinates. * * @param p1 * the start <code>Point3D</code> of the line segment * @param p2 * the end <code>Point3D</code> of the line segment */ public void setLine(Point3D p1, Point3D p2) { setLine(p1.getX(), p1.getY(), p1.getZ(), p2.getX(), p2.getY(), p2.getZ()); } /** * Sets the location of the end points of this <code>Line3D</code> to * the same as those end points of the specified <code>Line3D</code>. * * @param l * the specified <code>Line3D</code> */ public void setLine(Line3D l) { setLine(l.getX1(), l.getY1(), l.getZ1(), l.getX2(), l.getY2(), l.getZ2()); } /** * Tests if the line segment from {@code (x1,y1,z1)} to {@code (x2,y2,z2)} intersects the line segment from * {@code (x3,y3,z3)} to {@code (x4,y4,z4)}. * * @param x1 * the X coordinate of the start point of the first line segment * @param y1 * the Y coordinate of the start point of the first line segment * @param z1 * the Z coordinate of the start point of the first line segment * @param x2 * the X coordinate of the end point of the first line segment * @param y2 * the Y coordinate of the end point of the first line segment * @param z2 * the Z coordinate of the end point of the first line segment * @param x3 * the X coordinate of the start point of the end line segment * @param y3 * the Y coordinate of the start point of the end line segment * @param z3 * the Z coordinate of the start point of the end line segment * @param x4 * the X coordinate of the end point of the end line segment * @param y4 * the Y coordinate of the end point of the end line segment * @param z4 * the Z coordinate of the end point of the end line segment * @return <code>true</code> if the first specified line segment and the second specified line segment intersect * each other; <code>false</code> otherwise. */ public static boolean linesIntersect(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double x4, double y4, double z4) { // line 1 vector final Point3D vA = new Point3D.Double(x2 - x1, y2 - y1, z2 - z1); // line 2 vector final Point3D vB = new Point3D.Double(x4 - x3, y4 - y3, z4 - z3); // vector of the 2 starting point final Point3D vC = new Point3D.Double(x3 - x1, y3 - y1, z3 - z1); final Point3D crossAB = vA.crossProduct(vB); if (vC.dotProduct(crossAB) != 0d) // lines are not coplanar return false; final double norm2 = crossAB.norm2(); if (norm2 == 0d) return false; final Point3D crossCB = vC.crossProduct(vB); final double dot = crossCB.dotProduct(crossAB); final double s = dot / norm2; return (s >= 0d) && (s <= 1d); } /** * Tests if the line segment from {@code (x1,y1,z1)} to {@code (x2,y2,z2)} intersects this line segment. * * @param x1 * the X coordinate of the start point of the specified line segment * @param y1 * the Y coordinate of the start point of the specified line segment * @param z1 * the Z coordinate of the start point of the specified line segment * @param x2 * the X coordinate of the end point of the specified line segment * @param y2 * the Y coordinate of the end point of the specified line segment * @param z2 * the Z coordinate of the end point of the specified line segment * @return <true> if this line segment and the specified line segment intersect each other; <code>false</code> * otherwise. */ public boolean intersectsLine(double x1, double y1, double z1, double x2, double y2, double z2) { return linesIntersect(x1, y1, z1, x2, y2, z2, getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2()); } /** * Tests if the specified line segment intersects this line segment. * * @param l * the specified <code>Line3D</code> * @return <code>true</code> if this line segment and the specified line * segment intersect each other; <code>false</code> otherwise. */ public boolean intersectsLine(Line3D l) { return linesIntersect(l.getX1(), l.getY1(), l.getZ1(), l.getX2(), l.getY2(), l.getZ2(), getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2()); } /** * Returns the square of the distance from a point to a line segment. * The distance measured is the distance between the specified point and the closest point between the specified end * points. If the specified point intersects the line segment in between the end points, this method returns 0.0. * * @param x1 * the X coordinate of the start point of the specified line segment * @param y1 * the Y coordinate of the start point of the specified line segment * @param z1 * the Z coordinate of the start point of the specified line segment * @param x2 * the X coordinate of the end point of the specified line segment * @param y2 * the Y coordinate of the end point of the specified line segment * @param z2 * the Z coordinate of the end point of the specified line segment * @param px * the X coordinate of the specified point being measured against the specified line segment * @param py * the Y coordinate of the specified point being measured against the specified line segment * @param pz * the Z coordinate of the specified point being measured against the specified line segment * @return a double value that is the square of the distance from the specified point to the specified line segment. * @see #ptLineDistSq(double,double,double,double, double, double, double, double, double) */ public static double ptSegDistSq(double x1, double y1, double z1, double x2, double y2, double z2, double px, double py, double pz) { // Adjust vectors relative to x1,y1,z1 // x2,y2,z2 becomes relative vector from x1,y1,z1 to end of segment x2 -= x1; y2 -= y1; z2 -= z1; // px,py,pz becomes relative vector from x1,y1,z1 to test point px -= x1; py -= y1; pz -= z1; double dotprod = px * x2 + py * y2 + pz * z2; double projlenSq; if (dotprod <= 0.0) { // px,py,pz is on the side of x1,y1,z1 away from x2,y2,z2 // distance to segment is length of px,py vector // "length of its (clipped) projection" is now 0.0 projlenSq = 0.0; } else { // switch to backwards vectors relative to x2,y2,z2 // x2,y2,z2 are already the negative of x1,y1,z1=>x2,y2,z2 // to get px,py,pz to be the negative of px,py,pz=>x2,y2,z2 // the dot product of two negated vectors is the same // as the dot product of the two normal vectors px = x2 - px; py = y2 - py; pz = z2 - pz; dotprod = px * x2 + py * y2 + pz * z2; if (dotprod <= 0.0) { // px,py,pz is on the side of x2,y2,z2 away from x1,y1,z1 // distance to segment is length of (backwards) px,py,pz vector // "length of its (clipped) projection" is now 0.0 projlenSq = 0.0; } else { // px,py is between x1,y1,z1 and x2,y2,z2 // dotprod is the length of the px,py,pz vector // projected on the x2,y2,z2=>x1,y1,z1 vector times the // length of the x2,y2,z2=>x1,y1,z1 vector projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2 + z2 * z2); } } // Distance to line is now the length of the relative point // vector minus the length of its projection onto the line // (which is zero if the projection falls outside the range // of the line segment). double lenSq = px * px + py * py + pz * pz - projlenSq; if (lenSq < 0) lenSq = 0; return lenSq; } /** * Returns the distance from a point to a line segment. * The distance measured is the distance between the specified point and the closest point between the specified end * points. If the specified point intersects the line segment in between the end points, this method returns 0.0. * * @param x1 * the X coordinate of the start point of the specified line segment * @param y1 * the Y coordinate of the start point of the specified line segment * @param z1 * the Z coordinate of the start point of the specified line segment * @param x2 * the X coordinate of the end point of the specified line segment * @param y2 * the Y coordinate of the end point of the specified line segment * @param z2 * the Z coordinate of the end point of the specified line segment * @param px * the X coordinate of the specified point being measured against the specified line segment * @param py * the Y coordinate of the specified point being measured against the specified line segment * @param pz * the Z coordinate of the specified point being measured against the specified line segment * @return a double value that is the distance from the specified point to the specified line segment. * @see #ptLineDist(double, double, double, double,double, double, double, double, double) */ public static double ptSegDist(double x1, double y1, double z1, double x2, double y2, double z2, double px, double py, double pz) { return Math.sqrt(ptSegDistSq(x1, y1, z1, x2, y2, z2, px, py, pz)); } /** * Returns the square of the distance from a point to this line segment. * The distance measured is the distance between the specified point and the closest point between the current * line's end points. If the specified point intersects the line segment in between the end points, this method * returns 0.0. * * @param px * the X coordinate of the specified point being measured against this line segment * @param py * the Y coordinate of the specified point being measured against this line segment * @param pz * the Z coordinate of the specified point being measured against this line segment * @return a double value that is the square of the distance from the specified point to the current line segment. * @see #ptLineDistSq(double, double,double) */ public double ptSegDistSq(double px, double py, double pz) { return ptSegDistSq(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), px, py, pz); } /** * Returns the square of the distance from a <code>Point3D</code> to this line segment. * The distance measured is the distance between the specified point and the closest point between the current * line's end points. If the specified point intersects the line segment in between the end points, this method * returns 0.0. * * @param pt * the specified <code>Point3D</code> being measured against this line segment. * @return a double value that is the square of the distance from the specified <code>Point3D</code> to the current * line segment. * @see #ptLineDistSq(Point3D) */ public double ptSegDistSq(Point3D pt) { return ptSegDistSq(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), pt.getX(), pt.getY(), pt.getZ()); } /** * Returns the distance from a point to this line segment. * The distance measured is the distance between the specified point and the closest point between the current * line's end points. If the specified point intersects the line segment in between the end points, this method * returns 0.0. * * @param px * the X coordinate of the specified point being measured against this line segment * @param py * the Y coordinate of the specified point being measured against this line segment * @param pz * the Z coordinate of the specified point being measured against this line segment * @return a double value that is the distance from the specified point to the current line segment. * @see #ptLineDist(double, double, double) */ public double ptSegDist(double px, double py, double pz) { return ptSegDist(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), px, py, pz); } /** * Returns the distance from a <code>Point3D</code> to this line segment. * The distance measured is the distance between the specified point and the closest point between the current * line's end points. If the specified point intersects the line segment in between the end points, this method * returns 0.0. * * @param pt * the specified <code>Point3D</code> being measured against this line segment * @return a double value that is the distance from the specified <code>Point3D</code> to the current line segment. * @see #ptLineDist(Point3D) */ public double ptSegDist(Point3D pt) { return ptSegDist(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), pt.getX(), pt.getY(), pt.getZ()); } /** * Returns the square of the distance from a point to a line. * The distance measured is the distance between the specified point and the closest point on the * infinitely-extended line defined by the specified coordinates. If the specified point intersects the line, this * method returns 0.0. * * @param x1 * the X coordinate of the start point of the specified line * @param y1 * the Y coordinate of the start point of the specified line * @param z1 * the Z coordinate of the start point of the specified line * @param x2 * the X coordinate of the end point of the specified line * @param y2 * the Y coordinate of the end point of the specified line * @param z2 * the Z coordinate of the end point of the specified line * @param px * the X coordinate of the specified point being measured against the specified line * @param py * the Y coordinate of the specified point being measured against the specified line * @param pz * the Z coordinate of the specified point being measured against the specified line * @return a double value that is the square of the distance from the specified point to the specified line. * @see #ptSegDistSq(double, double, double, double,double, double, double, double, double) */ public static double ptLineDistSq(double x1, double y1, double z1, double x2, double y2, double z2, double px, double py, double pz) { // Adjust vectors relative to x1,y1,z1 // x2,y2 becomes relative vector from x1,y1,z1 to end of segment x2 -= x1; y2 -= y1; z2 -= z1; // px,py,pz becomes relative vector from x1,y1,z1 to test point px -= x1; py -= y1; pz -= z1; double dotprod = px * x2 + py * y2 + pz * z2; // dotprod is the length of the px,py vector // projected on the x1,y1,z1=>x2,y2,z2 vector times the // length of the x1,y1,z1=>x2,y2,z2 vector double projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2 + z2 * z2); // Distance to line is now the length of the relative point // vector minus the length of its projection onto the line double lenSq = px * px + py * py + pz * pz - projlenSq; if (lenSq < 0) lenSq = 0; return lenSq; } /** * Returns the distance from a point to a line. * The distance measured is the distance between the specified point and the closest point on the * infinitely-extended line defined by the specified coordinates. If the specified point intersects the line, this * method returns 0.0. * * @param x1 * the X coordinate of the start point of the specified line * @param y1 * the Y coordinate of the start point of the specified line * @param z1 * the Z coordinate of the start point of the specified line * @param x2 * the X coordinate of the end point of the specified line * @param y2 * the Y coordinate of the end point of the specified line * @param z2 * the Z coordinate of the end point of the specified line * @param px * the X coordinate of the specified point being measured against the specified line * @param py * the Y coordinate of the specified point being measured against the specified line * @param pz * the Z coordinate of the specified point being measured against the specified line * @return a double value that is the distance from the specified point to the specified line. * @see #ptSegDist(double, double, double, double, double, double, double, double, double) */ public static double ptLineDist(double x1, double y1, double z1, double x2, double y2, double z2, double px, double py, double pz) { return Math.sqrt(ptLineDistSq(x1, y1, z1, x2, y2, z2, px, py, pz)); } /** * Returns the square of the distance from a point to this line. * The distance measured is the distance between the specified point and the closest point on the * infinitely-extended line defined by this <code>Line3D</code>. If the specified point intersects the line, this * method returns 0.0. * * @param px * the X coordinate of the specified point being measured against the specified line * @param py * the Y coordinate of the specified point being measured against the specified line * @param pz * the Z coordinate of the specified point being measured against the specified line * @return a double value that is the square of the distance from a specified point to the current line. * @see #ptSegDistSq(double, double, double) */ public double ptLineDistSq(double px, double py, double pz) { return ptLineDistSq(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), px, py, pz); } /** * Returns the square of the distance from a specified <code>Point3D</code> to this line. * The distance measured is the distance between the specified point and the closest point on the * infinitely-extended line defined by this <code>Line3D</code>. If the specified point intersects the line, this * method returns 0.0. * * @param pt * the specified <code>Point3D</code> being measured against this line * @return a double value that is the square of the distance from a specified <code>Point3D</code> to the current * line. * @see #ptSegDistSq(Point3D) */ public double ptLineDistSq(Point3D pt) { return ptLineDistSq(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), pt.getX(), pt.getY(), pt.getZ()); } /** * Returns the distance from a point to this line. * The distance measured is the distance between the specified point and the closest point on the * infinitely-extended line defined by this <code>Line3D</code>. If the specified point intersects the line, this * method returns 0.0. * * @param px * the X coordinate of the specified point being measured against this line * @param py * the Y coordinate of the specified point being measured against this line * @param pz * the Z coordinate of the specified point being measured against this line * @return a double value that is the distance from a specified point to the current line. * @see #ptSegDist(double, double, double) */ public double ptLineDist(double px, double py, double pz) { return ptLineDist(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), px, py, pz); } /** * Returns the distance from a <code>Point3D</code> to this line. * The distance measured is the distance between the specified point and the closest point on the * infinitely-extended line defined by this <code>Line3D</code>. If the specified point intersects the line, this * method returns 0.0. * * @param pt * the specified <code>Point3D</code> being measured * @return a double value that is the distance from a specified <code>Point3D</code> to the current line. * @see #ptSegDist(Point3D) */ public double ptLineDist(Point3D pt) { return ptLineDist(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2(), pt.getX(), pt.getY(), pt.getZ()); } /** * Tests if a specified coordinate is inside the boundary of this <code>Line3D</code>. This method is required to * implement the {@link Shape3D} interface, but in the case of <code>Line3D</code> objects it always returns * <code>false</code> since a line contains no area. * * @param x * the X coordinate of the specified point to be tested * @param y * the Y coordinate of the specified point to be tested * @param z * the Z coordinate of the specified point to be tested * @return <code>false</code> because a <code>Line3D</code> contains no area. */ @Override public boolean contains(double x, double y, double z) { return false; } /** * Tests if a given <code>Point3D</code> is inside the boundary of this <code>Line3D</code>. * This method is required to implement the {@link Shape3D} interface, but in the case of <code>Line3D</code> * objects it always returns <code>false</code> since a line contains no area. * * @param p * the specified <code>Point3D</code> to be tested * @return <code>false</code> because a <code>Line3D</code> contains no area. */ @Override public boolean contains(Point3D p) { return false; } /** * Tests if the interior of this <code>Line3D</code> entirely contains the specified set of rectangular coordinates. * This method is required to implement the <code>Shape3D</code> interface, but in the case of <code>Line3D</code> * objects it always returns false since a line contains no area. * * @param x * the X coordinate of the closest-upper-left corner of the specified 3D rectangular area * @param y * the Y coordinate of the closest-upper-left corner of the specified 3D rectangular area * @param z * the Z coordinate of the closest-upper-left corner of the specified 3D rectangular area * @param sizeX * the width of the specified 3D rectangular area * @param sizeY * the height of the specified 3D rectangular area * @param sizeZ * the depth of the specified 3D rectangular area * @return <code>false</code> because a <code>Line3D</code> contains * no area. */ @Override public boolean contains(double x, double y, double z, double sizeX, double sizeY, double sizeZ) { return false; } /** * Tests if the interior of this <code>Line3D</code> entirely contains the specified <code>Rectangle3D</code>. * This method is required to implement the <code>Shape3D</code> interface, but in the case of <code>Line3D</code> * objects it always returns <code>false</code> since a line contains no area. * * @param r * the specified <code>Rectangle3D</code> to be tested * @return <code>false</code> because a <code>Line3D</code> contains no area. */ @Override public boolean contains(Rectangle3D r) { return false; } @Override public boolean intersects(double x, double y, double z, double sizeX, double sizeY, double sizeZ) { return intersects(new Rectangle3D.Double(x, y, z, sizeX, sizeY, sizeZ)); } @Override public boolean intersects(Rectangle3D r) { return r.intersectsLine(getX1(), getY1(), getZ1(), getX2(), getY2(), getZ2()); } /** * Creates a new object of the same class as this object. * * @return a clone of this instance. * @exception OutOfMemoryError * if there is not enough memory. * @see java.lang.Cloneable */ @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } }