/*
* Copyright (c) 2010, Frederik Vanhoutte 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 wblut.geom;
import wblut.WB_Epsilon;
import wblut.math.WB_Fast;
// TODO: Auto-generated Javadoc
/**
* 3D plane.
*
*/
public class WB_Plane {
/**
* Xy.
*
* @return the w b_ plane
*/
public static final WB_Plane XY() {
return new WB_Plane(0, 0, 0, 0, 0, 1);
}
/**
* Xz.
*
* @return the w b_ plane
*/
public static final WB_Plane XZ() {
return new WB_Plane(0, 0, 0, 0, 1, 0);
}
/**
* Yz.
*
* @return the w b_ plane
*/
public static final WB_Plane YZ() {
return new WB_Plane(0, 0, 0, 1, 0, 0);
}
/** Plane normal. */
private WB_Normal3d n;
/** Origin. */
private WB_Point3d origin;
/** d-parameter: p.n = d with p point on plane, n the normal and . the dot product. */
private double d;
/** planar coordinate system. */
private WB_Vector3d u, v;
/**
* Instantiates a new WB_Plane.
*
* @param p1 first point on plane
* @param p2 second point on plane
* @param p3 third point on plane
*/
public WB_Plane(final WB_Point3d p1, final WB_Point3d p2,
final WB_Point3d p3) {
p1.get();
final WB_Vector3d v21 = p2.subToVector(p1);
final WB_Vector3d v31 = p3.subToVector(p1);
n = new WB_Normal3d(v21.cross(v31));
n.normalize();
d = n.x * p1.x + n.y * p1.y + n.z * p1.z;
origin = p1.get();
setAxes();
}
/**
* Set plane.
*
* @param p1 first point on plane
* @param p2 second point on plane
* @param p3 third point on plane
*/
public void set(final WB_Point3d p1, final WB_Point3d p2,
final WB_Point3d p3) {
p1.get();
final WB_Vector3d v21 = p2.subToVector(p1);
final WB_Vector3d v31 = p3.subToVector(p1);
n = new WB_Normal3d(v21.cross(v31));
n.normalize();
d = n.x * p1.x + n.y * p1.y + n.z * p1.z;
origin = p1.get();
setAxes();
}
/**
* Instantiates a new WB_Plane.
*
* @param o origin
* @param n normal
*/
public WB_Plane(final WB_Point3d o, final WB_Vector3d n) {
origin = o.get();
this.n = new WB_Normal3d(n);
this.n.normalize();
d = this.n.dot(origin);
setAxes();
}
/**
* Set plane.
*
* @param o origin
* @param n normal
*/
public void set(final WB_Point3d o, final WB_Vector3d n) {
origin = o.get();
this.n = new WB_Normal3d(n);
this.n.normalize();
d = this.n.dot(origin);
setAxes();
}
/**
* Instantiates a new WB_Plane.
*
* @param ox x-coordinate of origin
* @param oy y-coordinate of origin
* @param oz z-coordinate of origin
* @param nx x-coordinate of normal
* @param ny y-coordinate of normal
* @param nz z-coordinate of normal
*/
public WB_Plane(final double ox, final double oy, final double oz,
final double nx, final double ny, final double nz) {
origin = new WB_Point3d(ox, oy, oz);
n = new WB_Normal3d(nx, ny, nz);
n.normalize();
d = n.dot(origin);
setAxes();
}
/**
* Set plane.
*
* @param ox x-coordinate of origin
* @param oy y-coordinate of origin
* @param oz z-coordinate of origin
* @param nx x-coordinate of normal
* @param ny y-coordinate of normal
* @param nz z-coordinate of normal
*/
public void set(final double ox, final double oy, final double oz,
final double nx, final double ny, final double nz) {
origin = new WB_Point3d(ox, oy, oz);
n = new WB_Normal3d(nx, ny, nz);
n.normalize();
d = n.dot(origin);
setAxes();
}
/**
* Instantiates a new WB_Plane.
*
* @param o origin
* @param n normal
*/
public WB_Plane(final WB_Point3d o, final WB_Normal3d n) {
origin = o.get();
this.n = n.get();
this.n.normalize();
d = this.n.dot(origin);
setAxes();
}
/**
* Set plane.
*
* @param o origin
* @param n normal
*/
public void set(final WB_Point3d o, final WB_Normal3d n) {
origin = o.get();
this.n = n.get();
this.n.normalize();
d = this.n.dot(origin);
setAxes();
}
/**
* Instantiates a new WB_Plane.
*
* @param n normal
* @param d d-parameter: p.n=d, for any point p on the plane
*/
public WB_Plane(final WB_Vector3d n, final double d) {
this.n = new WB_Normal3d(n);
this.n.normalize();
this.d = d;
origin = WB_Intersection.closestPoint(new WB_Point3d(), this);
setAxes();
}
/**
* Set plane.
*
* @param n normal
* @param d d-parameter
*/
public void set(final WB_Vector3d n, final double d) {
this.n = new WB_Normal3d(n);
this.n.normalize();
this.d = d;
origin = WB_Intersection.closestPoint(new WB_Point3d(), this);
setAxes();
}
/**
* Instantiates a new WB_Plane.
*
* @param n normal
* @param d d-parameter
*/
public WB_Plane(final WB_Normal3d n, final double d) {
this.n = n.get();
this.n.normalize();
this.d = d;
origin = WB_Intersection.closestPoint(new WB_Point3d(), this);
setAxes();
}
/**
* Set plane.
*
* @param n normal
* @param d d-parameter
*/
public void set(final WB_Normal3d n, final double d) {
this.n = n.get();
this.n.normalize();
this.d = d;
origin = WB_Intersection.closestPoint(new WB_Point3d(), this);
setAxes();
}
/**
* Get copy.
*
* @return copy
*/
public WB_Plane get() {
return new WB_Plane(origin, n);
}
/**
* Get plane normal.
*
* @return copy of plane normal
*/
public WB_Normal3d getNormal() {
return n.get();
}
/**
* Get d.
*
* @return d
*/
public double d() {
return d;
}
/**
* Get origin.
*
* @return origin, if plane was not created using points then this returns the projection of (0,0,0) on the plane
*/
public WB_Point3d getOrigin() {
return origin;
}
/**
* Flip normal.
*/
public void flipNormal() {
n.mult(-1);
setAxes();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Plane o: [" + origin + "] n: [" + n + "] d: [" + d + "]";
}
/**
* Classify point to plane.
*
* @param p point
* @return WB.ClassifyPointToPlane.POINT_IN_FRONT_OF_PLANE, WB.ClassifyPointToPlane.POINT_BEHIND_PLANE, WB.ClassifyPointToPlane.POINT_ON_PLANE
*/
public WB_ClassifyPointToPlane classifyPointToPlane(final WB_Point3d p) {
final double dist = getNormal().dot(p) - d();
if (dist > WB_Epsilon.PLANE_EPSILON) {
return WB_ClassifyPointToPlane.POINT_IN_FRONT_OF_PLANE;
}
if (dist < -WB_Epsilon.PLANE_EPSILON) {
return WB_ClassifyPointToPlane.POINT_BEHIND_PLANE;
}
return WB_ClassifyPointToPlane.POINT_ON_PLANE;
}
/**
* Classify point to plane.
*
* @param p point
* @param P plane
* @return WB.ClassifyPointToPlane.POINT_IN_FRONT_OF_PLANE, WB.ClassifyPointToPlane.POINT_BEHIND_PLANE, WB.ClassifyPointToPlane.POINT_ON_PLANE
*/
public static WB_ClassifyPointToPlane classifyPointToPlane(
final WB_Point3d p, final WB_Plane P) {
final double dist = P.getNormal().dot(p) - P.d();
if (dist > WB_Epsilon.PLANE_EPSILON) {
return WB_ClassifyPointToPlane.POINT_IN_FRONT_OF_PLANE;
}
if (dist < -WB_Epsilon.PLANE_EPSILON) {
return WB_ClassifyPointToPlane.POINT_BEHIND_PLANE;
}
return WB_ClassifyPointToPlane.POINT_ON_PLANE;
}
/**
* Check if points lies on positive side of plane defined by 3 clockwise points.
*
* @param p point to check
* @param a the a
* @param b the b
* @param c the c
* @return true, if successful
*/
public static boolean pointOutsideOfPlane(final WB_Point3d p,
final WB_Point3d a, final WB_Point3d b, final WB_Point3d c) {
return (p.subToVector(a))
.dot((b.subToVector(a)).cross(c.subToVector(a))) >= 0;
}
/**
* Check if points lies on other side of plane compared with reference points.
*
* @param p point to check
* @param q reference point
* @param a the a
* @param b the b
* @param c the c
* @return true, if successful
*/
public static boolean pointOtherSideOfPlane(final WB_Point3d p,
final WB_Point3d q, final WB_Point3d a, final WB_Point3d b,
final WB_Point3d c) {
final double signp = (p.subToVector(a)).dot((b.subToVector(a)).cross(c
.subToVector(a)));
final double signq = (q.subToVector(a)).dot((b.subToVector(a)).cross(c
.subToVector(a)));
return signp * signq <= 0;
}
/**
* Classify polygon to plane.
*
* @param poly the poly
* @return the w b_ classify polygon to plane
*/
public WB_ClassifyPolygonToPlane classifyPolygonToPlane(
final WB_Polygon poly) {
int numInFront = 0;
int numBehind = 0;
for (int i = 0; i < poly.getN(); i++) {
switch (classifyPointToPlane(poly.getPoint(i))) {
case POINT_IN_FRONT_OF_PLANE:
numInFront++;
break;
case POINT_BEHIND_PLANE:
numBehind++;
break;
}
if (numBehind > 0 && numInFront > 0) {
return WB_ClassifyPolygonToPlane.POLYGON_STRADDLING_PLANE;
}
}
if (numInFront > 0) {
return WB_ClassifyPolygonToPlane.POLYGON_IN_FRONT_OF_PLANE;
}
if (numBehind > 0) {
return WB_ClassifyPolygonToPlane.POLYGON_BEHIND_PLANE;
}
return WB_ClassifyPolygonToPlane.POLYGON_ON_PLANE;
}
/**
* Classify polygon to plane.
*
* @param poly the poly
* @param P the p
* @return the w b_ classify polygon to plane
*/
public static WB_ClassifyPolygonToPlane classifyPolygonToPlane(
final WB_Polygon poly, final WB_Plane P) {
int numInFront = 0;
int numBehind = 0;
for (int i = 0; i < poly.getN(); i++) {
switch (classifyPointToPlane(poly.getPoint(i), P)) {
case POINT_IN_FRONT_OF_PLANE:
numInFront++;
break;
case POINT_BEHIND_PLANE:
numBehind++;
break;
}
if (numBehind != 0 && numInFront != 0) {
return WB_ClassifyPolygonToPlane.POLYGON_STRADDLING_PLANE;
}
}
if (numInFront != 0) {
return WB_ClassifyPolygonToPlane.POLYGON_IN_FRONT_OF_PLANE;
}
if (numBehind != 0) {
return WB_ClassifyPolygonToPlane.POLYGON_BEHIND_PLANE;
}
return WB_ClassifyPolygonToPlane.POLYGON_ON_PLANE;
}
/**
* Are the planes equal?.
*
* @param P the p
* @param Q the q
* @return true/false
*/
public static boolean isEqual(final WB_Plane P, final WB_Plane Q) {
if (!WB_Epsilon.isZeroSq(WB_Distance.sqDistance(P.getOrigin(), Q))) {
return false;
}
if (!WB_Epsilon.isZeroSq(WB_Distance.sqDistance(Q.getOrigin(), P))) {
return false;
}
if (!P.getNormal().isParallelNorm(Q.getNormal())) {
return false;
}
return true;
}
/**
* Sets the axes.
*/
private void setAxes() {
final double x = WB_Fast.abs(n.x);
final double y = WB_Fast.abs(n.y);
if (x >= y) {
u = new WB_Vector3d(n.z, 0, -n.x);
} else {
u = new WB_Vector3d(0, n.z, -n.y);
}
u.normalize();
v = WB_Vector3d.cross(n, u);
}
// Return coordinates relative to plane axes
/**
* Local point.
*
* @param p the p
* @return the w b_ point3d
*/
public WB_Point3d localPoint(final WB_Point3d p) {
return new WB_Point3d(u.x * (p.x - origin.x) + u.y * (p.y - origin.y)
+ u.z * (p.z - origin.z), v.x * (p.x - origin.x) + v.y
* (p.y - origin.y) + v.z * (p.z - origin.z), n.x
* (p.x - origin.x) + n.y * (p.y - origin.y) + n.z
* (p.z - origin.z));
}
/**
* Local point2 d.
*
* @param p the p
* @return the w b_ point2d
*/
public WB_Point2d localPoint2D(final WB_Point3d p) {
return new WB_Point2d(u.x * (p.x - origin.x) + u.y * (p.y - origin.y)
+ u.z * (p.z - origin.z), v.x * (p.x - origin.x) + v.y
* (p.y - origin.y) + v.z * (p.z - origin.z));
}
// Return embedded point coordinates relative to world axes
/**
* Extract point.
*
* @param p the p
* @return the w b_ point3d
*/
public WB_Point3d extractPoint(final WB_Point2d p) {
return new WB_Point3d(origin.x + p.x * u.x + p.y * v.x, origin.y + p.x
* u.y + p.y * v.y, origin.z + p.x * u.z + p.y * v.z);
}
// Return embedded point coordinates relative to world axes
/**
* Extract point.
*
* @param x the x
* @param y the y
* @return the w b_ point3d
*/
public WB_Point3d extractPoint(final double x, final double y) {
return new WB_Point3d(origin.x + x * u.x + y * v.x, origin.y + x * u.y
+ y * v.y, origin.z + x * u.z + y * v.z);
}
// Return coordinates relative to world axes
/**
* Extract point.
*
* @param p the p
* @return the w b_ point3d
*/
public WB_Point3d extractPoint(final WB_Point3d p) {
return new WB_Point3d(origin.x + p.x * u.x + p.y * v.x + p.z * n.x,
origin.y + p.x * u.y + p.y * v.y + p.z * n.y, origin.z + p.x
* u.z + p.y * v.z + p.z * n.z);
}
// Return coordinates relative to world axes
/**
* Extract point.
*
* @param x the x
* @param y the y
* @param z the z
* @return the w b_ point3d
*/
public WB_Point3d extractPoint(final double x, final double y,
final double z) {
return new WB_Point3d(origin.x + x * u.x + y * v.x + z * n.x, origin.y
+ x * u.y + y * v.y + z * n.y, origin.z + x * u.z + y * v.z + z
* n.z);
}
// Return new point mirrored across plane
/**
* Mirror point.
*
* @param p the p
* @return the w b_ point3d
*/
public WB_Point3d mirrorPoint(final WB_Point3d p) {
if (WB_Epsilon.isZero(WB_Distance.distance(p, this))) {
return p.get();
}
return extractPoint(localPoint(p).scale(1, 1, -1));
}
// Return copy of u coordinate axis in world coordinates
/**
* Gets the u.
*
* @return the u
*/
public WB_Vector3d getU() {
return u.get();
}
// Return copy of v coordinate axis in world coordinates
/**
* Gets the v.
*
* @return the v
*/
public WB_Vector3d getV() {
return v.get();
}
// Return copy of w coordinate axis in world coordinates
/**
* Gets the w.
*
* @return the w
*/
public WB_Vector3d getW() {
return getNormal();
}
}