/* * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import javax.vecmath.Point3d; import javax.vecmath.Point4d; import javax.vecmath.Vector3d; import javax.vecmath.Vector4d; /** * This class defines a spherical bounding region which is defined by a * center point and a radius. */ public class BoundingSphere extends Bounds { /** * The center of the bounding sphere. */ final Point3d center; /** * The radius of the bounding sphere. */ double radius; /** * Constructs and initializes a BoundingSphere from a center and radius. * @param center the center of the bounding sphere * @param radius the radius of the bounding sphere */ public BoundingSphere(Point3d center, double radius) { boundId = BOUNDING_SPHERE; this.center = new Point3d(center); this.radius = radius; updateBoundsStates(); } /** * Constructs and initializes a BoundingSphere with radius = 1 at 0 0 0. */ public BoundingSphere() { boundId = BOUNDING_SPHERE; center = new Point3d(); radius = 1.0; } /** * Constructs and initializes a BoundingSphere from a bounding object. * @param boundsObject a bounds object */ public BoundingSphere(Bounds boundsObject) { boundId = BOUNDING_SPHERE; center = new Point3d(); if (boundsObject == null || boundsObject.boundsIsEmpty) { setEmptyBounds(); return; } if (boundsObject.boundsIsInfinite) { setInfiniteBounds(); return; } if( boundsObject.boundId == BOUNDING_BOX){ BoundingBox box = (BoundingBox)boundsObject; center.x = (box.upper.x+box.lower.x)/2.0; center.y = (box.upper.y+box.lower.y)/2.0; center.z = (box.upper.z+box.lower.z)/2.0; radius = 0.5*(Math.sqrt((box.upper.x-box.lower.x)* (box.upper.x-box.lower.x)+ (box.upper.y-box.lower.y)* (box.upper.y-box.lower.y)+ (box.upper.z-box.lower.z)* (box.upper.z-box.lower.z))); } else if (boundsObject.boundId == BOUNDING_SPHERE) { BoundingSphere sphere = (BoundingSphere)boundsObject; center.set(sphere.center); radius = sphere.radius; } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { BoundingPolytope polytope = (BoundingPolytope)boundsObject; double t,dis,dis_sq,rad_sq,oldc_to_new_c; center.x = polytope.centroid.x; center.y = polytope.centroid.y; center.z = polytope.centroid.z; radius = Math.sqrt( (polytope.verts[0].x - center.x)* (polytope.verts[0].x - center.x) + (polytope.verts[0].y - center.y)* (polytope.verts[0].y - center.y) + (polytope.verts[0].z - center.z)* (polytope.verts[0].z - center.z)); for (int i = 1; i < polytope.nVerts; i++) { rad_sq = radius * radius; dis_sq = (polytope.verts[i].x - center.x)* (polytope.verts[i].x - center.x) + (polytope.verts[i].y - center.y)* (polytope.verts[i].y - center.y) + (polytope.verts[i].z - center.z)* (polytope.verts[i].z - center.z); // change sphere so one side passes through the point // and other passes through the old sphere if( dis_sq > rad_sq) { dis = Math.sqrt( dis_sq); radius = (radius + dis)*.5; oldc_to_new_c = dis - radius; t = oldc_to_new_c/dis; center.x = center.x + (polytope.verts[i].x - center.x)*t; center.y = center.y + (polytope.verts[i].y - center.y)*t; center.z = center.z + (polytope.verts[i].z - center.z)*t; } } } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere0")); } updateBoundsStates(); } /** * Constructs and initializes a BoundingSphere from an array of bounding * objects. * @param boundsObjects an array of bounds objects */ public BoundingSphere(Bounds[] boundsObjects) { boundId = BOUNDING_SPHERE; center = new Point3d(); if (boundsObjects == null || boundsObjects.length <= 0) { setEmptyBounds(); return; } // find first non empty bounds object int i = 0; while( boundsObjects[i] == null && i < boundsObjects.length) { i++; } if (i >= boundsObjects.length) { // all bounds objects were empty setEmptyBounds(); return; } this.set(boundsObjects[i++]); if(boundsIsInfinite) return; Point3d[] boxVerts = null; for(;i sphere.radius) { if( (dis+sphere.radius) > radius) { d1 = .5*(radius-sphere.radius+dis); t = d1/dis; radius = d1+sphere.radius; center.x = sphere.center.x + (center.x-sphere.center.x)*t; center.y = sphere.center.y + (center.y-sphere.center.y)*t; center.z = sphere.center.z + (center.z-sphere.center.z)*t; } }else { if( (dis+radius) <= sphere.radius) { center.x = sphere.center.x; center.y = sphere.center.y; center.z = sphere.center.z; radius = sphere.radius; }else { d1 = .5*(sphere.radius-radius+dis); t = d1/dis; radius = d1+radius; center.x = center.x + (sphere.center.x-center.x)*t; center.y = center.y + (sphere.center.y-center.y)*t; center.z = center.z + (sphere.center.z-center.z)*t; } } } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) { BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i]; this.combine(polytope.verts); } else { if( boundsObjects[i] != null ) throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere0")); } } updateBoundsStates(); } /** * Returns the radius of this bounding sphere as a double. * @return the radius of the bounding sphere */ public double getRadius() { return radius; } /** * Sets the radius of this bounding sphere from a double. * @param r the new radius for the bounding sphere */ public void setRadius(double r) { radius = r; updateBoundsStates(); } /** * Returns the position of this bounding sphere as a point. * @param center a Point to receive the center of the bounding sphere */ @Override public void getCenter(Point3d center) { center.set(this.center); } /** * Sets the position of this bounding sphere from a point. * @param center a Point defining the new center of the bounding sphere */ public void setCenter(Point3d center) { this.center.set(center); updateBoundsStates(); } /** * Sets the value of this BoundingSphere. * @param boundsObject another bounds object */ @Override public void set(Bounds boundsObject){ int i; if (boundsObject == null || boundsObject.boundsIsEmpty) { setEmptyBounds(); return; } if( boundsObject.boundsIsInfinite ) { setInfiniteBounds(); return; } if( boundsObject.boundId == BOUNDING_BOX){ BoundingBox box = (BoundingBox)boundsObject; center.x = (box.upper.x + box.lower.x )/2.0; center.y = (box.upper.y + box.lower.y )/2.0; center.z = (box.upper.z + box.lower.z )/2.0; radius = 0.5*Math.sqrt((box.upper.x-box.lower.x)* (box.upper.x-box.lower.x)+ (box.upper.y-box.lower.y)* (box.upper.y-box.lower.y)+ (box.upper.z-box.lower.z)* (box.upper.z-box.lower.z)); } else if( boundsObject.boundId == BOUNDING_SPHERE ) { BoundingSphere sphere = (BoundingSphere)boundsObject; radius = sphere.radius; center.x = sphere.center.x; center.y = sphere.center.y; center.z = sphere.center.z; } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { BoundingPolytope polytope = (BoundingPolytope)boundsObject; double t,dis,dis_sq,rad_sq,oldc_to_new_c; center.x = polytope.centroid.x; center.y = polytope.centroid.y; center.z = polytope.centroid.z; radius = Math.sqrt((polytope.verts[0].x - center.x)* (polytope.verts[0].x - center.x) + (polytope.verts[0].y - center.y)* (polytope.verts[0].y - center.y) + (polytope.verts[0].z - center.z)* (polytope.verts[0].z - center.z)); for(i=1;i rad_sq) { // point is outside sphere dis = Math.sqrt( dis_sq); radius = (radius + dis)*.5; oldc_to_new_c = dis - radius; t = oldc_to_new_c/dis; center.x = center.x + (polytope.verts[i].x - center.x)*t; center.y = center.y + (polytope.verts[i].y - center.y)*t; center.z = center.z + (polytope.verts[i].z - center.z)*t; } } } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere2")); } updateBoundsStates(); } /** * Creates a copy of the bounding sphere. * @return a BoundingSphere */ @Override public Object clone() { return new BoundingSphere(this.center, this.radius); } /** * Indicates whether the specified bounds object is * equal to this BoundingSphere object. They are equal if the * specified bounds object is an instance of * BoundingSphere and all of the data * members of bounds are equal to the corresponding * data members in this BoundingSphere. * @param bounds the object with which the comparison is made. * @return true if this BoundingSphere is equal to bounds; * otherwise false * * @since Java 3D 1.2 */ @Override public boolean equals(Object bounds) { try { BoundingSphere sphere = (BoundingSphere)bounds; return (center.equals(sphere.center) && radius == sphere.radius); } catch (NullPointerException e) { return false; } catch (ClassCastException e) { return false; } } /** * Returns a hash code value for this BoundingSphere object * based on the data values in this object. Two different * BoundingSphere objects with identical data values (i.e., * BoundingSphere.equals returns true) will return the same hash * code value. Two BoundingSphere objects with different data * members may return the same hash code value, although this is * not likely. * @return a hash code value for this BoundingSphere object. * * @since Java 3D 1.2 */ @Override public int hashCode() { long bits = 1L; bits = J3dHash.mixDoubleBits(bits, radius); bits = J3dHash.mixDoubleBits(bits, center.x); bits = J3dHash.mixDoubleBits(bits, center.y); bits = J3dHash.mixDoubleBits(bits, center.z); return J3dHash.finish(bits); } /** * Combines this bounding sphere with a bounding object so that the * resulting bounding sphere encloses the original bounding sphere and the * given bounds object. * @param boundsObject another bounds object */ @Override public void combine(Bounds boundsObject) { double t,dis,d1,u,l,x,y,z,oldc_to_new_c; BoundingSphere sphere; if((boundsObject == null) || (boundsObject.boundsIsEmpty) || (boundsIsInfinite)) return; if((boundsIsEmpty) || (boundsObject.boundsIsInfinite)) { this.set(boundsObject); return; } if( boundsObject.boundId == BOUNDING_BOX){ BoundingBox b = (BoundingBox)boundsObject; // start with point furthest from sphere u = b.upper.x-center.x; l = b.lower.x-center.x; if( u*u > l*l) x = b.upper.x; else x = b.lower.x; u = b.upper.y-center.y; l = b.lower.y-center.y; if( u*u > l*l) y = b.upper.y; else y = b.lower.y; u = b.upper.z-center.z; l = b.lower.z-center.z; if( u*u > l*l) z = b.upper.z; else z = b.lower.z; dis = Math.sqrt( (x - center.x)*(x - center.x) + (y - center.y)*(y - center.y) + (z - center.z)*(z - center.z) ); if( dis > radius) { radius = (dis + radius)*.5; oldc_to_new_c = dis - radius; center.x = (radius*center.x + oldc_to_new_c*x)/dis; center.y = (radius*center.y + oldc_to_new_c*y)/dis; center.z = (radius*center.z + oldc_to_new_c*z)/dis; combinePoint( b.upper.x, b.upper.y, b.upper.z); combinePoint( b.upper.x, b.upper.y, b.lower.z); combinePoint( b.upper.x, b.lower.y, b.upper.z); combinePoint( b.upper.x, b.lower.y, b.lower.z); combinePoint( b.lower.x, b.upper.y, b.upper.z); combinePoint( b.lower.x, b.upper.y, b.lower.z); combinePoint( b.lower.x, b.lower.y, b.upper.z); combinePoint( b.lower.x, b.lower.y, b.lower.z); } } else if( boundsObject.boundId == BOUNDING_SPHERE ) { sphere = (BoundingSphere)boundsObject; dis = Math.sqrt( (center.x - sphere.center.x)* (center.x - sphere.center.x) + (center.y - sphere.center.y)* (center.y - sphere.center.y) + (center.z - sphere.center.z)* (center.z - sphere.center.z) ); if( radius > sphere.radius) { if( (dis+sphere.radius) > radius) { d1 = .5*(radius-sphere.radius+dis); t = d1/dis; radius = d1+sphere.radius; center.x = sphere.center.x + (center.x-sphere.center.x)*t; center.y = sphere.center.y + (center.y-sphere.center.y)*t; center.z = sphere.center.z + (center.z-sphere.center.z)*t; } }else { if( (dis+radius) <= sphere.radius) { center.x = sphere.center.x; center.y = sphere.center.y; center.z = sphere.center.z; radius = sphere.radius; }else { d1 = .5*(sphere.radius-radius+dis); t = d1/dis; radius = d1+radius; center.x = center.x + (sphere.center.x-center.x)*t; center.y = center.y + (sphere.center.y-center.y)*t; center.z = center.z + (sphere.center.z-center.z)*t; } } } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { BoundingPolytope polytope = (BoundingPolytope)boundsObject; this.combine(polytope.verts); } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere3")); } updateBoundsStates(); } private void combinePoint( double x, double y, double z) { double dis,oldc_to_new_c; dis = Math.sqrt( (x - center.x)*(x - center.x) + (y - center.y)*(y - center.y) + (z - center.z)*(z - center.z) ); if( dis > radius) { radius = (dis + radius)*.5; oldc_to_new_c = dis - radius; center.x = (radius*center.x + oldc_to_new_c*x)/dis; center.y = (radius*center.y + oldc_to_new_c*y)/dis; center.z = (radius*center.z + oldc_to_new_c*z)/dis; } } /** * Combines this bounding sphere with an array of bounding objects so that the * resulting bounding sphere encloses the original bounding sphere and the * given array of bounds object. * @param boundsObjects an array of bounds objects */ @Override public void combine(Bounds[] boundsObjects) { BoundingSphere sphere; BoundingBox b; BoundingPolytope polytope; double t,dis,d1,u,l,x,y,z,oldc_to_new_c; int i=0; if((boundsObjects == null) || (boundsObjects.length <= 0) || (boundsIsInfinite)) return; // find first non empty bounds object while((i= boundsObjects.length) return; // no non empty bounds so do not modify current bounds if( boundsIsEmpty) this.set(boundsObjects[i++]); if(boundsIsInfinite) return; for(;i l*l) x = b.upper.x; else x = b.lower.x; u = b.upper.y-center.y; l = b.lower.y-center.y; if( u*u > l*l) y = b.upper.y; else y = b.lower.y; u = b.upper.z-center.z; l = b.lower.z-center.z; if( u*u > l*l) z = b.upper.z; else z = b.lower.z; dis = Math.sqrt( (x - center.x)*(x - center.x) + (y - center.y)*(y - center.y) + (z - center.z)*(z - center.z) ); if( dis > radius) { radius = (dis + radius)*.5; oldc_to_new_c = dis - radius; center.x = (radius*center.x + oldc_to_new_c*x)/dis; center.y = (radius*center.y + oldc_to_new_c*y)/dis; center.z = (radius*center.z + oldc_to_new_c*z)/dis; combinePoint( b.upper.x, b.upper.y, b.upper.z); combinePoint( b.upper.x, b.upper.y, b.lower.z); combinePoint( b.upper.x, b.lower.y, b.upper.z); combinePoint( b.upper.x, b.lower.y, b.lower.z); combinePoint( b.lower.x, b.upper.y, b.upper.z); combinePoint( b.lower.x, b.upper.y, b.lower.z); combinePoint( b.lower.x, b.lower.y, b.upper.z); combinePoint( b.lower.x, b.lower.y, b.lower.z); } } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) { sphere = (BoundingSphere)boundsObjects[i]; dis = Math.sqrt( (center.x - sphere.center.x)* (center.x - sphere.center.x) + (center.y - sphere.center.y)* (center.y - sphere.center.y) + (center.z - sphere.center.z)* (center.z - sphere.center.z) ); if( radius > sphere.radius) { if( (dis+sphere.radius) > radius) { d1 = .5*(radius-sphere.radius+dis); t = d1/dis; radius = d1+sphere.radius; center.x = sphere.center.x + (center.x-sphere.center.x)*t; center.y = sphere.center.y + (center.y-sphere.center.y)*t; center.z = sphere.center.z + (center.z-sphere.center.z)*t; } }else { if( (dis+radius) <= sphere.radius) { center.x = sphere.center.x; center.y = sphere.center.y; center.z = sphere.center.z; radius = sphere.radius; }else { d1 = .5*(sphere.radius-radius+dis); t = d1/dis; radius = d1+radius; center.x = center.x + (sphere.center.x-center.x)*t; center.y = center.y + (sphere.center.y-center.y)*t; center.z = center.z + (sphere.center.z-center.z)*t; } } } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) { polytope = (BoundingPolytope)boundsObjects[i]; this.combine(polytope.verts); } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere4")); } } updateBoundsStates(); } /** * Combines this bounding sphere with a point. * @param point a 3D point in space */ @Override public void combine(Point3d point) { double dis,oldc_to_new_c; if( boundsIsInfinite) { return; } if( boundsIsEmpty) { radius = 0.0; center.x = point.x; center.y = point.y; center.z = point.z; } else { dis = Math.sqrt( (point.x - center.x)*(point.x - center.x) + (point.y - center.y)*(point.y - center.y) + (point.z - center.z)*(point.z - center.z) ); if( dis > radius) { radius = (dis + radius)*.5; oldc_to_new_c = dis - radius; center.x = (radius*center.x + oldc_to_new_c*point.x)/dis; center.y = (radius*center.y + oldc_to_new_c*point.y)/dis; center.z = (radius*center.z + oldc_to_new_c*point.z)/dis; } } updateBoundsStates(); } /** * Combines this bounding sphere with an array of points. * @param points an array of 3D points in space */ @Override public void combine(Point3d[] points) { int i; double dis,dis_sq,rad_sq,oldc_to_new_c; if( boundsIsInfinite) { return; } if( boundsIsEmpty ) { center.x = points[0].x; center.y = points[0].y; center.z = points[0].z; radius = 0.0; } for(i=0;i rad_sq) { dis = Math.sqrt( dis_sq); radius = (radius + dis)*.5; oldc_to_new_c = dis - radius; center.x = (radius*center.x + oldc_to_new_c*points[i].x)/dis; center.y = (radius*center.y + oldc_to_new_c*points[i].y)/dis; center.z = (radius*center.z + oldc_to_new_c*points[i].z)/dis; } } updateBoundsStates(); } /** * Modifies the bounding sphere so that it bounds the volume * generated by transforming the given bounding object. * @param boundsObject the bounding object to be transformed * @param matrix a transformation matrix */ @Override public void transform( Bounds boundsObject, Transform3D matrix) { if (boundsObject == null || boundsObject.boundsIsEmpty) { setEmptyBounds(); return; } if (boundsObject.boundsIsInfinite) { setInfiniteBounds(); return; } if (boundsObject.boundId == BOUNDING_BOX) { BoundingBox tmpBox = new BoundingBox(boundsObject); tmpBox.transform(matrix); this.set(tmpBox); } else if (boundsObject.boundId == BOUNDING_SPHERE) { this.set(boundsObject); this.transform(matrix); } else if (boundsObject.boundId == BOUNDING_POLYTOPE) { BoundingPolytope tmpPolytope = new BoundingPolytope(boundsObject); tmpPolytope.transform(matrix); this.set(tmpPolytope); } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere5")); } } /** * Transforms this bounding sphere by the given matrix. */ @Override public void transform( Transform3D trans) { double scale; if(boundsIsInfinite) return; trans.transform(center); scale = trans.getDistanceScale(); radius = radius * scale; if (Double.isNaN(radius)) { setEmptyBounds(); return; } } /** * Test for intersection with a ray * @param origin the starting point of the ray * @param direction the direction of the ray * @param position3 a point defining the location of the pick w= distance to pick * @return true or false indicating if an intersection occured */ @Override boolean intersect(Point3d origin, Vector3d direction, Point4d position ) { if( boundsIsEmpty ) { return false; } if( boundsIsInfinite ) { position.x = origin.x; position.y = origin.y; position.z = origin.z; position.w = 0.0; return true; } double l2oc,rad2,tca,t2hc,t,invMag; Vector3d dir = new Vector3d(); // normalized direction of ray Point3d oc = new Point3d(); // vector from sphere center to ray origin oc.x = center.x - origin.x; oc.y = center.y - origin.y; oc.z = center.z - origin.z; l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared rad2 = radius*radius; if( l2oc < rad2 ){ // System.err.println("ray origin inside sphere" ); return true; // ray origin inside sphere } invMag = 1.0/Math.sqrt(direction.x*direction.x + direction.y*direction.y + direction.z*direction.z); dir.x = direction.x*invMag; dir.y = direction.y*invMag; dir.z = direction.z*invMag; tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z; if( tca <= 0.0 ) { // System.err.println("ray points away from sphere" ); return false; // ray points away from sphere } t2hc = rad2 - l2oc + tca*tca; if( t2hc > 0.0 ){ t = tca - Math.sqrt(t2hc); // System.err.println("ray hits sphere:"+this.toString()+" t="+t+" direction="+dir ); position.x = origin.x + dir.x*t; position.y = origin.y + dir.y*t; position.z = origin.z + dir.z*t; position.w = t; return true; // ray hits sphere }else { // System.err.println("ray does not hit sphere" ); return false; } } /** * Test for intersection with a point * @param point the pick point * @param position a point defining the location of the pick w= distance to pick * @return true or false indicating if an intersection occured */ @Override boolean intersect(Point3d point, Point4d position ) { double x,y,z,dist; if( boundsIsEmpty ) { return false; } if( boundsIsInfinite ) { position.x = point.x; position.y = point.y; position.z = point.z; position.w = 0.0; return true; } x = point.x - center.x; y = point.y - center.y; z = point.z - center.z; dist = x*x + y*y + z*z; if( dist > radius*radius) return false; else { position.x = point.x; position.y = point.y; position.z = point.z; position.w = Math.sqrt(dist); return true; } } /** * Test for intersection with a segment * @param start a point defining the start of the line segment * @param end a point defining the end of the line segment * @param position a point defining the location of the pick w= distance to pick * @return true or false indicating if an intersection occured */ @Override boolean intersect( Point3d start, Point3d end, Point4d position ) { if( boundsIsEmpty ) { return false; } if( boundsIsInfinite ) { position.x = start.x; position.y = start.y; position.z = start.z; position.w = 0.0; return true; } double l2oc,rad2,tca,t2hc,invMag,t; Vector3d dir = new Vector3d(); // normalized direction of ray Point3d oc = new Point3d(); // vector from sphere center to ray origin Vector3d direction = new Vector3d(); oc.x = center.x - start.x; oc.y = center.y - start.y; oc.z = center.z - start.z; direction.x = end.x - start.x; direction.y = end.y - start.y; direction.z = end.z - start.z; invMag = 1.0/Math.sqrt( direction.x*direction.x + direction.y*direction.y + direction.z*direction.z); dir.x = direction.x*invMag; dir.y = direction.y*invMag; dir.z = direction.z*invMag; l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared rad2 = radius*radius; if( l2oc < rad2 ){ // System.err.println("ray origin inside sphere" ); return true; // ray origin inside sphere } tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z; if( tca <= 0.0 ) { // System.err.println("ray points away from sphere" ); return false; // ray points away from sphere } t2hc = rad2 - l2oc + tca*tca; if( t2hc > 0.0 ){ t = tca - Math.sqrt(t2hc); if( t*t <= ((end.x-start.x)*(end.x-start.x)+ (end.y-start.y)*(end.y-start.y)+ (end.z-start.z)*(end.z-start.z))){ position.x = start.x + dir.x*t; position.y = start.y + dir.x*t; position.z = start.z + dir.x*t; position.w = t; return true; // segment hits sphere } } return false; } /** * Test for intersection with a ray. * @param origin the starting point of the ray * @param direction the direction of the ray * @return true or false indicating if an intersection occured */ @Override public boolean intersect(Point3d origin, Vector3d direction ) { if( boundsIsEmpty ) { return false; } if( boundsIsInfinite ) { return true; } double l2oc,rad2,tca,t2hc,mag; Vector3d dir = new Vector3d(); // normalized direction of ray Point3d oc = new Point3d(); // vector from sphere center to ray origin oc.x = center.x - origin.x; oc.y = center.y - origin.y; oc.z = center.z - origin.z; l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared rad2 = radius*radius; if( l2oc < rad2 ){ // System.err.println("ray origin inside sphere" ); return true; // ray origin inside sphere } mag = Math.sqrt(direction.x*direction.x + direction.y*direction.y + direction.z*direction.z); dir.x = direction.x/mag; dir.y = direction.y/mag; dir.z = direction.z/mag; tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z; if( tca <= 0.0 ) { // System.err.println("ray points away from sphere" ); return false; // ray points away from sphere } t2hc = rad2 - l2oc + tca*tca; if( t2hc > 0.0 ){ // System.err.println("ray hits sphere" ); return true; // ray hits sphere }else { // System.err.println("ray does not hit sphere" ); return false; } } /** * Returns the position of the intersect point if the ray intersects with * the sphere. * */ boolean intersect(Point3d origin, Vector3d direction, Point3d intersectPoint ) { if( boundsIsEmpty ) { return false; } if( boundsIsInfinite ) { intersectPoint.x = origin.x; intersectPoint.y = origin.y; intersectPoint.z = origin.z; return true; } double l2oc,rad2,tca,t2hc,mag,t; Point3d dir = new Point3d(); // normalized direction of ray Point3d oc = new Point3d(); // vector from sphere center to ray origin oc.x = center.x - origin.x; // XXXX: check if this method is still needed oc.y = center.y - origin.y; oc.z = center.z - origin.z; l2oc = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z; // center to origin squared rad2 = radius*radius; if( l2oc < rad2 ){ // System.err.println("ray origin inside sphere" ); return true; // ray origin inside sphere } mag = Math.sqrt(direction.x*direction.x + direction.y*direction.y + direction.z*direction.z); dir.x = direction.x/mag; dir.y = direction.y/mag; dir.z = direction.z/mag; tca = oc.x*dir.x + oc.y*dir.y + oc.z*dir.z; if( tca <= 0.0 ) { // System.err.println("ray points away from sphere" ); return false; // ray points away from sphere } t2hc = rad2 - l2oc + tca*tca; if( t2hc > 0.0 ){ t = tca - Math.sqrt(t2hc); intersectPoint.x = origin.x + direction.x*t; intersectPoint.y = origin.y + direction.y*t; intersectPoint.z = origin.z + direction.z*t; // System.err.println("ray hits sphere" ); return true; // ray hits sphere }else { // System.err.println("ray does not hit sphere" ); return false; } } /** * Test for intersection with a point. * @param point a point defining a position in 3-space * @return true or false indicating if an intersection occured */ @Override public boolean intersect(Point3d point ) { double x,y,z,dist; if( boundsIsEmpty ) { return false; } if( boundsIsInfinite ) { return true; } x = point.x - center.x; y = point.y - center.y; z = point.z - center.z; dist = x*x + y*y + z*z; if( dist > radius*radius) return false; else return true; } /** * Tests whether the bounding sphere is empty. A bounding sphere is * empty if it is null (either by construction or as the result of * a null intersection) or if its volume is negative. A bounding sphere * with a volume of zero is not empty. * @return true if the bounding sphere is empty; * otherwise, it returns false */ @Override public boolean isEmpty() { return boundsIsEmpty; } /** * Test for intersection with another bounds object. * @param boundsObject another bounds object * @return true or false indicating if an intersection occured */ @Override boolean intersect(Bounds boundsObject, Point4d position) { return intersect(boundsObject); } /** * Test for intersection with another bounds object. * @param boundsObject another bounds object * @return true or false indicating if an intersection occured */ @Override public boolean intersect(Bounds boundsObject) { double distsq, radsq; BoundingSphere sphere; if( boundsObject == null ) { return false; } if( boundsIsEmpty || boundsObject.boundsIsEmpty ) { return false; } if( boundsIsInfinite || boundsObject.boundsIsInfinite ) { return true; } if( boundsObject.boundId == BOUNDING_BOX){ BoundingBox box = (BoundingBox)boundsObject; double dis = 0.0; double rad_sq = radius*radius; // find the corner closest to the center of sphere if( center.x < box.lower.x ) dis = (center.x-box.lower.x)*(center.x-box.lower.x); else if( center.x > box.upper.x ) dis = (center.x-box.upper.x)*(center.x-box.upper.x); if( center.y < box.lower.y ) dis += (center.y-box.lower.y)*(center.y-box.lower.y); else if( center.y > box.upper.y ) dis += (center.y-box.upper.y)*(center.y-box.upper.y); if( center.z < box.lower.z ) dis += (center.z-box.lower.z)*(center.z-box.lower.z); else if( center.z > box.upper.z ) dis += (center.z-box.upper.z)*(center.z-box.upper.z); return ( dis <= rad_sq ); } else if( boundsObject.boundId == BOUNDING_SPHERE ) { sphere = (BoundingSphere)boundsObject; radsq = radius + sphere.radius; radsq *= radsq; distsq = center.distanceSquared(sphere.center); return (distsq <= radsq); } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { return intersect_ptope_sphere( (BoundingPolytope)boundsObject, this); } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere6")); } } /** * Test for intersection with another bounds object. * @param boundsObjects an array of bounding objects * @return true or false indicating if an intersection occured */ @Override public boolean intersect(Bounds[] boundsObjects) { double distsq, radsq; BoundingSphere sphere; int i; if( boundsObjects == null || boundsObjects.length <= 0 ) { return false; } if( boundsIsEmpty ) { return false; } for(i = 0; i < boundsObjects.length; i++){ if( boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty); else if( boundsIsInfinite || boundsObjects[i].boundsIsInfinite ) { return true; // We're done here. } else if( boundsObjects[i].boundId == BOUNDING_BOX){ if( this.intersect( boundsObjects[i])) return true; } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) { sphere = (BoundingSphere)boundsObjects[i]; radsq = radius + sphere.radius; radsq *= radsq; distsq = center.distanceSquared(sphere.center); if (distsq <= radsq) { return true; } } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) { if( this.intersect( boundsObjects[i])) return true; } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere7")); } } return false; } /** * Test for intersection with another bounds object. * @param boundsObject another bounds object * @param newBoundSphere the new bounding sphere which is the intersection of * the boundsObject and this BoundingSphere * @return true or false indicating if an intersection occured */ public boolean intersect(Bounds boundsObject, BoundingSphere newBoundSphere) { if (boundsObject == null || boundsIsEmpty || boundsObject.boundsIsEmpty) { newBoundSphere.set(null); return false; } if (boundsIsInfinite && !boundsObject.boundsIsInfinite) { newBoundSphere.set(boundsObject); return true; } if (boundsObject.boundsIsInfinite) { newBoundSphere.set(this); return true; } if(boundsObject.boundId == BOUNDING_BOX){ BoundingBox tbox = new BoundingBox(); BoundingBox box = (BoundingBox)boundsObject; if( this.intersect( box) ){ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box sbox.intersect(box, tbox); // insersect two boxes newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxes return true; } else { newBoundSphere.set(null); return false; } } else if( boundsObject.boundId == BOUNDING_SPHERE ) { BoundingSphere sphere = (BoundingSphere)boundsObject; double dis,t,d2; dis = Math.sqrt( (center.x-sphere.center.x)*(center.x-sphere.center.x) + (center.y-sphere.center.y)*(center.y-sphere.center.y) + (center.z-sphere.center.z)*(center.z-sphere.center.z) ); if ( dis > radius+sphere.radius) { newBoundSphere.set(null); return false; } else if( dis+radius <= sphere.radius ) { // this sphere is contained within boundsObject newBoundSphere.center.x = center.x; newBoundSphere.center.y = center.y; newBoundSphere.center.z = center.z; newBoundSphere.radius = radius; } else if( dis+sphere.radius <= radius ) { // boundsObject is containted within this sphere newBoundSphere.center.x = sphere.center.x; newBoundSphere.center.y = sphere.center.y; newBoundSphere.center.z = sphere.center.z; newBoundSphere.radius = sphere.radius; } else { // distance from this center to center of overlapped volume d2 = (dis*dis + radius*radius - sphere.radius*sphere.radius)/(2.0*dis); newBoundSphere.radius = Math.sqrt( radius*radius - d2*d2); t = d2/dis; newBoundSphere.center.x = center.x + (sphere.center.x - center.x)*t; newBoundSphere.center.y = center.y + (sphere.center.y - center.y)*t; newBoundSphere.center.z = center.z + (sphere.center.z - center.z)*t; } newBoundSphere.updateBoundsStates(); return true; } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { BoundingBox tbox = new BoundingBox(); BoundingPolytope polytope = (BoundingPolytope)boundsObject; if( this.intersect( polytope) ){ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box BoundingBox pbox = new BoundingBox( polytope ); // convert polytope to box sbox.intersect(pbox,tbox); // insersect two boxes newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxesf return true; } else { newBoundSphere.set(null); return false; } } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere8")); } } /** * Test for intersection with an array of bounds objects. * @param boundsObjects an array of bounds objects * @param newBoundSphere the new bounding sphere which is the intersection of * the boundsObject and this BoundingSphere * @return true or false indicating if an intersection occured */ public boolean intersect(Bounds[] boundsObjects, BoundingSphere newBoundSphere) { if (boundsObjects == null || boundsObjects.length <= 0 || boundsIsEmpty) { newBoundSphere.set(null); return false; } int i=0; // find first non null bounds object while( boundsObjects[i] == null && i < boundsObjects.length) { i++; } if (i >= boundsObjects.length) { // all bounds objects were empty newBoundSphere.set(null); return false; } boolean status = false; double newRadius; Point3d newCenter = new Point3d(); BoundingBox tbox = new BoundingBox(); for(i=0;i radius+sphere.radius) { } else if( dis+radius <= sphere.radius ) { // this sphere is contained within boundsObject if( status ) { newBoundSphere.combine( this ); } else { newBoundSphere.center.x = center.x; newBoundSphere.center.y = center.y; newBoundSphere.center.z = center.z; newBoundSphere.radius = radius; status = true; newBoundSphere.updateBoundsStates(); } } else if( dis+sphere.radius <= radius ) { // boundsObject is containted within this sphere if( status ) { newBoundSphere.combine( sphere ); } else { newBoundSphere.center.x = center.x; newBoundSphere.center.y = center.y; newBoundSphere.center.z = center.z; newBoundSphere.radius = sphere.radius; status = true; newBoundSphere.updateBoundsStates(); } } else { // distance from this center to center of overlapped volume d2 = (dis*dis + radius*radius - sphere.radius*sphere.radius)/(2.0*dis); newRadius = Math.sqrt( radius*radius - d2*d2); t = d2/dis; newCenter.x = center.x + (sphere.center.x - center.x)*t; newCenter.y = center.y + (sphere.center.y - center.y)*t; newCenter.z = center.z + (sphere.center.z - center.z)*t; if( status ) { BoundingSphere newSphere = new BoundingSphere( newCenter, newRadius ); newBoundSphere.combine( newSphere ); } else { newBoundSphere.setRadius( newRadius ); newBoundSphere.setCenter( newCenter ); status = true; } } } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) { BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i]; if( this.intersect( polytope) ){ BoundingBox sbox = new BoundingBox( this ); // convert sphere to box BoundingBox pbox = new BoundingBox( polytope ); // convert polytope to box sbox.intersect(pbox, tbox); // insersect two boxes if( status ) { newBoundSphere.combine( tbox ); } else { newBoundSphere.set( tbox ); // set sphere to the intersection of 2 boxesf status = true; } } } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere9")); } } if (status == false) newBoundSphere.set(null); return status; } /** * Finds closest bounding object that intersects this bounding sphere. * @param boundsObjects an array of bounds objects * @return closest bounding object */ @Override public Bounds closestIntersection( Bounds[] boundsObjects) { if( boundsObjects == null || boundsObjects.length <= 0 ) { return null; } if( boundsIsEmpty ) { return null; } double dis,far_dis,pdist,x,y,z,rad_sq; double cenX = 0.0, cenY = 0.0, cenZ = 0.0; boolean contains = false; boolean inside; boolean intersect = false; double smallest_distance = Double.MAX_VALUE; int i,j,index=0; for(i = 0; i < boundsObjects.length; i++){ if( boundsObjects[i] == null ) ; else if( this.intersect( boundsObjects[i])) { intersect = true; if(boundsObjects[i].boundId == BOUNDING_BOX){ BoundingBox box = (BoundingBox)boundsObjects[i]; cenX = (box.upper.x+box.lower.x)/2.0; cenY = (box.upper.y+box.lower.y)/2.0; cenZ = (box.upper.z+box.lower.z)/2.0; dis = Math.sqrt( (center.x-cenX)*(center.x-cenX) + (center.y-cenY)*(center.y-cenY) + (center.z-cenZ)*(center.z-cenZ) ); if( (center.x-box.lower.x)*(center.x-box.lower.x) > (center.x-box.upper.x)*(center.x-box.upper.x) ) far_dis = (center.x-box.lower.x)*(center.x-box.lower.x); else far_dis = (center.x-box.upper.x)*(center.x-box.upper.x); if( (center.y-box.lower.y)*(center.y-box.lower.y) > (center.y-box.upper.y)*(center.y-box.upper.y) ) far_dis += (center.y-box.lower.y)*(center.y-box.lower.y); else far_dis += (center.y-box.upper.y)*(center.y-box.upper.y); if( (center.z-box.lower.z)*(center.z-box.lower.z) > (center.z-box.upper.z)*(center.z-box.upper.z) ) far_dis += (center.z-box.lower.z)*(center.z-box.lower.z); else far_dis += (center.z-box.upper.z)*(center.z-box.upper.z); rad_sq = radius * radius; if( far_dis <= rad_sq ) { // contains box if( !contains ){ // initialize smallest_distance for the first containment index = i; smallest_distance = dis; contains = true; } else{ if( dis < smallest_distance){ index = i; smallest_distance = dis; } } } else if (!contains) { if( dis < smallest_distance){ index = i; smallest_distance = dis; } } } else if( boundsObjects[i].boundId == BOUNDING_SPHERE ) { BoundingSphere sphere = (BoundingSphere)boundsObjects[i]; dis = Math.sqrt( (center.x-sphere.center.x)*(center.x-sphere.center.x) + (center.y-sphere.center.y)*(center.y-sphere.center.y) + (center.z-sphere.center.z)*(center.z-sphere.center.z) ); if( (dis+sphere.radius) <= radius) { // contains the sphere if( !contains ){ // initialize smallest_distance for the first containment index = i; smallest_distance = dis; contains = true; } else{ if( dis < smallest_distance){ index = i; smallest_distance = dis; } } } else if (!contains) { if( dis < smallest_distance){ index = i; smallest_distance = dis; } } } else if(boundsObjects[i].boundId == BOUNDING_POLYTOPE) { BoundingPolytope polytope = (BoundingPolytope)boundsObjects[i]; dis = Math.sqrt( (center.x-polytope.centroid.x)*(center.x-polytope.centroid.x) + (center.y-polytope.centroid.y)*(center.y-polytope.centroid.y) + (center.z-polytope.centroid.z)*(center.z-polytope.centroid.z) ); inside = true; for(j=0;j radius*radius) inside=false; } if( inside ) { if( !contains ){ // initialize smallest_distance for the first containment index = i; smallest_distance = dis; contains = true; } else{ if( dis < smallest_distance){ index = i; smallest_distance = dis; } } } else if (!contains) { if( dis < smallest_distance){ index = i; smallest_distance = dis; } } } else { throw new IllegalArgumentException(J3dI18N.getString("BoundingSphere10")); } } } if ( intersect ) return boundsObjects[index]; else return null; } /** * Intersects this bounding sphere with preprocessed frustum. * @return true if the bounding sphere and frustum intersect. */ boolean intersect(CachedFrustum frustum) { int i; double dist; if( boundsIsEmpty ) { return false; } if(boundsIsInfinite) return true; for (i=0; i<6; i++) { dist = frustum.clipPlanes[i].x*center.x + frustum.clipPlanes[i].y*center.y + frustum.clipPlanes[i].z*center.z + frustum.clipPlanes[i].w; if (dist < 0.0 && (dist + radius) < 0.0) { return(false); } } return true; } /** * This intersects this bounding sphere with 6 frustum plane equations * @return returns true if the bounding sphere and frustum intersect. */ boolean intersect(Vector4d[] planes) { int i; double dist; if( boundsIsEmpty ) { return false; } if(boundsIsInfinite) return true; for (i=0; i<6; i++) { dist = planes[i].x*center.x + planes[i].y*center.y + planes[i].z*center.z + planes[i].w; if (dist < 0.0 && (dist + radius) < 0.0) { //System.err.println("Tossing " + i + " " + dist + " " + radius); return(false); } } return true; } /** * Returns a string representation of this class. */ @Override public String toString() { return new String( "Center="+center+" Radius="+radius); } private void setEmptyBounds() { center.set(0.0d, 0.0d, 0.0d); radius = -1.0; boundsIsInfinite = false; boundsIsEmpty = true; } private void setInfiniteBounds() { center.set(0.0d, 0.0d, 0.0d); radius = Double.POSITIVE_INFINITY; boundsIsEmpty = false; boundsIsInfinite = true; } private void updateBoundsStates() { if (Double.isNaN(radius + center.x + center.y + center.z)) { boundsIsEmpty = true; boundsIsInfinite = false; return; } if(radius == Double.POSITIVE_INFINITY) { boundsIsEmpty = false; boundsIsInfinite = true; } else { boundsIsInfinite = false; if( radius < 0.0 ) { boundsIsEmpty = true; } else { boundsIsEmpty = false; } } } @Override Point3d getCenter() { return center; } /** * if the passed the "region" is same type as this object * then do a copy, otherwise clone the Bounds and * return */ @Override Bounds copy(Bounds r) { if (r != null && this.boundId == r.boundId) { BoundingSphere region = (BoundingSphere)r; region.radius = radius; region.center.x = center.x; region.center.y = center.y; region.center.z = center.z; region.boundsIsEmpty = boundsIsEmpty; region.boundsIsInfinite = boundsIsInfinite; return region; } else { return (Bounds) this.clone(); } } @Override int getPickType() { return PickShape.PICKBOUNDINGSPHERE; } }