javax.media.j3d.BoundingBox.java Source code

Java tutorial

Introduction

Here is the source code for javax.media.j3d.BoundingBox.java

Source

/*
 * 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 an axis aligned bounding box which is used for
 *  bounding regions.
 *
 */

public class BoundingBox extends Bounds {

    /**
     * The corner of the bounding box with the numerically smallest values.
     */
    final Point3d lower;

    /**
     * The corner of the bounding box with the numerically largest values.
     */
    final Point3d upper;
    private static final double EPS = 1.0E-8;

    /**
     * Constructs and initializes a BoundingBox given min,max in x,y,z.
     * @param lower the "small" corner
     * @param upper the "large" corner
     */
    public BoundingBox(Point3d lower, Point3d upper) {
        boundId = BOUNDING_BOX;
        this.lower = new Point3d(lower);
        this.upper = new Point3d(upper);
        updateBoundsStates();
    }

    /**
     * Constructs and initializes a 2X bounding box about the origin. The lower
     * corner is initialized to (-1.0d, -1.0d, -1.0d) and the upper corner is
     * initialized to (1.0d, 1.0d, 1.0d).
     */
    public BoundingBox() {
        boundId = BOUNDING_BOX;
        lower = new Point3d(-1.0d, -1.0d, -1.0d);
        upper = new Point3d(1.0d, 1.0d, 1.0d);
        boundsIsEmpty = false;
        boundsIsInfinite = false;
    }

    /**
     * Constructs a BoundingBox from a bounding object.
     * @param boundsObject a bounds object
     */
    public BoundingBox(Bounds boundsObject) {
        boundId = BOUNDING_BOX;
        lower = new Point3d();
        upper = new Point3d();

        if (boundsObject == null || boundsObject.boundsIsEmpty) {
            setEmptyBounds();
            return;
        }

        if (boundsObject.boundsIsInfinite) {
            setInfiniteBounds();
            return;
        }

        if (boundsObject.boundId == BOUNDING_BOX) {
            BoundingBox box = (BoundingBox) boundsObject;

            lower.set(box.lower);
            upper.set(box.upper);
        } else if (boundsObject.boundId == BOUNDING_SPHERE) {
            BoundingSphere sphere = (BoundingSphere) boundsObject;

            lower.set(sphere.center.x - sphere.radius, sphere.center.y - sphere.radius,
                    sphere.center.z - sphere.radius);

            upper.set(sphere.center.x + sphere.radius, sphere.center.y + sphere.radius,
                    sphere.center.z + sphere.radius);
        } else if (boundsObject.boundId == BOUNDING_POLYTOPE) {
            BoundingPolytope polytope = (BoundingPolytope) boundsObject;
            if (polytope.nVerts < 1) { // handle degenerate case
                lower.set(-1.0d, -1.0d, -1.0d);
                upper.set(1.0d, 1.0d, 1.0d);
            } else {
                lower.set(polytope.verts[0]);
                upper.set(polytope.verts[0]);

                for (int i = 1; i < polytope.nVerts; i++) {
                    if (polytope.verts[i].x < lower.x)
                        lower.x = polytope.verts[i].x;
                    if (polytope.verts[i].y < lower.y)
                        lower.y = polytope.verts[i].y;
                    if (polytope.verts[i].z < lower.z)
                        lower.z = polytope.verts[i].z;
                    if (polytope.verts[i].x > upper.x)
                        upper.x = polytope.verts[i].x;
                    if (polytope.verts[i].y > upper.y)
                        upper.y = polytope.verts[i].y;
                    if (polytope.verts[i].z > upper.z)
                        upper.z = polytope.verts[i].z;
                }
            }

        } else {
            throw new IllegalArgumentException(J3dI18N.getString("BoundingBox0"));
        }

        updateBoundsStates();
    }

    /**
     * Constructs a BoundingBox from an array of bounding objects.
     * @param bounds an array of bounding objects
     */
    public BoundingBox(Bounds[] bounds) {
        boundId = BOUNDING_BOX;
        upper = new Point3d();
        lower = new Point3d();

        if (bounds == null || bounds.length <= 0) {
            setEmptyBounds();
            return;
        }

        int i = 0;
        // find first non empty bounds object
        while ((i < bounds.length) && ((bounds[i] == null) || bounds[i].boundsIsEmpty)) {
            i++;
        }

        if (i >= bounds.length) {
            // all bounds objects were empty
            setEmptyBounds();
            return;
        }

        this.set(bounds[i++]);
        if (boundsIsInfinite)
            return;

        for (; i < bounds.length; i++) {
            if (bounds[i] == null)
                ; // do nothing
            else if (bounds[i].boundsIsEmpty)
                ; // do nothing
            else if (bounds[i].boundsIsInfinite) {
                setInfiniteBounds();
                return; // We're done.
            } else if (bounds[i].boundId == BOUNDING_BOX) {
                BoundingBox box = (BoundingBox) bounds[i];

                if (lower.x > box.lower.x)
                    lower.x = box.lower.x;
                if (lower.y > box.lower.y)
                    lower.y = box.lower.y;
                if (lower.z > box.lower.z)
                    lower.z = box.lower.z;
                if (upper.x < box.upper.x)
                    upper.x = box.upper.x;
                if (upper.y < box.upper.y)
                    upper.y = box.upper.y;
                if (upper.z < box.upper.z)
                    upper.z = box.upper.z;

            } else if (bounds[i].boundId == BOUNDING_SPHERE) {
                BoundingSphere sphere = (BoundingSphere) bounds[i];
                if (lower.x > (sphere.center.x - sphere.radius))
                    lower.x = sphere.center.x - sphere.radius;
                if (lower.y > (sphere.center.y - sphere.radius))
                    lower.y = sphere.center.y - sphere.radius;
                if (lower.z > (sphere.center.z - sphere.radius))
                    lower.z = sphere.center.z - sphere.radius;
                if (upper.x < (sphere.center.x + sphere.radius))
                    upper.x = sphere.center.x + sphere.radius;
                if (upper.y < (sphere.center.y + sphere.radius))
                    upper.y = sphere.center.y + sphere.radius;
                if (upper.z < (sphere.center.z + sphere.radius))
                    upper.z = sphere.center.z + sphere.radius;
            } else if (bounds[i].boundId == BOUNDING_POLYTOPE) {
                BoundingPolytope polytope = (BoundingPolytope) bounds[i];

                for (i = 0; i < polytope.nVerts; i++) { // XXXX: handle polytope with no verts
                    if (polytope.verts[i].x < lower.x)
                        lower.x = polytope.verts[i].x;
                    if (polytope.verts[i].y < lower.y)
                        lower.y = polytope.verts[i].y;
                    if (polytope.verts[i].z < lower.z)
                        lower.z = polytope.verts[i].z;
                    if (polytope.verts[i].x > upper.x)
                        upper.x = polytope.verts[i].x;
                    if (polytope.verts[i].y > upper.y)
                        upper.y = polytope.verts[i].y;
                    if (polytope.verts[i].z > upper.z)
                        upper.z = polytope.verts[i].z;
                }
            } else {
                throw new IllegalArgumentException(J3dI18N.getString("BoundingBox1"));
            }
        }
        updateBoundsStates();
    }

