/**
*
*/
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();
}
}
}