Java tutorial
/* * 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 < boundsObjects.length; i++) { if (boundsObjects[i] == null) ; // do nothing else if (boundsObjects[i].boundsIsEmpty) ; // do nothing else if (boundsObjects[i].boundsIsInfinite) { setInfiniteBounds(); return; // We're done. } else if (boundsObjects[i].boundId == BOUNDING_BOX) { BoundingBox b = (BoundingBox) boundsObjects[i]; if (boxVerts == null) { boxVerts = new Point3d[8]; for (int j = 0; j < 8; j++) boxVerts[j] = new Point3d(); } 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 (boundsObjects[i].boundId == BOUNDING_SPHERE) { double dis, t, d1; 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 (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) { 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 < 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) { // 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 <code>bounds</code> object is * equal to this BoundingSphere object. They are equal if the * specified <code>bounds</code> object is an instance of * BoundingSphere and all of the data * members of <code>bounds</code> 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 <code>bounds</code>; * 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) && ((boundsObjects[i] == null) || boundsObjects[i].boundsIsEmpty)) { i++; } if (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 < boundsObjects.length; i++) { if (boundsObjects[i] == null) ; // do nothing else if (boundsObjects[i].boundsIsEmpty) ; // do nothing else if (boundsObjects[i].boundsIsInfinite) { setInfiniteBounds(); return; // We're done. } else if (boundsObjects[i].boundId == BOUNDING_BOX) { b = (BoundingBox) boundsObjects[i]; // 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 (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 < points.length; i++) { rad_sq = radius * radius; dis_sq = (points[i].x - center.x) * (points[i].x - center.x) + (points[i].y - center.y) * (points[i].y - center.y) + (points[i].z - center.z) * (points[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; 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 <i>not</i> 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 < boundsObjects.length; i++) { if (boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty) ; else if (boundsObjects[i].boundId == BOUNDING_BOX) { BoundingBox box = (BoundingBox) boundsObjects[i]; if (this.intersect(box)) { BoundingBox sbox = new BoundingBox(this); // convert sphere to box sbox.intersect(box, tbox); // insersect two boxes if (status) { newBoundSphere.combine(tbox); } else { newBoundSphere.set(tbox); // set sphere to the intersection of 2 boxesf status = true; } } } else if (boundsObjects[i].boundId == BOUNDING_SPHERE) { BoundingSphere sphere = (BoundingSphere) boundsObjects[i]; 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) { } 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 < polytope.nVerts; j++) { x = polytope.verts[j].x - center.x; y = polytope.verts[j].y - center.y; z = polytope.verts[j].z - center.z; pdist = x * x + y * y + z * z; if (pdist > 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; } }