    /**
     * Gets the lower corner of this bounding box.
     * @param p1 a Point to receive the lower corner of the bounding box
     */
    public void getLower(Point3d p1) {
        p1.set(lower);
    }

    /**
     * Sets the lower corner of this bounding box.
     * @param xmin minimum x value of bounding box
     * @param ymin minimum y value of bounding box
     * @param zmin minimum z value of bounding box
     */
    public void setLower(double xmin, double ymin, double zmin) {
        lower.set(xmin, ymin, zmin);
        updateBoundsStates();
    }

    /**
     * Sets the lower corner of this bounding box.
     * @param p1 a Point defining the new lower corner of the bounding box
     */
    public void setLower(Point3d p1) {
        lower.set(p1);
        updateBoundsStates();
    }

    /**
     * Gets the upper corner of this bounding box.
     * @param p1 a Point to receive the upper corner of the bounding box
     */
    public void getUpper(Point3d p1) {
        p1.set(upper);
    }

    /**
     * Sets the upper corner of this bounding box.
     * @param xmax max x value of bounding box
     * @param ymax max y value of bounding box
     * @param zmax max z value of bounding box
     */
    public void setUpper(double xmax, double ymax, double zmax) {
        upper.set(xmax, ymax, zmax);
        updateBoundsStates();
    }

    /**
     * Sets the upper corner of this bounding box.
     * @param p1 a Point defining the new upper corner of the bounding box
     */
    public void setUpper(Point3d p1) {
        upper.set(p1);
        updateBoundsStates();
    }

    /**
     * Sets the the value of this BoundingBox
     * @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;

            lower.x = box.lower.x;
            lower.y = box.lower.y;
            lower.z = box.lower.z;
            upper.x = box.upper.x;
            upper.y = box.upper.y;
            upper.z = box.upper.z;

        } else if (boundsObject.boundId == BOUNDING_SPHERE) {
            BoundingSphere sphere = (BoundingSphere) boundsObject;
            lower.x = sphere.center.x - sphere.radius;
            lower.y = sphere.center.y - sphere.radius;
            lower.z = sphere.center.z - sphere.radius;
            upper.x = sphere.center.x + sphere.radius;
            upper.y = sphere.center.y + sphere.radius;
            upper.z = sphere.center.z + sphere.radius;

        } else if (boundsObject.boundId == BOUNDING_POLYTOPE) {
            BoundingPolytope polytope = (BoundingPolytope) boundsObject;
            lower.x = upper.x = polytope.verts[0].x;
            lower.y = upper.y = polytope.verts[0].y;
            lower.z = upper.z = polytope.verts[0].z;

            for (i = 1; i < polytope.nVerts; i++) {
                if (polytope.verts[i].x < lower.x)
                    lower.x = polytope.verts[i].x;
                if (polytope.verts[i].y < lower.y)
                    lower.y = polytope.verts[i].y;
                if (polytope.verts[i].z < lower.z)
                    lower.z = polytope.verts[i].z;
                if (polytope.verts[i].x > upper.x)
                    upper.x = polytope.verts[i].x;
                if (polytope.verts[i].y > upper.y)
                    upper.y = polytope.verts[i].y;
                if (polytope.verts[i].z > upper.z)
                    upper.z = polytope.verts[i].z;
            }

        } else {
            throw new IllegalArgumentException(J3dI18N.getString("BoundingBox0"));
        }

        updateBoundsStates();
    }

    /**
     * Creates a copy of this bounding box.
     * @return a new bounding box
     */
    @Override
    public Object clone() {
        return new BoundingBox(this.lower, this.upper);
    }

    /**
     * Indicates whether the specified <code>bounds</code> object is
     * equal to this BoundingBox object.  They are equal if the
     * specified <code>bounds</code> object is an instance of
     * BoundingBox and all of the data
     * members of <code>bounds</code> are equal to the corresponding
     * data members in this BoundingBox.
     * @param bounds the object with which the comparison is made.
     * @return true if this BoundingBox is equal to <code>bounds</code>;
     * otherwise false
     *
     * @since Java 3D 1.2
     */
    @Override
    public boolean equals(Object bounds) {
        try {
            BoundingBox box = (BoundingBox) bounds;
            return (lower.equals(box.lower) && upper.equals(box.upper));
        } catch (NullPointerException e) {
            return false;
        } catch (ClassCastException e) {
            return false;
        }
    }

    /**
     * Returns a hash code value for this BoundingBox object
     * based on the data values in this object.  Two different
     * BoundingBox objects with identical data values (i.e.,
     * BoundingBox.equals returns true) will return the same hash
     * code value.  Two BoundingBox objects with different data
     * members may return the same hash code value, although this is
     * not likely.
     * @return a hash code value for this BoundingBox object.
     *
     * @since Java 3D 1.2
     */
    @Override
    public int hashCode() {
        long bits = 1L;
        bits = J3dHash.mixDoubleBits(bits, lower.x);
        bits = J3dHash.mixDoubleBits(bits, lower.y);
        bits = J3dHash.mixDoubleBits(bits, lower.z);
        bits = J3dHash.mixDoubleBits(bits, upper.x);
        bits = J3dHash.mixDoubleBits(bits, upper.y);
        bits = J3dHash.mixDoubleBits(bits, upper.z);
        return J3dHash.finish(bits);
    }

