package java3d;
public class BoundingPolytope extends Bounds {
Vector4d[] planes;
double[] mag; // magnitude of plane vector
double[] pDotN; // point on plane dotted with normal
Point3d[] verts; // vertices of polytope
int nVerts; // number of verts in polytope
Point3d centroid = new Point3d(); // centroid of polytope
Point3d boxVerts[];
boolean allocBoxVerts = false;
/**
* Constructs a BoundingPolytope using the specified planes.
*
* @param planes
* a set of planes defining the polytope.
* @exception IllegalArgumentException
* if the length of the specified array of planes is less
* than 4.
*/
public BoundingPolytope(Vector4d[] planes) {
boundId = BOUNDING_POLYTOPE;
int i;
double invMag;
this.planes = new Vector4d[planes.length];
mag = new double[planes.length];
pDotN = new double[planes.length];
for (i = 0; i < planes.length; i++) {
// normalize the plane normals
mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y
* planes[i].y + planes[i].z * planes[i].z);
invMag = 1.0 / mag[i];
this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y
* invMag, planes[i].z * invMag, planes[i].w * invMag);
}
computeAllVerts(); // XXXX: lazy evaluate
}
/**
* Constructs a BoundingPolytope and initializes it to a set of 6 planes
* that defines a cube such that -1 <= x,y,z <= 1. The values of the planes
* are as follows:
* <ul>
* planes[0] : x <= 1 (1,0,0,-1)<br>
* planes[1] : -x <= 1 (-1,0,0,-1)<br>
* planes[2] : y <= 1 (0,1,0,-1)<br>
* planes[3] : -y <= 1 (0,-1,0,-1)<br>
* planes[4] : z <= 1 (0,0,1,-1)<br>
* planes[5] : -z <= 1 (0,0,-1,-1)<br>
* </ul>
*/
public BoundingPolytope() {
boundId = BOUNDING_POLYTOPE;
planes = new Vector4d[6];
mag = new double[planes.length];
pDotN = new double[planes.length];
planes[0] = new Vector4d(1.0, 0.0, 0.0, -1.0);
planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0);
planes[2] = new Vector4d(0.0, 1.0, 0.0, -1.0);
planes[3] = new Vector4d(0.0, -1.0, 0.0, -1.0);
planes[4] = new Vector4d(0.0, 0.0, 1.0, -1.0);
planes[5] = new Vector4d(0.0, 0.0, -1.0, -1.0);
mag[0] = 1.0;
mag[1] = 1.0;
mag[2] = 1.0;
mag[3] = 1.0;
mag[4] = 1.0;
mag[5] = 1.0;
computeAllVerts(); // XXXX: lazy evaluate
}
public void setPlanes(Vector4d[] planes) {
int i;
double invMag;
this.planes = new Vector4d[planes.length];
pDotN = new double[planes.length];
mag = new double[planes.length];
if (planes.length <= 0) {
computeAllVerts(); // XXXX: lazy evaluate
return;
}
for (i = 0; i < planes.length; i++) {
// normalize the plane normals
mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y
* planes[i].y + planes[i].z * planes[i].z);
invMag = 1.0 / mag[i];
this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y
* invMag, planes[i].z * invMag, planes[i].w * invMag);
}
computeAllVerts(); // XXXX: lazy evaluate
}
/**
* Returns the equations of the bounding planes for this bounding polytope.
* The equations are copied into the specified array. The array must be
* large enough to hold all of the vectors. The individual array elements
* must be allocated by the caller.
*
* @param planes
* an array Vector4d to receive the bounding planes
*/
public void getPlanes(Vector4d[] planes) {
int i;
for (i = 0; i < planes.length; i++) {
planes[i].x = this.planes[i].x * mag[i];
planes[i].y = this.planes[i].y * mag[i];
planes[i].z = this.planes[i].z * mag[i];
planes[i].w = this.planes[i].w * mag[i];
}
}
public int getNumPlanes() {
return planes.length;
}
/**
* Sets the planes for this BoundingPolytope by keeping its current
* number and position of planes and computing new planes positions
* to enclose the given bounds object.
* @param boundsObject another bounds object
*/
public void set(Bounds boundsObject) {
int i,k;
double dis;
// no polytope exists yet so initialize one using the boundsObject
if( boundsObject == null ) {
boundsIsEmpty = true;
boundsIsInfinite = false;
computeAllVerts(); // XXXX: lazy evaluate
}else if( boundsObject.boundId == BOUNDING_SPHERE ) {
BoundingSphere sphere = (BoundingSphere)boundsObject;
if( boundsIsEmpty) {
initEmptyPolytope(); // no ptope exist so must initialize to default
computeAllVerts();
}
for(i=0;i<planes.length;i++) { // D = -(N dot C + radius)
planes[i].w = -(sphere.center.x*planes[i].x +
sphere.center.y*planes[i].y +
sphere.center.z*planes[i].z + sphere.radius);
}
boundsIsEmpty = boundsObject.boundsIsEmpty;
boundsIsInfinite = boundsObject.boundsIsInfinite;
computeAllVerts(); // XXXX: lazy evaluate
} else if( boundsObject.boundId == BOUNDING_BOX){
BoundingBox box = (BoundingBox)boundsObject;
double ux,uy,uz,lx,ly,lz,newD;
if( boundsIsEmpty) {
initEmptyPolytope(); // no ptope exist so must initialize to default
computeAllVerts();
}
for(i=0;i<planes.length;i++) {
ux = box.upper.x*planes[i].x;
uy = box.upper.y*planes[i].y;
uz = box.upper.z*planes[i].z;
lx = box.lower.x*planes[i].x;
ly = box.lower.y*planes[i].y;
lz = box.lower.z*planes[i].z;
planes[i].w = -(ux + uy + uz ); // initalize plane to upper vert
if( (newD = ux + uy + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
if( (newD = ux + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD;
if( (newD = ux + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
if( (newD = lx + uy + uz ) + planes[i].w > 0.0) planes[i].w = -newD;
if( (newD = lx + uy + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
if( (newD = lx + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD;
if( (newD = lx + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD;
}
boundsIsEmpty = boundsObject.boundsIsEmpty;
boundsIsInfinite = boundsObject.boundsIsInfinite;
computeAllVerts(); // XXXX: lazy evaluate
} else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
BoundingPolytope polytope = (BoundingPolytope)boundsObject;
if( planes.length != polytope.planes.length) {
planes = new Vector4d[polytope.planes.length];
for(k=0;k<polytope.planes.length;k++) planes[k] = new Vector4d();
mag = new double[polytope.planes.length];
pDotN = new double[polytope.planes.length];
}
for(i=0;i<polytope.planes.length;i++) {
planes[i].x = polytope.planes[i].x;
planes[i].y = polytope.planes[i].y;
planes[i].z = polytope.planes[i].z;
planes[i].w = polytope.planes[i].w;
mag[i] = polytope.mag[i];
}
nVerts = polytope.nVerts;
verts = new Point3d[nVerts];
for (k=0; k<nVerts; k++) {
verts[k] = new Point3d(polytope.verts[k]);
}
boundsIsEmpty = boundsObject.boundsIsEmpty;
boundsIsInfinite = boundsObject.boundsIsInfinite;
} else {
}
}
/**
* Creates a copy of a polytope.
*
* @return a new BoundingPolytope
*/
@Override
public Object clone() {
return new BoundingPolytope(planes);
}
/**
* Combines this bounding polytope with a bounding object so that the
* resulting bounding polytope encloses the original bounding polytope and
* the given bounds object.
*
* @param boundsObject
* another bounds object
*/
@Override
public void combine(Bounds boundsObject) {
BoundingSphere sphere;
if((boundsObject == null) || (boundsObject.boundsIsEmpty)
|| (boundsIsInfinite))
return;
if((boundsIsEmpty) || (boundsObject.boundsIsInfinite)) {
this.set(boundsObject);
return;
}
boundsIsEmpty = boundsObject.boundsIsEmpty;
boundsIsInfinite = boundsObject.boundsIsInfinite;
if( boundsObject.boundId == BOUNDING_SPHERE ) {
sphere = (BoundingSphere)boundsObject;
int i;
double dis;
for(i = 0; i < planes.length; i++){
dis = sphere.radius+ sphere.center.x*planes[i].x +
sphere.center.y*planes[i].y + sphere.center.z *
planes[i].z + planes[i].w;
if( dis > 0.0 ) {
planes[i].w += -dis;
}
}
} else if( boundsObject instanceof BoundingBox){
BoundingBox b = (BoundingBox)boundsObject;
if( !allocBoxVerts){
boxVerts = new Point3d[8];
for(int j=0;j<8;j++)boxVerts[j] = new Point3d();
allocBoxVerts = true;
}
boxVerts[0].set(b.lower.x, b.lower.y, b.lower.z );
boxVerts[1].set(b.lower.x, b.upper.y, b.lower.z );
boxVerts[2].set(b.upper.x, b.lower.y, b.lower.z );
boxVerts[3].set(b.upper.x, b.upper.y, b.lower.z );
boxVerts[4].set(b.lower.x, b.lower.y, b.upper.z );
boxVerts[5].set(b.lower.x, b.upper.y, b.upper.z );
boxVerts[6].set(b.upper.x, b.lower.y, b.upper.z );
boxVerts[7].set(b.upper.x, b.upper.y, b.upper.z );
this.combine(boxVerts);
} else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
BoundingPolytope polytope = (BoundingPolytope)boundsObject;
this.combine(polytope.verts);
} else {
}
computeAllVerts();
}
/**
* Combines this bounding polytope with a point.
* @param point a 3d point in space
*/
public void combine(Point3d point) {
int i;
double dis;
if(boundsIsInfinite) {
return;
}
if( boundsIsEmpty ){
planes = new Vector4d[6];
mag = new double[planes.length];
pDotN = new double[planes.length];
nVerts = 1;
verts = new Point3d[nVerts];
verts[0] = new Point3d( point.x, point.y, point.z);
for(i=0;i<planes.length;i++) {
pDotN[i] = 0.0;
}
planes[0] = new Vector4d( 1.0, 0.0, 0.0, -point.x );
planes[1] = new Vector4d(-1.0, 0.0, 0.0, point.x );
planes[2] = new Vector4d( 0.0, 1.0, 0.0, -point.y );
planes[3] = new Vector4d( 0.0,-1.0, 0.0, point.y );
planes[4] = new Vector4d( 0.0, 0.0, 1.0, -point.z );
planes[5] = new Vector4d( 0.0, 0.0,-1.0, point.z );
mag[0] = 1.0;
mag[1] = 1.0;
mag[2] = 1.0;
mag[3] = 1.0;
mag[4] = 1.0;
mag[5] = 1.0;
centroid.x = point.x;
centroid.y = point.y;
centroid.z = point.z;
boundsIsEmpty = false;
boundsIsInfinite = false;
} else {
for(i = 0; i < planes.length; i++){
dis = point.x*planes[i].x + point.y*planes[i].y + point.z*
planes[i].z + planes[i].w;
if( dis > 0.0 ) {
planes[i].w += -dis;
}
}
computeAllVerts();
}
}
/**
* Combines this bounding polytope with an array of points.
* @param points an array of 3d points in space
*/
public void combine(Point3d[] points) {
int i,j;
double dis;
if( boundsIsInfinite) {
return;
}
if( boundsIsEmpty ){
planes = new Vector4d[6];
mag = new double[planes.length];
pDotN = new double[planes.length];
nVerts = points.length;
verts = new Point3d[nVerts];
verts[0] = new Point3d( points[0].x, points[0].y, points[0].z);
for(i=0;i<planes.length;i++) {
pDotN[i] = 0.0;
}
planes[0] = new Vector4d( 1.0, 0.0, 0.0, -points[0].x );
planes[1] = new Vector4d(-1.0, 0.0, 0.0, points[0].x );
planes[2] = new Vector4d( 0.0, 1.0, 0.0, -points[0].y );
planes[3] = new Vector4d( 0.0,-1.0, 0.0, points[0].y );
planes[4] = new Vector4d( 0.0, 0.0, 1.0, -points[0].z );
planes[5] = new Vector4d( 0.0, 0.0,-1.0, points[0].z );
mag[0] = 1.0;
mag[1] = 1.0;
mag[2] = 1.0;
mag[3] = 1.0;
mag[4] = 1.0;
mag[5] = 1.0;
centroid.x = points[0].x;
centroid.y = points[0].y;
centroid.z = points[0].z;
boundsIsEmpty = false;
boundsIsInfinite = false;
}
for(j = 0; j < points.length; j++){
for(i = 0; i < planes.length; i++){
dis = points[j].x*planes[i].x + points[j].y*planes[i].y +
points[j].z*planes[i].z + planes[i].w;
if( dis > 0.0 ) {
planes[i].w += -dis;
}
}
}
computeAllVerts();
}
/**
* Transforms this bounding polytope by the given transformation matrix.
*
* @param matrix
* a transformation matrix
*/
@Override
public void transform(Transform3D matrix) {
int i;
double invMag;
Transform3D invTrans = new Transform3D(matrix);
invTrans.invert();
invTrans.transpose();
for (i = 0; i < planes.length; i++) {
planes[i].x = planes[i].x * mag[i];
planes[i].y = planes[i].y * mag[i];
planes[i].z = planes[i].z * mag[i];
planes[i].w = planes[i].w * mag[i];
invTrans.transform(planes[i]);
}
for (i = 0; i < planes.length; i++) {
// normalize the plane normals
mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y
* planes[i].y + planes[i].z * planes[i].z);
invMag = 1.0 / mag[i];
this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y
* invMag, planes[i].z * invMag, planes[i].w * invMag);
}
for (i = 0; i < verts.length; i++) {
matrix.transform(verts[i]);
}
}
/**
* Test for intersection with another bounds object.
* @param boundsObject another bounds object
* @return true or false indicating if an intersection occured
*/
public boolean intersect(Bounds boundsObject) {
if( boundsObject == null ) {
return false;
}
if( boundsIsEmpty || boundsObject.boundsIsEmpty ) {
return false;
}
if( boundsIsInfinite || boundsObject.boundsIsInfinite ) {
return true;
}
if( boundsObject.boundId == BOUNDING_SPHERE ) {
return intersect_ptope_sphere( this, (BoundingSphere)boundsObject);
} else if( boundsObject.boundId == BOUNDING_BOX){
return intersect_ptope_abox( this, (BoundingBox)boundsObject);
} else if(boundsObject.boundId == BOUNDING_POLYTOPE) {
return intersect_ptope_ptope( this, (BoundingPolytope)boundsObject);
} else {
return false;
}
}
private void computeVertex(int a, int b, int c) {
double det, x, y, z;
det = planes[a].x * planes[b].y * planes[c].z + planes[a].y
* planes[b].z * planes[c].x + planes[a].z * planes[b].x
* planes[c].y - planes[a].z * planes[b].y * planes[c].x
- planes[a].y * planes[b].x * planes[c].z - planes[a].x
* planes[b].z * planes[c].y;
// System.err.println("\n det="+det);
if (det * det < EPSILON) {
// System.err.println("parallel planes="+a+" "+b+" "+c);
return; // two planes are parallel
}
det = 1.0 / det;
x = (planes[b].y * planes[c].z - planes[b].z * planes[c].y) * pDotN[a];
y = (planes[b].z * planes[c].x - planes[b].x * planes[c].z) * pDotN[a];
z = (planes[b].x * planes[c].y - planes[b].y * planes[c].x) * pDotN[a];
x += (planes[c].y * planes[a].z - planes[c].z * planes[a].y) * pDotN[b];
y += (planes[c].z * planes[a].x - planes[c].x * planes[a].z) * pDotN[b];
z += (planes[c].x * planes[a].y - planes[c].y * planes[a].x) * pDotN[b];
x += (planes[a].y * planes[b].z - planes[a].z * planes[b].y) * pDotN[c];
y += (planes[a].z * planes[b].x - planes[a].x * planes[b].z) * pDotN[c];
z += (planes[a].x * planes[b].y - planes[a].y * planes[b].x) * pDotN[c];
x = x * det;
y = y * det;
z = z * det;
if (pointInPolytope(x, y, z)) {
if (nVerts >= verts.length) {
Point3d newVerts[] = new Point3d[nVerts << 1];
for (int i = 0; i < nVerts; i++) {
newVerts[i] = verts[i];
}
verts = newVerts;
}
verts[nVerts++] = new Point3d(x, y, z);
}
}
private void computeAllVerts() {
int i, a, b, c;
double x, y, z;
nVerts = 0;
verts = new Point3d[planes.length * planes.length];
for (i = 0; i < planes.length; i++) {
pDotN[i] = -planes[i].x * planes[i].w * planes[i].x - planes[i].y
* planes[i].w * planes[i].y - planes[i].z * planes[i].w
* planes[i].z;
}
for (a = 0; a < planes.length - 2; a++) {
for (b = a + 1; b < planes.length - 1; b++) {
for (c = b + 1; c < planes.length; c++) {
computeVertex(a, b, c);
}
}
}
// XXXX: correctly compute centroid
x = y = z = 0.0;
Point3d newVerts[] = new Point3d[nVerts];
for (i = 0; i < nVerts; i++) {
x += verts[i].x;
y += verts[i].y;
z += verts[i].z;
// copy the verts into an array of the correct size
newVerts[i] = verts[i];
}
this.verts = newVerts; // copy the verts into an array of the correct
// size
centroid.x = x / nVerts;
centroid.y = y / nVerts;
centroid.z = z / nVerts;
}
private boolean pointInPolytope(double x, double y, double z) {
for (int i = 0; i < planes.length; i++) {
if ((x * planes[i].x + y * planes[i].y + z * planes[i].z + planes[i].w) > EPSILON) {
return false;
}
}
return true;
}
private void checkBoundsIsEmpty() {
boundsIsEmpty = (planes.length < 4);
}
private void initEmptyPolytope() {
planes = new Vector4d[6];
pDotN = new double[6];
mag = new double[6];
verts = new Point3d[planes.length*planes.length];
nVerts = 0;
planes[0] = new Vector4d( 1.0, 0.0, 0.0, -1.0 );
planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0 );
planes[2] = new Vector4d( 0.0, 1.0, 0.0, -1.0 );
planes[3] = new Vector4d( 0.0,-1.0, 0.0, -1.0 );
planes[4] = new Vector4d( 0.0, 0.0, 1.0, -1.0 );
planes[5] = new Vector4d( 0.0, 0.0,-1.0, -1.0 );
mag[0] = 1.0;
mag[1] = 1.0;
mag[2] = 1.0;
mag[3] = 1.0;
mag[4] = 1.0;
mag[5] = 1.0;
checkBoundsIsEmpty();
}
}