/* * __ .__ .__ ._____. * _/ |_ _______ __|__| ____ | | |__\_ |__ ______ * \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/ * | | ( <_> > <| \ \___| |_| || \_\ \\___ \ * |__| \____/__/\_ \__|\___ >____/__||___ /____ > * \/ \/ \/ \/ * * Copyright (c) 2006-2011 Karsten Schmidt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * http://creativecommons.org/licenses/LGPL/2.1/ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ package spimedb.util.geom; import spimedb.util.math.MathUtils; /** * Class to describe and work with infinite generic 3D planes. Useful for * intersection problems and classifying points. */ //@XmlAccessorType(XmlAccessType.FIELD) public class Plane extends Vec3D implements Shape3D { /** * Classifier constant for {@link Plane#classifyPoint(roVec3D, float)} */ public enum Classifier { FRONT, BACK, ON_PLANE } public static final Plane XY = new Plane(new Vec3D(), Vec3D.Z_AXIS); public static final Plane XZ = new Plane(new Vec3D(), Vec3D.Y_AXIS); public static final Plane YZ = new Plane(new Vec3D(), Vec3D.X_AXIS); //@XmlElement(required = true) public Vec3D normal; public Plane() { super(); normal = Vec3D.Y_AXIS.copy(); } public Plane(roVec3D origin, roVec3D norm) { super(origin); normal = norm.getNormalized(); } public Plane(Triangle3D t) { this(t.computeCentroid(), t.computeNormal()); } /** * Classifies the relative position of the given point to the plane using * the given tolerance. * * @return One of the 3 classification types: FRONT, BACK, ON_PLANE */ public Classifier classifyPoint(XYZ p, float tolerance) { float d = this.sub(p).normalize().dot(normal); if (d < -tolerance) { return Classifier.FRONT; } else if (d > tolerance) { return Classifier.BACK; } return Classifier.ON_PLANE; } public boolean containsPoint(XYZ p) { return classifyPoint(p, MathUtils.EPS) == Classifier.ON_PLANE; } public float getDCoeff() { return this.dot(normal); } /** * Calculates distance from the plane to point P. * * @param p * @return distance */ public float getDistanceToPoint(Vec3D p) { float sn = -normal.dot(p.sub(this)); float sd = normal.magSquared(); Vec3D isec = p.plus(normal.scale(sn / sd)); return isec.distanceTo(p); } /** * Calculates the intersection point between plane and ray (line). * * @param r * @return intersection point or null if ray doesn't intersect plane */ public roVec3D getIntersectionWithRay(Ray3D r) { float denom = normal.dot(r.getDirection()); if (denom > MathUtils.EPS) { float u = normal.dot(this.sub(r)) / denom; return r.getPointAtDistance(u); } else { return null; } } public XYZ getProjectedPoint(Vec3D p) { Vec3D dir; if (normal.dot(sub(p)) < 0) { dir = normal.getInverted(); } else { dir = normal; } XYZ proj = new Ray3D(p, dir) .getPointAtDistance(getDistanceToPoint(p)); return proj; } /** * Calculates the distance of the vector to the given plane in the specified * direction. A plane is specified by a 3D point and a normal vector * perpendicular to the plane. Normalized directional vectors expected (for * rayDir and planeNormal). * * @param ray * intersection ray * @return distance to plane in world units, -1 if no intersection. */ public float intersectRayDistance(Ray3D ray) { float d = -normal.dot(this); float numer = normal.dot(ray) + d; float denom = normal.dot(ray.dir); // normal is orthogonal to vector, cant intersect if (Math.abs(denom) < MathUtils.EPS) { return -1; } return -(numer / denom); } /** * Computes the intersection ray between this plane and the given one. If * the planes are parallel or coincident the method returns null. If the * planes are intersecting, the returned {@link Ray3D} will start at a point * lying on both planes and point along the infinite intersection line * between them. * * Code ported from: * http://forums.create.msdn.com/forums/p/39074/234178.aspx#234178 * * @param plane * intersection partner * @return intersection ray or null */ public Ray3D intersectsPlane(Plane plane) { float d = getDCoeff(); float d2 = plane.getDCoeff(); if (normal.equalsWithTolerance(plane.normal, 0.0001f) || d == d2) { return null; } float offDiagonal = normal.dot(plane.normal); double det = 1.0 / (1 - offDiagonal * offDiagonal); double a = (d - d2 * offDiagonal) * det; double b = (d2 - d * offDiagonal) * det; Vec3D anchor = normal.scale((float) a).addSelf( plane.normal.scale((float) b)); Vec3D dir = normal.cross(plane.normal); return new Ray3D(anchor, dir); } // /** // * Creates a TriangleMesh representation of the plane as a finite, squared // * quad of the requested size, centred around the current plane point. // * // * @param size // * desired edge length // * @return mesh // */ // public Mesh3D toMesh(float size) { // return toMesh(null, size); // } // // public Mesh3D toMesh(Mesh3D mesh, float size) { // if (mesh == null) { // mesh = new TriangleMesh("plane", 4, 2); // } // roVec3D p = equalsWithTolerance(Vec3D.ZERO, 0.01f) ? add(0.01f, // 0.01f, 0.01f) : this; // size *= 0.5f; // Vec3D n = p.cross(normal).normalizeTo(size); // Vec3D m = n.cross(normal).normalizeTo(size); // Vec3D a = this.add(n).addSelf(m); // Vec3D b = this.add(n).subSelf(m); // Vec3D c = this.sub(n).subSelf(m); // Vec3D d = this.sub(n).addSelf(m); // mesh.addFace(a, d, b, null, null, null, null); // mesh.addFace(b, d, c, null, null, null, null); // return mesh; // } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("origin: ").append(super.toString()).append(" norm: ") .append(normal.toString()); return sb.toString(); } }