    /**
     * Combines this bounding box with a bounding object   so that the
     * resulting bounding box encloses the original bounding box and the
     * specified bounds object.
     * @param boundsObject another bounds object
     */
    @Override
    public void combine(Bounds boundsObject) {

        if ((boundsObject == null) || (boundsObject.boundsIsEmpty) || (boundsIsInfinite))
            return;

        if ((boundsIsEmpty) || (boundsObject.boundsIsInfinite)) {
            this.set(boundsObject);
            return;
        }

        if (boundsObject.boundId == BOUNDING_BOX) {
            BoundingBox box = (BoundingBox) boundsObject;

            if (lower.x > box.lower.x)
                lower.x = box.lower.x;
            if (lower.y > box.lower.y)
                lower.y = box.lower.y;
            if (lower.z > box.lower.z)
                lower.z = box.lower.z;
            if (upper.x < box.upper.x)
                upper.x = box.upper.x;
            if (upper.y < box.upper.y)
                upper.y = box.upper.y;
            if (upper.z < box.upper.z)
                upper.z = box.upper.z;

        } else if (boundsObject.boundId == BOUNDING_SPHERE) {
            BoundingSphere sphere = (BoundingSphere) boundsObject;
            if (lower.x > (sphere.center.x - sphere.radius))
                lower.x = sphere.center.x - sphere.radius;
            if (lower.y > (sphere.center.y - sphere.radius))
                lower.y = sphere.center.y - sphere.radius;
            if (lower.z > (sphere.center.z - sphere.radius))
                lower.z = sphere.center.z - sphere.radius;
            if (upper.x < (sphere.center.x + sphere.radius))
                upper.x = sphere.center.x + sphere.radius;
            if (upper.y < (sphere.center.y + sphere.radius))
                upper.y = sphere.center.y + sphere.radius;
            if (upper.z < (sphere.center.z + sphere.radius))
                upper.z = sphere.center.z + sphere.radius;

        } else if (boundsObject.boundId == BOUNDING_POLYTOPE) {
            BoundingPolytope polytope = (BoundingPolytope) boundsObject;
            int i;
            for (i = 1; i < polytope.nVerts; i++) {
                if (polytope.verts[i].x < lower.x)
                    lower.x = polytope.verts[i].x;
                if (polytope.verts[i].y < lower.y)
                    lower.y = polytope.verts[i].y;
                if (polytope.verts[i].z < lower.z)
                    lower.z = polytope.verts[i].z;
                if (polytope.verts[i].x > upper.x)
                    upper.x = polytope.verts[i].x;
                if (polytope.verts[i].y > upper.y)
                    upper.y = polytope.verts[i].y;
                if (polytope.verts[i].z > upper.z)
                    upper.z = polytope.verts[i].z;
            }
        } else {
            throw new IllegalArgumentException(J3dI18N.getString("BoundingBox3"));
        }

        updateBoundsStates();
    }

    /**
     * Combines this bounding box with an array of bounding objects
     * so that the resulting bounding box encloses the original bounding
     * box and the array of bounding objects.
     * @param bounds an array of bounds objects
     */
    @Override
    public void combine(Bounds[] bounds) {
        int i = 0;

        if ((bounds == null) || (bounds.length <= 0) || (boundsIsInfinite))
            return;

        // find first non empty bounds object
        while ((i < bounds.length) && ((bounds[i] == null) || bounds[i].boundsIsEmpty)) {
            i++;
        }
        if (i >= bounds.length)
            return; // no non empty bounds so do not modify current bounds

        if (boundsIsEmpty)
            this.set(bounds[i++]);

        if (boundsIsInfinite)
            return;

        for (; i < bounds.length; i++) {
            if (bounds[i] == null)
                ; // do nothing
            else if (bounds[i].boundsIsEmpty)
                ; // do nothing
            else if (bounds[i].boundsIsInfinite) {
                lower.x = lower.y = lower.z = Double.NEGATIVE_INFINITY;
                upper.x = upper.y = upper.z = Double.POSITIVE_INFINITY;
                break; // We're done.
            } else if (bounds[i].boundId == BOUNDING_BOX) {
                BoundingBox box = (BoundingBox) bounds[i];

                if (lower.x > box.lower.x)
                    lower.x = box.lower.x;
                if (lower.y > box.lower.y)
                    lower.y = box.lower.y;
                if (lower.z > box.lower.z)
                    lower.z = box.lower.z;
                if (upper.x < box.upper.x)
                    upper.x = box.upper.x;
                if (upper.y < box.upper.y)
                    upper.y = box.upper.y;
                if (upper.z < box.upper.z)
                    upper.z = box.upper.z;
            } else if (bounds[i].boundId == BOUNDING_SPHERE) {
                BoundingSphere sphere = (BoundingSphere) bounds[i];
                if (lower.x > (sphere.center.x - sphere.radius))
                    lower.x = sphere.center.x - sphere.radius;
                if (lower.y > (sphere.center.y - sphere.radius))
                    lower.y = sphere.center.y - sphere.radius;
                if (lower.z > (sphere.center.z - sphere.radius))
                    lower.z = sphere.center.z - sphere.radius;
                if (upper.x < (sphere.center.x + sphere.radius))
                    upper.x = sphere.center.x + sphere.radius;
                if (upper.y < (sphere.center.y + sphere.radius))
                    upper.y = sphere.center.y + sphere.radius;
                if (upper.z < (sphere.center.z + sphere.radius))
                    upper.z = sphere.center.z + sphere.radius;
            } else if (bounds[i].boundId == BOUNDING_POLYTOPE) {
                BoundingPolytope polytope = (BoundingPolytope) bounds[i];
                for (i = 1; i < polytope.nVerts; i++) {
                    if (polytope.verts[i].x < lower.x)
                        lower.x = polytope.verts[i].x;
                    if (polytope.verts[i].y < lower.y)
                        lower.y = polytope.verts[i].y;
                    if (polytope.verts[i].z < lower.z)
                        lower.z = polytope.verts[i].z;
                    if (polytope.verts[i].x > upper.x)
                        upper.x = polytope.verts[i].x;
                    if (polytope.verts[i].y > upper.y)
                        upper.y = polytope.verts[i].y;
                    if (polytope.verts[i].z > upper.z)
                        upper.z = polytope.verts[i].z;
                }
            } else {
                throw new IllegalArgumentException(J3dI18N.getString("BoundingBox4"));
            }
        }

        updateBoundsStates();
    }

    /**
     * Combines this bounding box with a point so that the resulting
     * bounding box encloses the original bounding box and the point.
     * @param point a 3d point in space
     */
    @Override
    public void combine(Point3d point) {

        if (boundsIsInfinite) {
            return;
        }

        if (boundsIsEmpty) {
            upper.x = lower.x = point.x;
            upper.y = lower.y = point.y;
            upper.z = lower.z = point.z;
        } else {
            if (point.x > upper.x)
                upper.x = point.x;
            if (point.y > upper.y)
                upper.y = point.y;
            if (point.z > upper.z)
                upper.z = point.z;

            if (point.x < lower.x)
                lower.x = point.x;
            if (point.y < lower.y)
                lower.y = point.y;
            if (point.z < lower.z)
                lower.z = point.z;
        }

        updateBoundsStates();
    }

