/**
*
*/
package cz.cuni.mff.peckam.java.origamist.math;
import static cz.cuni.mff.peckam.java.origamist.math.MathHelper.EPSILON;
import static java.lang.Math.abs;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
/**
* A plane in 3D.
*
* @author Martin Pecka
*/
public class Plane3d implements Cloneable
{
/**
* A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
protected double a;
/**
* A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
protected double b;
/**
* A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
protected double c;
/**
* A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
protected double d;
/** The normal of the plane. */
protected Vector3d normal = null;
/**
* Constructs a standalone copy (clone) of this plane.
*
* @param plane The plane to copy.
*/
public Plane3d(Plane3d plane)
{
a = plane.a;
b = plane.b;
c = plane.c;
d = plane.d;
}
/**
* Constructs a plane containing the given three points.
*
* @param p1 A point in the plane.
* @param p2 A point in the plane.
* @param p3 A point in the plane.
*
* @throws IllegalArgumentException If the points are colinear.
*/
public Plane3d(Point3d p1, Point3d p2, Point3d p3) throws IllegalArgumentException
{
if (new Line3d(p1, p2).contains(p3)) {
throw new IllegalArgumentException("Trying to define a plane using three colinear points.");
}
a = p1.y * (p2.z - p3.z) + p2.y * (p3.z - p1.z) + p3.y * (p1.z - p2.z);
b = p1.z * (p2.x - p3.x) + p2.z * (p3.x - p1.x) + p3.z * (p1.x - p2.x);
c = p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y);
d = -p1.x * (p2.y * p3.z - p3.y * p2.z) - p2.x * (p3.y * p1.z - p1.y * p3.z) - p3.x
* (p1.y * p2.z - p2.y * p1.z);
}
/**
* Constructs a plane containing the given three points.
*
* @param p1x x-coordinate of a point in the plane.
* @param p1y y-coordinate of a point in the plane.
* @param p1z z-coordinate of a point in the plane.
* @param p2x x-coordinate of a point in the plane.
* @param p2y y-coordinate of a point in the plane.
* @param p2z z-coordinate of a point in the plane.
* @param p3x x-coordinate of a point in the plane.
* @param p3y y-coordinate of a point in the plane.
* @param p3z z-coordinate of a point in the plane.
*
* @throws IllegalArgumentException If the points are colinear.
*/
public Plane3d(double p1x, double p1y, double p1z, double p2x, double p2y, double p2z, double p3x, double p3y,
double p3z) throws IllegalArgumentException
{
this(new Point3d(p1x, p1y, p1z), new Point3d(p2x, p2y, p2z), new Point3d(p3x, p3y, p3z));
}
/**
* Create a plane with the given normal and containing the given point.
*
* @param normal
* @param point
*/
public Plane3d(Vector3d normal, Point3d point)
{
this.a = normal.x;
this.b = normal.y;
this.c = normal.z;
this.d = -normal.dot(new Vector3d(point));
}
public boolean contains(Point3d point)
{
return abs(a * point.x + b * point.y + c * point.z + d) < EPSILON;
}
/**
* Return the intersection line of this plane and the <code>other</code> plane.
*
* @param other The other plane to find intersection with.
* @return The intersection line or <code>null</code>, if the planes are equal or parallel (to distinguish these two
* states, use {@link Plane3d#epsilonEquals(Plane3d)}.)
*
* @see http://local.wasp.uwa.edu.au/~pbourke/geometry/planeplane/
*/
public Line3d getIntersection(Plane3d other)
{
Vector3d normal = new Vector3d(getNormal());
Vector3d oNormal = new Vector3d(other.getNormal());
Vector3d lineDir = new Vector3d();
lineDir.cross(normal, oNormal);
if (lineDir.epsilonEquals(new Vector3d(), EPSILON))
return null;
normal.normalize();
oNormal.normalize();
lineDir.normalize();
// the quotients must exist - they're just quotients of a vector and its normalized form
double d1 = -d * MathHelper.vectorQuotient3d(normal, getNormal());
double d2 = -other.d * MathHelper.vectorQuotient3d(oNormal, other.getNormal());
double n1n2 = normal.dot(oNormal);
double det = 1 - n1n2 * n1n2;
double c1 = (d1 - d2 * n1n2) / det;
double c2 = (d2 - d1 * n1n2) / det;
Point3d c1n1 = new Point3d(normal);
c1n1.scale(c1);
Point3d c2n2 = new Point3d(oNormal);
c2n2.scale(c2);
Point3d linePoint = new Point3d(c1n1);
linePoint.add(c2n2);
return new Line3d(linePoint, lineDir);
}
/**
* Return the intersection point of this plane and the given line.
*
* @param line The line to search for intersection with.
* @return The intersection point (as a line with zero direction vector), or <code>null</code> if the line is
* parallel to the plane and not lying in it, or return <code>line</code> (if it lies in this plane).
*/
public Line3d getIntersection(Line3d line)
{
// just substitute the parametric equation of the line into the general equation of the plane and extract the
// line parameter
Vector3d normal = getNormal();
if (Math.abs(normal.dot(line.v)) < EPSILON) {// the line is perpendicular to the plane
if (this.contains(line.p)) {
return line;
}
return null;
}
Vector3d pVect = new Vector3d(line.p);
double t = -((normal.dot(pVect) + d) / normal.dot(line.v));
Point3d p = new Point3d(line.p.x + t * line.v.x, line.p.y + t * line.v.y, line.p.z + t * line.v.z);
return new Line3d(p, new Vector3d());
}
/**
* @return The normal vector of this plane.
*/
public Vector3d getNormal()
{
if (normal == null)
normal = new Vector3d(a, b, c);
return normal;
}
/**
* Return the distance of the given point from this plane.
*
* @param p The point to get distance of.
* @return The distance of the given point from this plane.
*/
public double distance(Point3d p)
{
return Math.abs(signedDistance(p));
}
/**
* Return the distance of the given point from this plane. Will be negative if the point is on the other side than
* the normal of this plane.
*
* @param p The point to get distance of.
* @return The signed distance of the given point from this plane.
*/
public double signedDistance(Point3d p)
{
return (a * p.x + b * p.y + c * p.z + d) / Math.sqrt(a * a + b * b + c * c);
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(a);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(b);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(c);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(d);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Plane3d other = (Plane3d) obj;
if (Double.doubleToLongBits(a) != Double.doubleToLongBits(other.a))
return false;
if (Double.doubleToLongBits(b) != Double.doubleToLongBits(other.b))
return false;
if (Double.doubleToLongBits(c) != Double.doubleToLongBits(other.c))
return false;
if (Double.doubleToLongBits(d) != Double.doubleToLongBits(other.d))
return false;
return true;
}
/**
* Return <code>true</code> if the given plane is almost equal to this one.
*
* @param other The plane to compare.
* @return <code>true</code> if the given plane is almost equal to this one.
*/
public boolean epsilonEquals(Plane3d other)
{
if (other == null)
return false;
Double quotient = MathHelper.vectorQuotient3d(normal, other.normal);
if (quotient == null)
return false;
return Math.abs(this.d - quotient * other.d) < MathHelper.EPSILON;
}
@Override
protected Plane3d clone()
{
return new Plane3d(this);
}
@Override
public String toString()
{
return "Plane3d [" + a + "x + " + b + "y + " + c + "z + " + d + " = 0]";
}
/**
* @return A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public double getA()
{
return a;
}
/**
* @param a A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public void setA(double a)
{
this.a = a;
normal = null;
}
/**
* @return A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public double getB()
{
return b;
}
/**
* @param b A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public void setB(double b)
{
this.b = b;
normal = null;
}
/**
* @return A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public double getC()
{
return c;
}
/**
* @param c A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public void setC(double c)
{
this.c = c;
normal = null;
}
/**
* @return A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public double getD()
{
return d;
}
/**
* @param d A coeficient in the general equation of a plane: ax + by + cz + d = 0.
*/
public void setD(double d)
{
this.d = d;
normal = null;
}
}