/** * */ package cz.cuni.mff.peckam.java.origamist.math; import static cz.cuni.mff.peckam.java.origamist.math.MathHelper.EPSILON; import java.util.Collection; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; /** * Represents a half-space in 3D. * * @author Martin Pecka */ public class HalfSpace3d implements Cloneable { /** * The border plane of this halfspace. The points in the halfspace are defined as the points (x,y,z), for which ax + * by + cz + d >= 0 holds, where a,b,c,d are the general form coefficients of the plane. */ protected Plane3d plane; /** * Create a halfspace with the given plane as the border plane and that contains the part of space where the planes * normal points. * * @param plane The border plane. */ public HalfSpace3d(Plane3d plane) { this.plane = plane; } /** * Create a halfspace with its border plane perpendicular to the given normal and containing the given point, and * that contains the part of space where the given normal points. * * @param normal the normal of the border plane, also defines the half of the space that is contained. * @param point A point on the border plane. */ public HalfSpace3d(Vector3d normal, Point3d point) { this(new Plane3d(normal, point)); } /** * Create a halfspace defined by the plane containing p1,p2,p3. The halfspace contains a general point r. * * @param p1 A point in the border plane. * @param p2 A point in the border plane. * @param p3 A point in the border plane. * @param r A general point in the halfspace. * * @throws IllegalArgumentException If r lies in the plane defined by p1, p2 and p3. */ public HalfSpace3d(Point3d p1, Point3d p2, Point3d p3, Point3d r) throws IllegalArgumentException { plane = new Plane3d(p1, p2, p3); if (plane.contains(r)) { throw new IllegalArgumentException("Trying to define a halfspace using a plane and a point lying in it."); } if (!contains(r)) { invert(); } } /** * Create a halfspace defined by the plane containing p1,p2,p3. The halfspace contains a general point r. * * @param p1x X coordinate of a point in the border plane. * @param p1y Y coordinate of a point in the border plane. * @param p1z Z coordinate of a point in the border plane. * @param p2x X coordinate of a point in the border plane. * @param p2y Y coordinate of a point in the border plane. * @param p2z Z coordinate of a point in the border plane. * @param p3x X coordinate of a point in the border plane. * @param p3y Y coordinate of a point in the border plane. * @param p3z Z coordinate of a point in the border plane. * @param rx X coordinate of a general point in the halfspace. * @param ry Y coordinate of a general point in the halfspace. * @param rz Z coordinate of a general point in the halfspace. * * @throws IllegalArgumentException If r lies in the plane defined by p1, p2 and p3. */ public HalfSpace3d(double p1x, double p1y, double p1z, double p2x, double p2y, double p2z, double p3x, double p3y, double p3z, double rx, double ry, double rz) throws IllegalArgumentException { this(new Point3d(p1x, p1y, p1z), new Point3d(p2x, p2y, p2z), new Point3d(p3x, p3y, p3z), new Point3d(rx, ry, rz)); } /** * Returns true if this halfspace contains the given point. * * @param point The point to check. * @return Whether this halfspace contains the given point. */ public boolean contains(Point3d point) { return plane.a * point.x + plane.b * point.y + plane.c * point.z + plane.d >= -EPSILON; } /** * Return true if this halfspace contains the given point, but its distance from the border plane is nonzero. * * @param point The point to check. * @return Whether this halfspace contains the given point and its border plane does not. */ public boolean containsExclusive(Point3d point) { return plane.signedDistance(point) > EPSILON; } /** * Return true if this halfspace contains all the given triangles and at least one of their points is contained * exclusively. * * @param triangles The list of triangles to check. * @return Whether the triangles are contained or not. */ public boolean containsTriangles(Collection<? extends Triangle3d> triangles) { for (Triangle3d t : triangles) { for (Point3d p : t.getVertices()) { double sDist = plane.signedDistance(p); if (sDist > EPSILON) return true; else if (sDist < -EPSILON) return false; } } return false; } /** * Invert this halfspace to contain the other half of the space. * * @return this */ public HalfSpace3d invert() { plane.setA(-plane.getA()); plane.setB(-plane.getB()); plane.setC(-plane.getC()); plane.setD(-plane.getD()); return this; } /** * Create a halfspace perpendicular to the plane containing the given triangle. The halfspace is further defined by * the first two points of the triangle (which lie in the border plane); the third point (p3) of the triangle is the * general point somewhere in the halfspace. * * @param p1 A point in the border plane of the new halfspace. * @param p2 A point in the border plane of the new halfspace. * @param r A general point of the new halfspace - defines the triangle together with p1, p2. * @return The halfspace as defined above. */ public static HalfSpace3d createPerpendicularToTriangle(Point3d p1, Point3d p2, Point3d r) { Vector3d v1 = new Vector3d(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); Vector3d v2 = new Vector3d(r.x - p1.x, r.y - p1.y, r.z - p1.z); Vector3d c = new Vector3d(); c.cross(v1, v2); c.normalize(); // added to improve robustness against rounding errors c.add(p1); Point3d p = new Point3d(c); return new HalfSpace3d(p1, p2, p, r); } /** * Create a halfspace perpendicular to the plane containing the given triangle. The halfspace is further defined by * the first two points of the triangle (which lie in the border plane); the third point (r) of the triangle is the * general point somewhere in the halfspace. * * @param p1x x-coordinate of a point in the border plane of the new halfspace. * @param p1y y-coordinate of a point in the border plane of the new halfspace. * @param p1z z-coordinate of a point in the border plane of the new halfspace. * @param p2x x-coordinate of a point in the border plane of the new halfspace. * @param p2y y-coordinate of a point in the border plane of the new halfspace. * @param p2z z-coordinate of a point in the border plane of the new halfspace. * @param rx x-coordinate of a general point of the new halfspace - defines the triangle together with p1, p2. * @param ry y-coordinate of a general point of the new halfspace - defines the triangle together with p1, p2. * @param rz z-coordinate of a general point of the new halfspace - defines the triangle together with p1, p2. * @return The halfspace as defined above. */ public static HalfSpace3d createPerpendicularToTriangle(double p1x, double p1y, double p1z, double p2x, double p2y, double p2z, double rx, double ry, double rz) { return createPerpendicularToTriangle(new Point3d(p1x, p1y, p1z), new Point3d(p2x, p2y, p2z), new Point3d(rx, ry, rz)); } /** * @return The border plane of this halfspace. */ public Plane3d getPlane() { return plane; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((plane == null) ? 0 : plane.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HalfSpace3d other = (HalfSpace3d) obj; if (plane == null) { if (other.plane != null) return false; } else if (!plane.equals(other.plane)) return false; return true; } /** * Return <code>true</code> if the given halfspance is almost equal to this one. * * @param other The halfspace to compare. * @return <code>true</code> if the given halfspace is almost equal to this one. */ public boolean epsilonEquals(HalfSpace3d other) { if (other == null) return false; return plane.epsilonEquals(other.getPlane()); } @Override public String toString() { return "HalfSpace3d [" + plane.a + "x + " + plane.b + "y + " + plane.c + "z + " + plane.d + " >= 0]"; } @Override protected HalfSpace3d clone() { return new HalfSpace3d(plane.clone()); } }