    /**
     * Combines this bounding box with an array of points so that the
     * resulting bounding box encloses the original bounding box and the
     * array of points.
     * @param points an array of 3d points in space
     */
    @Override
    public void combine(Point3d[] points) {

        int i;

        if (boundsIsInfinite) {
            return;
        }

        if (boundsIsEmpty) {
            this.setUpper(points[0]);
            this.setLower(points[0]);
        }

        for (i = 0; i < points.length; i++) {
            if (points[i].x > upper.x)
                upper.x = points[i].x;
            if (points[i].y > upper.y)
                upper.y = points[i].y;
            if (points[i].z > upper.z)
                upper.z = points[i].z;

            if (points[i].x < lower.x)
                lower.x = points[i].x;
            if (points[i].y < lower.y)
                lower.y = points[i].y;
            if (points[i].z < lower.z)
                lower.z = points[i].z;
        }

        updateBoundsStates();
    }

    /**
     * Modifies the bounding box 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) {
            this.set(boundsObject);
            this.transform(matrix);
        } else if (boundsObject.boundId == BOUNDING_SPHERE) {
            BoundingSphere tmpSphere = new BoundingSphere(boundsObject);
            tmpSphere.transform(matrix);
            this.set(tmpSphere);
        } else if (boundsObject.boundId == BOUNDING_POLYTOPE) {
            BoundingPolytope tmpPolytope = new BoundingPolytope(boundsObject);
            tmpPolytope.transform(matrix);
            this.set(tmpPolytope);
        } else {
            throw new IllegalArgumentException(J3dI18N.getString("BoundingBox5"));
        }
    }

    /**
     * Transforms this bounding box by the given matrix.
     * @param matrix a transformation matrix
     */
    @Override
    public void transform(Transform3D matrix) {

        if (boundsIsInfinite)
            return;

        Point3d tmpP3d = new Point3d();

        double ux, uy, uz, lx, ly, lz;
        ux = upper.x;
        uy = upper.y;
        uz = upper.z;
        lx = lower.x;
        ly = lower.y;
        lz = lower.z;

        tmpP3d.set(ux, uy, uz);
        matrix.transform(tmpP3d);
        upper.x = tmpP3d.x;
        upper.y = tmpP3d.y;
        upper.z = tmpP3d.z;
        lower.x = tmpP3d.x;
        lower.y = tmpP3d.y;
        lower.z = tmpP3d.z;

        tmpP3d.set(lx, uy, uz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

        tmpP3d.set(lx, ly, uz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

        tmpP3d.set(ux, ly, uz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

        tmpP3d.set(lx, uy, lz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

        tmpP3d.set(ux, uy, lz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

        tmpP3d.set(lx, ly, lz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

        tmpP3d.set(ux, ly, lz);
        matrix.transform(tmpP3d);
        if (tmpP3d.x > upper.x)
            upper.x = tmpP3d.x;
        if (tmpP3d.y > upper.y)
            upper.y = tmpP3d.y;
        if (tmpP3d.z > upper.z)
            upper.z = tmpP3d.z;
        if (tmpP3d.x < lower.x)
            lower.x = tmpP3d.x;
        if (tmpP3d.y < lower.y)
            lower.y = tmpP3d.y;
        if (tmpP3d.z < lower.z)
            lower.z = tmpP3d.z;

    }

    /**
     * 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) {
        double t1, t2, tmp, tnear, tfar, invDir, invMag;
        double dirx, diry, dirz;

        /*
          System.err.println("BoundingBox.intersect(p,d,p) called\n");
          System.err.println("bounds = " + lower + " -> " + upper);
          */

        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 dirLen = direction.x * direction.x + direction.y * direction.y + direction.z * direction.z;

        // Handle zero length direction vector.
        if (dirLen == 0.0)
            return intersect(origin, position);

        invMag = 1.0 / Math.sqrt(dirLen);
        dirx = direction.x * invMag;
        diry = direction.y * invMag;
        dirz = direction.z * invMag;

        /*
          System.err.println("dir = " + dirx + ", " + diry + ", " + dirz);
          System.err.println("origin = " + origin);
          */

        // initialize tnear and tfar to handle dir.? == 0 cases
        tnear = -Double.MAX_VALUE;
        tfar = Double.MAX_VALUE;

        if (dirx == 0.0) {
            //System.err.println("dirx == 0.0");
            if (origin.x < lower.x || origin.x > upper.x) {
                //System.err.println( "parallel to x plane and outside");
                return false;
            }
        } else {
            invDir = 1.0 / dirx;
            t1 = (lower.x - origin.x) * invDir;
            t2 = (upper.x - origin.x) * invDir;

            //System.err.println("x t1 = " + t1 + " t2 = " + t2);
            if (t1 > t2) {
                tnear = t2;
                tfar = t1;
            } else {
                tnear = t1;
                tfar = t2;
            }
            if (tfar < 0.0) {
                //System.err.println( "x failed: tnear="+tnear+"  tfar="+tfar);
                return false;
            }
            //System.err.println("x tnear = " + tnear + " tfar = " + tfar);
        }
        // y
        if (diry == 0.0) {
            //System.err.println("diry == 0.0");
            if (origin.y < lower.y || origin.y > upper.y) {
                //System.err.println( "parallel to y plane and outside");
                return false;
            }
        } else {
            invDir = 1.0 / diry;
            //System.err.println("invDir = " + invDir);
            t1 = (lower.y - origin.y) * invDir;
            t2 = (upper.y - origin.y) * invDir;

            if (t1 > t2) {
                tmp = t1;
                t1 = t2;
                t2 = tmp;
            }
            //System.err.println("y t1 = " + t1 + " t2 = " + t2);
            if (t1 > tnear)
                tnear = t1;
            if (t2 < tfar)
                tfar = t2;

            if ((tfar < 0.0) || (tnear > tfar)) {
                //System.err.println( "y failed: tnear="+tnear+"  tfar="+tfar);
                return false;
            }
            //System.err.println("y tnear = " + tnear + " tfar = " + tfar);
        }

        // z
        if (dirz == 0.0) {
            //System.err.println("dirz == 0.0");
            if (origin.z < lower.z || origin.z > upper.z) {
                //System.err.println( "parallel to z plane and outside");
                return false;
            }
        } else {
            invDir = 1.0 / dirz;
            t1 = (lower.z - origin.z) * invDir;
            t2 = (upper.z - origin.z) * invDir;

            if (t1 > t2) {
                tmp = t1;
                t1 = t2;
                t2 = tmp;
            }
            //System.err.println("z t1 = " + t1 + " t2 = " + t2);
            if (t1 > tnear)
                tnear = t1;
            if (t2 < tfar)
                tfar = t2;

            if ((tfar < 0.0) || (tnear > tfar)) {
                //System.err.println( "z failed: tnear="+tnear+"  tfar="+tfar);
                return false;
            }
            //System.err.println("z tnear = " + tnear + " tfar = " + tfar);
        }

        if ((tnear < 0.0) && (tfar >= 0.0)) {
            // origin is inside the BBox.
            position.x = origin.x + dirx * tfar;
            position.y = origin.y + diry * tfar;
            position.z = origin.z + dirz * tfar;
            position.w = tfar;
        } else {
            position.x = origin.x + dirx * tnear;
            position.y = origin.y + diry * tnear;
            position.z = origin.z + dirz * tnear;
            position.w = tnear;
        }

        return true;

    }

    /**
     * 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) {

        if (boundsIsEmpty) {
            return false;
        }

        if (boundsIsInfinite) {
            position.x = point.x;
            position.y = point.y;
            position.z = point.z;
            position.w = 0.0;
            return true;
        }

        if (point.x <= upper.x && point.x >= lower.x && point.y <= upper.y && point.y >= lower.y
                && point.z <= upper.z && point.z >= lower.z) {
            position.x = point.x;
            position.y = point.y;
            position.z = point.z;
            position.w = 0.0;
            return true;
        } else
            return false;

    }

    /**
     * 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) {
        double t1, t2, tmp, tnear, tfar, invDir, invMag;
        double dirx, diry, dirz;

        if (boundsIsEmpty) {
            return false;
        }

        if (boundsIsInfinite) {
            position.x = start.x;
            position.y = start.y;
            position.z = start.z;
            position.w = 0.0;
            return true;
        }

        dirx = end.x - start.x;
        diry = end.y - start.y;
        dirz = end.z - start.z;

        double dirLen = dirx * dirx + diry * diry + dirz * dirz;

        // Optimization : Handle zero length direction vector.
        if (dirLen == 0.0)
            return intersect(start, position);

        dirLen = Math.sqrt(dirLen);
        // System.err.println("dirLen is " + dirLen);
        invMag = 1.0 / dirLen;
        dirx = dirx * invMag;
        diry = diry * invMag;
        dirz = dirz * invMag;

        /*
        System.err.println("dir = " + dir);
        System.err.println("start = " + start);
        System.err.println("lower = " + lower);
        System.err.println("upper = " + upper);
        */

        // initialize tnear and tfar to handle dir.? == 0 cases
        tnear = -Double.MAX_VALUE;
        tfar = Double.MAX_VALUE;

        if (dirx == 0.0) {
            //System.err.println("dirx == 0.0");
            if (start.x < lower.x || start.x > upper.x) {
                //System.err.println( "parallel to x plane and outside");
                return false;
            }
        } else {
            invDir = 1.0 / dirx;
            t1 = (lower.x - start.x) * invDir;
            t2 = (upper.x - start.x) * invDir;

            //System.err.println("x t1 = " + t1 + " t2 = " + t2);
            if (t1 > t2) {
                tnear = t2;
                tfar = t1;
            } else {
                tnear = t1;
                tfar = t2;
            }
            if (tfar < 0.0) {
                //System.err.println( "x failed: tnear="+tnear+"  tfar="+tfar);
                return false;
            }
            //System.err.println("x tnear = " + tnear + " tfar = " + tfar);
        }
        // y
        if (diry == 0.0) {
            //System.err.println("diry == 0.0");
            if (start.y < lower.y || start.y > upper.y) {
                //System.err.println( "parallel to y plane and outside");
                return false;
            }
        } else {
            invDir = 1.0 / diry;
            //System.err.println("invDir = " + invDir);
            t1 = (lower.y - start.y) * invDir;
            t2 = (upper.y - start.y) * invDir;

            if (t1 > t2) {
                tmp = t1;
                t1 = t2;
                t2 = tmp;
            }
            //System.err.println("y t1 = " + t1 + " t2 = " + t2);
            if (t1 > tnear)
                tnear = t1;
            if (t2 < tfar)
                tfar = t2;

            if ((tfar < 0.0) || (tnear > tfar)) {
                //System.err.println( "y failed: tnear="+tnear+"  tfar="+tfar);
                return false;
            }
            //System.err.println("y tnear = " + tnear + " tfar = " + tfar);
        }

        // z
        if (dirz == 0.0) {
            //System.err.println("dirz == 0.0");
            if (start.z < lower.z || start.z > upper.z) {
                //System.err.println( "parallel to z plane and outside");
                return false;
            }
        } else {
            invDir = 1.0 / dirz;
            t1 = (lower.z - start.z) * invDir;
            t2 = (upper.z - start.z) * invDir;

            if (t1 > t2) {
                tmp = t1;
                t1 = t2;
                t2 = tmp;
            }
            //System.err.println("z t1 = " + t1 + " t2 = " + t2);
            if (t1 > tnear)
                tnear = t1;
            if (t2 < tfar)
                tfar = t2;

            if ((tfar < 0.0) || (tnear > tfar)) {
                //System.err.println( "z failed: tnear="+tnear+"  tfar="+tfar);
                return false;
            }
            //System.err.println("z tnear = " + tnear + " tfar = " + tfar);
        }

        if ((tnear < 0.0) && (tfar >= 0.0)) {
            // origin is inside the BBox.
            position.x = start.x + dirx * tfar;
            position.y = start.y + diry * tfar;
            position.z = start.z + dirz * tfar;
            position.w = tfar;
        } else {
            if (tnear > dirLen) {
                // Segment is behind BBox.
                /*
                  System.err.println("PickSegment : intersected postion : " + position
                  + " tnear " + tnear + " tfar " + tfar );
                  */
                return false;
            }
            position.x = start.x + dirx * tnear;
            position.y = start.y + diry * tnear;
            position.z = start.z + dirz * tnear;

            position.w = tnear;
        }

        /*
            System.err.println("tnear = " + tnear + " tfar = " + tfar + " w " +
            position.w);
            System.err.println("lower = " + lower);
            System.err.println("upper = " + upper + "\n");
        */
        return true;

    }

    /**
     * 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;
        }

        Point3d p = new Point3d();
        return intersect(origin, direction, p);
    }

    /**
     * A protected intersect method that returns the point of intersection.
     * Used by Picking methods to sort or return closest picked item.
     */
    boolean intersect(Point3d origin, Vector3d direction, Point3d intersect) {
        double theta = 0.0;

        if (boundsIsEmpty) {
            return false;
        }

        if (boundsIsInfinite) {
            intersect.x = origin.x;
            intersect.y = origin.y;
            intersect.z = origin.z;
            return true;
        }

        if (direction.x > 0.0)
            theta = Math.max(theta, (lower.x - origin.x) / direction.x);
        if (direction.x < 0.0)
            theta = Math.max(theta, (upper.x - origin.x) / direction.x);
        if (direction.y > 0.0)
            theta = Math.max(theta, (lower.y - origin.y) / direction.y);
        if (direction.y < 0.0)
            theta = Math.max(theta, (upper.y - origin.y) / direction.y);
        if (direction.z > 0.0)
            theta = Math.max(theta, (lower.z - origin.z) / direction.z);
        if (direction.z < 0.0)
            theta = Math.max(theta, (upper.z - origin.z) / direction.z);

        intersect.x = origin.x + theta * direction.x;
        intersect.y = origin.y + theta * direction.y;
        intersect.z = origin.z + theta * direction.z;

        if (intersect.x < (lower.x - EPS))
            return false;
        if (intersect.x > (upper.x + EPS))
            return false;
        if (intersect.y < (lower.y - EPS))
            return false;
        if (intersect.y > (upper.y + EPS))
            return false;
        if (intersect.z < (lower.z - EPS))
            return false;
        if (intersect.z > (upper.z + EPS))
            return false;

        return true;

    }

    /**
     * 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) {

        if (boundsIsEmpty) {
            return false;
        }
        if (boundsIsInfinite) {
            return true;
        }

        if (point.x <= upper.x && point.x >= lower.x && point.y <= upper.y && point.y >= lower.y
                && point.z <= upper.z && point.z >= lower.z)
            return true;
        else
            return false;
    }

    /**
     * Tests whether the bounding box is empty.  A bounding box 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 box
     * with a volume of zero is <i>not</i> empty.
     * @return true if the bounding box 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) {

        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;
            // both boxes are axis aligned
            if (upper.x > box.lower.x && box.upper.x > lower.x && upper.y > box.lower.y && box.upper.y > lower.y
                    && upper.z > box.lower.z && box.upper.z > lower.z)
                return true;
            else
                return false;
        } else if (boundsObject.boundId == BOUNDING_SPHERE) {
            BoundingSphere sphere = (BoundingSphere) boundsObject;
            double rad_sq = sphere.radius * sphere.radius;
            double dis = 0.0;

            if (sphere.center.x < lower.x)
                dis = (sphere.center.x - lower.x) * (sphere.center.x - lower.x);
            else if (sphere.center.x > upper.x)
                dis = (sphere.center.x - upper.x) * (sphere.center.x - upper.x);

            if (sphere.center.y < lower.y)
                dis += (sphere.center.y - lower.y) * (sphere.center.y - lower.y);
            else if (sphere.center.y > upper.y)
                dis += (sphere.center.y - upper.y) * (sphere.center.y - upper.y);

            if (sphere.center.z < lower.z)
                dis += (sphere.center.z - lower.z) * (sphere.center.z - lower.z);
            else if (sphere.center.z > upper.z)
                dis += (sphere.center.z - upper.z) * (sphere.center.z - upper.z);

            if (dis <= rad_sq)
                return true;
            else
                return false;
        } else if (boundsObject.boundId == BOUNDING_POLYTOPE) {
            // intersect an axis aligned box with a polytope
            return intersect_ptope_abox((BoundingPolytope) boundsObject, this);
        } else {
            throw new IllegalArgumentException(J3dI18N.getString("BoundingBox6"));
        }

    }

    /**
     * Test for intersection with an array of bounds objects.
     * @param boundsObjects an array of bounding objects
     * @return true or false indicating if an intersection occured
     */
    @Override
    public boolean intersect(Bounds[] boundsObjects) {

        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) {
                BoundingBox box = (BoundingBox) boundsObjects[i];
                // both boxes are axis aligned
                if (upper.x > box.lower.x && box.upper.x > lower.x && upper.y > box.lower.y && box.upper.y > lower.y
                        && upper.z > box.lower.z && box.upper.z > lower.z)
                    return true;
            } else if (boundsObjects[i].boundId == BOUNDING_SPHERE) {
                BoundingSphere sphere = (BoundingSphere) boundsObjects[i];
                double rad_sq = sphere.radius * sphere.radius;
                double dis = 0.0;

                if (sphere.center.x < lower.x)
                    dis = (sphere.center.x - lower.x) * (sphere.center.x - lower.x);
                else if (sphere.center.x > upper.x)
                    dis = (sphere.center.x - upper.x) * (sphere.center.x - upper.x);

                if (sphere.center.y < lower.y)
                    dis += (sphere.center.y - lower.y) * (sphere.center.y - lower.y);
                else if (sphere.center.y > upper.y)
                    dis += (sphere.center.y - upper.y) * (sphere.center.y - upper.y);

                if (sphere.center.z < lower.z)
                    dis += (sphere.center.z - lower.z) * (sphere.center.z - lower.z);
                else if (sphere.center.z > upper.z)
                    dis += (sphere.center.z - upper.z) * (sphere.center.z - upper.z);

                if (dis <= rad_sq)
                    return true;

            } else if (boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
                if (intersect_ptope_abox((BoundingPolytope) boundsObjects[i], this))
                    return true;
            } else {
                //          System.err.println("intersect ?? ");
            }
        }

        return false;
    }

    /**
     * Test for intersection with another bounding box.
     * @param boundsObject another bounding object
     * @param newBoundBox the new bounding box which is the intersection of
     *        the boundsObject and this BoundingBox
     * @return true or false indicating if an intersection occured
     */
    public boolean intersect(Bounds boundsObject, BoundingBox newBoundBox) {

        if ((boundsObject == null) || boundsIsEmpty || boundsObject.boundsIsEmpty) {
            newBoundBox.set((Bounds) null);
            return false;
        }

        if (boundsIsInfinite && (!boundsObject.boundsIsInfinite)) {
            newBoundBox.set(boundsObject);
            return true;
        } else if ((!boundsIsInfinite) && boundsObject.boundsIsInfinite) {
            newBoundBox.set(this);
            return true;
        } else if (boundsIsInfinite && boundsObject.boundsIsInfinite) {
            newBoundBox.set(this);
            return true;
        } else if (boundsObject.boundId == BOUNDING_BOX) {
            BoundingBox box = (BoundingBox) boundsObject;
            // both boxes are axis aligned
            if (upper.x > box.lower.x && box.upper.x > lower.x && upper.y > box.lower.y && box.upper.y > lower.y
                    && upper.z > box.lower.z && box.upper.z > lower.z) {

                if (upper.x > box.upper.x)
                    newBoundBox.upper.x = box.upper.x;
                else
                    newBoundBox.upper.x = upper.x;

                if (upper.y > box.upper.y)
                    newBoundBox.upper.y = box.upper.y;
                else
                    newBoundBox.upper.y = upper.y;

                if (upper.z > box.upper.z)
                    newBoundBox.upper.z = box.upper.z;
                else
                    newBoundBox.upper.z = upper.z;

                if (lower.x < box.lower.x)
                    newBoundBox.lower.x = box.lower.x;
                else
                    newBoundBox.lower.x = lower.x;

                if (lower.y < box.lower.y)
                    newBoundBox.lower.y = box.lower.y;
                else
                    newBoundBox.lower.y = lower.y;

                if (lower.z < box.lower.z)
                    newBoundBox.lower.z = box.lower.z;
                else
                    newBoundBox.lower.z = lower.z;

                newBoundBox.updateBoundsStates();
                return true;
            } else {
                // Negative volume.
                newBoundBox.set((Bounds) null);
                return false;
            }
        } else if (boundsObject.boundId == BOUNDING_SPHERE) {
            BoundingSphere sphere = (BoundingSphere) boundsObject;
            if (this.intersect(sphere)) {
                BoundingBox sbox = new BoundingBox(sphere);
                this.intersect(sbox, newBoundBox);
                return true;
            } else {
                // Negative volume.
                newBoundBox.set((Bounds) null);
                return false;
            }

            //      System.err.println("intersect Sphere ");
        } else if (boundsObject.boundId == BOUNDING_POLYTOPE) {
            BoundingPolytope polytope = (BoundingPolytope) boundsObject;
            if (this.intersect(polytope)) {
                BoundingBox pbox = new BoundingBox(polytope); // convert polytope to box
                this.intersect(pbox, newBoundBox);
                return true;
            } else {
                // Negative volume.
                newBoundBox.set((Bounds) null);
                return false;
            }
        } else {
            throw new IllegalArgumentException(J3dI18N.getString("BoundingBox7"));
        }
    }

    /**
     * Test for intersection with an array of  bounds objects.
     * @param boundsObjects an array of  bounds objects
     * @param newBoundBox the new bounding box which is the intersection of
     *         the boundsObject and this BoundingBox
     * @return true or false indicating if an intersection occured
     */
    public boolean intersect(Bounds[] boundsObjects, BoundingBox newBoundBox) {

        if (boundsObjects == null || boundsObjects.length <= 0 || boundsIsEmpty) {
            // Negative volume.
            newBoundBox.set((Bounds) 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
            // Negative volume.
            newBoundBox.set((Bounds) null);
            return false;
        }

        boolean status = false;
        BoundingBox tbox = new BoundingBox();

        for (; i < boundsObjects.length; i++) {
            if (boundsObjects[i] == null || boundsObjects[i].boundsIsEmpty)
                ;
            else if (boundsObjects[i].boundId == BOUNDING_BOX) {
                BoundingBox box = (BoundingBox) boundsObjects[i];
                // both boxes are axis aligned
                if (upper.x > box.lower.x && box.upper.x > lower.x && upper.y > box.lower.y && box.upper.y > lower.y
                        && upper.z > box.lower.z && box.upper.z > lower.z) {

                    if (upper.x > box.upper.x)
                        newBoundBox.upper.x = box.upper.x;
                    else
                        newBoundBox.upper.x = upper.x;

                    if (upper.y > box.upper.y)
                        newBoundBox.upper.y = box.upper.y;
                    else
                        newBoundBox.upper.y = upper.y;

                    if (upper.z > box.upper.z)
                        newBoundBox.upper.z = box.upper.z;
                    else
                        newBoundBox.upper.z = upper.z;

                    if (lower.x < box.lower.x)
                        newBoundBox.lower.x = box.lower.x;
                    else
                        newBoundBox.lower.x = lower.x;

                    if (lower.y < box.lower.y)
                        newBoundBox.lower.y = box.lower.y;
                    else
                        newBoundBox.lower.y = lower.y;

                    if (lower.z < box.lower.z)
                        newBoundBox.lower.z = box.lower.z;
                    else
                        newBoundBox.lower.z = lower.z;
                    status = true;
                    newBoundBox.updateBoundsStates();
                }
            } else if (boundsObjects[i].boundId == BOUNDING_SPHERE) {
                BoundingSphere sphere = (BoundingSphere) boundsObjects[i];
                if (this.intersect(sphere)) {
                    BoundingBox sbox = new BoundingBox(sphere); // convert sphere to box
                    this.intersect(sbox, tbox); // insersect two boxes
                    if (status) {
                        newBoundBox.combine(tbox);
                    } else {
                        newBoundBox.set(tbox);
                        status = true;
                    }
                }

            } else if (boundsObjects[i].boundId == BOUNDING_POLYTOPE) {
                BoundingPolytope polytope = (BoundingPolytope) boundsObjects[i];
                if (this.intersect(polytope)) {
                    BoundingBox pbox = new BoundingBox(polytope); // convert polytope to box
                    this.intersect(pbox, tbox); // insersect two boxes
                    if (status) {
                        newBoundBox.combine(tbox);
                    } else {
                        newBoundBox.set(tbox);
                        status = true;
                    }
                }
            } else {
                throw new IllegalArgumentException(J3dI18N.getString("BoundingBox6"));
            }

            if (newBoundBox.boundsIsInfinite)
                break; // We're done.
        }
        if (status == false) {
            // Negative volume.
            newBoundBox.set((Bounds) null);
        }
        return status;
    }

    /**
     * Finds closest bounding object that intersects this bounding box.
     * @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;
        }

        Point3d centroid = getCenter();

        double dis;
        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(
                            (centroid.x - cenX) * (centroid.x - cenX) + (centroid.y - cenY) * (centroid.y - cenY)
                                    + (centroid.z - cenZ) * (centroid.z - cenZ));
                    inside = false;

                    if (lower.x <= box.lower.x && lower.y <= box.lower.y && lower.z <= box.lower.z
                            && upper.x >= box.upper.x && upper.y >= box.upper.y && upper.z >= box.upper.z) { // box is contained
                        inside = true;
                    }
                    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 if (boundsObjects[i].boundId == BOUNDING_SPHERE) {
                    BoundingSphere sphere = (BoundingSphere) boundsObjects[i];
                    dis = Math.sqrt((centroid.x - sphere.center.x) * (centroid.x - sphere.center.x)
                            + (centroid.y - sphere.center.y) * (centroid.y - sphere.center.y)
                            + (centroid.z - sphere.center.z) * (centroid.z - sphere.center.z));

                    inside = false;

                    // sphere sphere.center is inside box
                    if (sphere.center.x <= upper.x && sphere.center.x >= lower.x && sphere.center.y <= upper.y
                            && sphere.center.y >= lower.y && sphere.center.z <= upper.z
                            && sphere.center.z >= lower.z) {
                        // check if sphere intersects any side
                        if (sphere.center.x - lower.x >= sphere.radius && upper.x - sphere.center.x >= sphere.radius
                                && sphere.center.y - lower.y >= sphere.radius
                                && upper.y - sphere.center.y >= sphere.radius
                                && sphere.center.z - lower.z >= sphere.radius
                                && upper.z - sphere.center.z >= sphere.radius) {
                            // contains the sphere
                            inside = true;
                        }
                    }
                    if (inside) {
                        // initialize smallest_distance for the first containment
                        if (!contains) {
                            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((centroid.x - polytope.centroid.x) * (centroid.x - polytope.centroid.x)
                            + (centroid.y - polytope.centroid.y) * (centroid.y - polytope.centroid.y)
                            + (centroid.z - polytope.centroid.z) * (centroid.z - polytope.centroid.z));
                    inside = true;
                    for (j = 0; j < polytope.nVerts; j++) {
                        if (polytope.verts[j].x < lower.x || polytope.verts[j].y < lower.y
                                || polytope.verts[j].z < lower.z || polytope.verts[j].x > upper.x
                                || polytope.verts[j].y > upper.y || polytope.verts[j].z > upper.z) { // box contains polytope
                            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("BoundingBox9"));
                }
            }
        }

        if (intersect)
            return boundsObjects[index];
        else
            return null;
    }

    /**
     * Tests for intersection of box and frustum.
     * @param frustum
     * @return true if they intersect
     */
    boolean intersect(CachedFrustum frustum) {

        if (boundsIsEmpty)
            return false;

        if (boundsIsInfinite)
            return true;

        // System.err.println("intersect frustum with box="+this.toString());
        // System.err.println("frustum "+frustum.toString());
        // check if box and bounding box  of frustum intersect
        if ((upper.x < frustum.lower.x) || (lower.x > frustum.upper.x) || (upper.y < frustum.lower.y)
                || (lower.y > frustum.upper.y) || (upper.z < frustum.lower.z) || (lower.z > frustum.upper.z)) {

            // System.err.println("*** box and bounding box of frustum do not intersect");
            return false;
        }

        // check if all box points out any frustum plane
        int i = 5;
        while (i >= 0) {
            Vector4d vc = frustum.clipPlanes[i--];
            if (((upper.x * vc.x + upper.y * vc.y + upper.z * vc.z + vc.w) < 0.0)
                    && ((upper.x * vc.x + lower.y * vc.y + upper.z * vc.z + vc.w) < 0.0)
                    && ((upper.x * vc.x + lower.y * vc.y + lower.z * vc.z + vc.w) < 0.0)
                    && ((upper.x * vc.x + upper.y * vc.y + lower.z * vc.z + vc.w) < 0.0)
                    && ((lower.x * vc.x + upper.y * vc.y + upper.z * vc.z + vc.w) < 0.0)
                    && ((lower.x * vc.x + lower.y * vc.y + upper.z * vc.z + vc.w) < 0.0)
                    && ((lower.x * vc.x + lower.y * vc.y + lower.z * vc.z + vc.w) < 0.0)
                    && ((lower.x * vc.x + upper.y * vc.y + lower.z * vc.z + vc.w) < 0.0)) {
                // all corners outside this frustum plane
                // System.err.println("*** all corners outside this frustum plane");
                return false;
            }
        }

        return true;
    }

    /**
     * Returns a string representation of this class.
     */
    @Override
    public String toString() {
        return new String("Bounding box: Lower=" + lower.x + " " + lower.y + " " + lower.z + " Upper=" + upper.x
                + " " + upper.y + " " + upper.z);
    }

    private void setEmptyBounds() {
        lower.set(1.0d, 1.0d, 1.0d);
        upper.set(-1.0d, -1.0d, -1.0d);
        boundsIsInfinite = false;
        boundsIsEmpty = true;
    }

    private void setInfiniteBounds() {
        lower.set(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
        upper.set(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);

        boundsIsInfinite = true;
        boundsIsEmpty = false;
    }

    private void updateBoundsStates() {
        if ((lower.x == Double.NEGATIVE_INFINITY) && (lower.y == Double.NEGATIVE_INFINITY)
                && (lower.z == Double.NEGATIVE_INFINITY) && (upper.x == Double.POSITIVE_INFINITY)
                && (upper.y == Double.POSITIVE_INFINITY) && (upper.z == Double.POSITIVE_INFINITY)) {
            boundsIsEmpty = false;
            boundsIsInfinite = true;
            return;
        }

        if (Double.isNaN(lower.x + lower.y + lower.z + upper.x + upper.y + upper.z)) {
            boundsIsEmpty = true;
            boundsIsInfinite = false;
            return;
        } else {
            boundsIsInfinite = false;
            if (lower.x > upper.x || lower.y > upper.y || lower.z > upper.z) {
                boundsIsEmpty = true;
            } else {
                boundsIsEmpty = false;
            }
        }
    }

    // For a infinite bounds. What is the centroid ?
    @Override
    Point3d getCenter() {
        Point3d cent = new Point3d();
        cent.add(upper, lower);
        cent.scale(0.5d);
        return cent;
    }

    @Override
    public void getCenter(Point3d center) {
        center.add(lower, upper);
        center.scale(0.5d);
    }

    void translate(BoundingBox bbox, Vector3d value) {
        if (bbox == null || bbox.boundsIsEmpty) {
            setEmptyBounds();
            return;
        }

        if (bbox.boundsIsInfinite) {
            setInfiniteBounds();
            return;
        }

        lower.x = bbox.lower.x + value.x;
        lower.y = bbox.lower.y + value.y;
        lower.z = bbox.lower.z + value.z;
        upper.x = bbox.upper.x + value.x;
        upper.y = bbox.upper.y + value.y;
        upper.z = bbox.upper.z + value.z;
    }

    /**
     * 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) {
            BoundingBox region = (BoundingBox) r;
            region.lower.x = lower.x;
            region.lower.y = lower.y;
            region.lower.z = lower.z;
            region.upper.x = upper.x;
            region.upper.y = upper.y;
            region.upper.z = upper.z;
            region.boundsIsEmpty = boundsIsEmpty;
            region.boundsIsInfinite = boundsIsInfinite;
            return region;
        } else {
            return (Bounds) this.clone();
        }
    }

    @Override
    int getPickType() {
        return PickShape.PICKBOUNDINGBOX;
    }
}