javax.media.j3d.Bounds.java Source code

Java tutorial

Introduction

Here is the source code for javax.media.j3d.Bounds.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.Matrix3d;
import javax.vecmath.Point3d;
import javax.vecmath.Point4d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector4d;

/**
 * The abstract base class for bounds objects.  Bounds objects define
 * a convex, closed volume that is used for various intersection and
 * culling operations.
 */

public abstract class Bounds extends Object implements Cloneable {
    static final double EPSILON = .000001;
    static final boolean debug = false;

    static final int BOUNDING_BOX = 0x1;
    static final int BOUNDING_SPHERE = 0x2;
    static final int BOUNDING_POLYTOPE = 0x4;

    boolean boundsIsEmpty = false;
    boolean boundsIsInfinite = false;
    int boundId = 0;

    /**
     * Constructs a new Bounds object.
     */
    public Bounds() {
    }

    /**
     * Makes a copy of a bounds object.
     */
    @Override
    public abstract Object clone();

    /**
     * Indicates whether the specified <code>bounds</code> object is
     * equal to this Bounds object.  They are equal if both the
     * specified <code>bounds</code> object and this Bounds are
     * instances of the same Bounds subclass and all of the data
     * members of <code>bounds</code> are equal to the corresponding
     * data members in this Bounds.
     * @param bounds the object with which the comparison is made.
     * @return true if this Bounds object is equal to <code>bounds</code>;
     * otherwise false
     *
     * @since Java 3D 1.2
     */
    @Override
    public abstract boolean equals(Object bounds);

    /**
     * Returns a hash code for this Bounds object based on the
     * data values in this object.  Two different Bounds objects of
     * the same type with identical data values (i.e., Bounds.equals
     * returns true) will return the same hash code.  Two Bounds
     * objects with different data members may return the same hash code
     * value, although this is not likely.
     * @return a hash code for this Bounds object.
     *
     * @since Java 3D 1.2
     */
    @Override
    public abstract int hashCode();

    /**
     * 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
     */
    public abstract boolean intersect(Point3d origin, Vector3d direction);

    /**
     * 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
     */
    public abstract boolean intersect(Point3d point);

    /**
     * Test for intersection with a ray
     * @param origin is a the starting point of the ray
     * @param direction is the direction of the ray
     * @param position is a point defining the location  of the pick w= distance to pick
     * @return true or false indicating if an intersection occured
     */
    abstract boolean intersect(Point3d origin, Vector3d direction, Point4d position);

    /**
     * Test for intersection with a point
     * @param point is a point defining a position in 3-space
     * @param position is a point defining the location  of the pick w= distance to pick
     * @return true or false indicating if an intersection occured
     */
    abstract boolean intersect(Point3d point, Point4d position);

    /**
     * Test for intersection with a segment
     * @param start is a point defining  the start of the line segment
     * @param end is a point defining the end of the line segment
     * @param position is a point defining the location  of the pick w= distance to pick
     * @return true or false indicating if an intersection occured
     */
    abstract boolean intersect(Point3d start, Point3d end, Point4d position);

    /**
      * Test for intersection with another bounds object
      *
      * Test for intersection with another bounds object
      * @param boundsObject is another bounds object
      * @return true or false indicating if an intersection occured
      */
    abstract boolean intersect(Bounds boundsObject, Point4d position);

    /**
     * Test for intersection with another bounds object.
     * @param boundsObject another bounds object
     * @return true or false indicating if an intersection occurred
     */
    public abstract boolean intersect(Bounds boundsObject);

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

    /**
    * Finds closest bounding object that intersects this bounding object.
    * @param boundsObjects an array of  bounds objects
    * @return closest bounding object
    */
    public abstract Bounds closestIntersection(Bounds[] boundsObjects);

    /**
     * Returns the center of the bounds
     * @return bounds center
     */
    abstract Point3d getCenter();

    /**
     * Gets the centroid of this bounding region.
     * @param center a Point to receive the centroid of the bounding region
     */
    public abstract void getCenter(Point3d center);

    /**
     * Combines this bounding object with a bounding object so that the
     * resulting bounding object encloses the original bounding object and the
     * given bounds object.
     * @param boundsObject another bounds object
     */
    public abstract void combine(Bounds boundsObject);

    /**
     * Combines this bounding object with an array of bounding objects so that the
     * resulting bounding object encloses the original bounding object and the
     * given array of bounds object.
     * @param boundsObjects an array of bounds objects
     */
    public abstract void combine(Bounds[] boundsObjects);

    /**
     * Combines this bounding object with a point.
     * @param point a 3d point in space
     */
    public abstract void combine(Point3d point);

    /**
     * Combines this bounding object with an array of points.
     * @param points an array of 3d points in space
     */
    public abstract void combine(Point3d[] points);

    /**
     * Transforms this bounding object by the given matrix.
     * @param trans the transformation matrix
     */
    public abstract void transform(Transform3D trans);

    /**
     * Modifies the bounding object so that it bounds the volume
     * generated by transforming the given bounding object.
     * @param bounds the bounding object to be transformed
     * @param trans the transformation matrix
     */
    public abstract void transform(Bounds bounds, Transform3D trans);

    /**
     * Tests whether the bounds is empty.  A bounds is
     * empty if it is null (either by construction or as the result of
     * a null intersection) or if its volume is negative.  A bounds
     * with a volume of zero is <i>not</i> empty.
     * @return true if the bounds is empty; otherwise, it returns false
     */
    public abstract boolean isEmpty();

    /**
     * Sets the value of this Bounds object.
     * @param boundsObject another bounds object.
     */
    public abstract void set(Bounds boundsObject);

    abstract Bounds copy(Bounds region);

    private void test_point(Vector4d[] planes, Point3d new_point) {
        for (int i = 0; i < planes.length; i++) {
            double dist = (new_point.x * planes[i].x + new_point.y * planes[i].y + new_point.z * planes[i].z
                    + planes[i].w);
            if (dist > EPSILON) {
                System.err.println("new point is outside of" + " plane[" + i + "] dist = " + dist);
            }
        }
    }

    /**
     * computes the closest point from the given point to a set of planes
     * (polytope)
     * @param g the point
     * @param planes array of bounding planes
     * @param new_point point on planes closest g
     */
    boolean closest_point(Point3d g, Vector4d[] planes, Point3d new_point) {

        double t, s, dist, w;
        boolean converged, inside, firstPoint, firstInside;
        int i, count;
        double ab, ac, bc, ad, bd, cd, aa, bb, cc;
        double b1, b2, b3, d1, d2, d3, y1, y2, y3;
        double h11, h12, h13, h22, h23, h33;
        double l12, l13, l23;
        Point3d n = new Point3d();
        Point3d p = new Point3d();
        Vector3d delta = null;

        // These are temporary until the solve code is working

        /*
         * The algorithm:
         * We want to find the point "n", closest to "g", while still within
         * the the polytope defined by "planes".  We find the solution by
         * minimizing the value for a "penalty function";
         *
         * f = distance(n,g)^2 + sum for each i: w(distance(n, planes[i]))
         *
         * Where "w" is a weighting which indicates how much more important
         * it is to be close to the planes than it is to be close to "g".
         *
         * We minimize this function by taking it's derivitive, and then
         * solving for the value of n when the derivitive equals 0.
         *
         * For the 1D case with a single plane (a,b,c,d),  x = n.x and g = g.x,
         * this looks like:
         *
         *    f(x) = (x - g) ^ 2 + w(ax + d)^2
         *    f'(x) = 2x -2g + 2waax + 2wad
         *
         * (note aa = a^2) setting f'(x) = 0 gives:
         *
         *    (1 + waa)x = g - wad
         *
         * Note that the solution is just outside the plane [a, d]. With the
         * correct choice of w, this should be inside of the EPSILON tolerance
         * outside the planes.
         *
         * Extending to 3D gives the matrix solution:
         *
         *      | (1 + waa)  wab        wac       |
         *  H = | wab        (1 + wbb)  wbc     |
         *      | wac        wbc        (1 + wcc) |
         *
         *  b = [g.x - wad, g.y - wbd, g.z - wcd]
         *
         *  H * n = b
         *
         *  n = b * H.inverse()
         *
         *  The implementation speeds this process up by recognizing that
         *  H is symmetric, so that it can be decomposed into three matrices:
         *
         *  H = L * D * L.transpose()
         *
         *      1.0 0.0 0.0       d1  0.0 0.0
         *  L = l12 1.0 0.0  D =  0.0 d2  0.0
         *      l13 l23 1.0       0.0 0.0 d3
         *
         *  n can then be derived by back-substitution, where the original
         *  problem is decomposed as:
         *
         *  H * n = b
         *  L * D * L.transpose() * n = b
         *  L * D * y = b;   L.transpose() * n = y
         *
         *  We can then multiply out the terms of L * D and solve for y, and
         *  then use y to solve for n.
         */

        w = 100.0 / EPSILON; // must be large enough to ensure that solution
        // is within EPSILON of planes

        count = 0;
        p.set(g);

        if (debug) {
            System.err.println("closest_point():\nincoming g=" + " " + g.x + " " + g.y + " " + g.z);
        }

        converged = false;
        firstPoint = true;
        firstInside = false;

        Vector4d pln;

        while (!converged) {
            if (debug) {
                System.err.println("start: p=" + " " + p.x + " " + p.y + " " + p.z);
            }

            // test the current point against the planes, for each
            // plane that is violated, add it's contribution to the
            // penalty function
            inside = true;
            aa = 0.0;
            bb = 0.0;
            cc = 0.0;
            ab = 0.0;
            ac = 0.0;
            bc = 0.0;
            ad = 0.0;
            bd = 0.0;
            cd = 0.0;
            for (i = 0; i < planes.length; i++) {
                pln = planes[i];
                dist = (p.x * pln.x + p.y * pln.y + p.z * pln.z + pln.w);
                // if point is outside or within EPSILON of the boundary, add
                // the plane to the penalty matrix.  We do this even if the
                // point is already inside the polytope to prevent numerical
                // instablity in cases where the point is just outside the
                // boundary of several planes of the polytope
                if (dist > -EPSILON) {
                    aa = aa + pln.x * pln.x;
                    bb = bb + pln.y * pln.y;
                    cc = cc + pln.z * pln.z;
                    ab = ab + pln.x * pln.y;
                    ac = ac + pln.x * pln.z;
                    bc = bc + pln.y * pln.z;
                    ad = ad + pln.x * pln.w;
                    bd = bd + pln.y * pln.w;
                    cd = cd + pln.z * pln.w;
                }
                // If the point is inside if dist is <= EPSILON
                if (dist > EPSILON) {
                    inside = false;
                    if (debug) {
                        System.err.println("point outside plane[" + i + "]=(" + pln.x + "," + pln.y + ",\n\t"
                                + pln.z + "," + pln.w + ")\ndist = " + dist);
                    }
                }
            }
            // see if we are done
            if (inside) {
                if (debug) {
                    System.err.println("p is inside");
                }
                if (firstPoint) {
                    firstInside = true;
                }
                new_point.set(p);
                converged = true;
            } else { // solve for a closer point
                firstPoint = false;

                // this is the upper right corner of H, which is all we
                // need to do the decomposition since the matrix is symetric
                h11 = 1.0 + aa * w;
                h12 = ab * w;
                h13 = ac * w;
                h22 = 1.0 + bb * w;
                h23 = bc * w;
                h33 = 1.0 + cc * w;

                if (debug) {
                    System.err.println(" hessin= ");
                    System.err.println(h11 + " " + h12 + " " + h13);
                    System.err.println("     " + h22 + " " + h23);
                    System.err.println("           " + h33);
                }

                // these are the constant terms
                b1 = g.x - w * ad;
                b2 = g.y - w * bd;
                b3 = g.z - w * cd;

                if (debug) {
                    System.err.println(" b1,b2,b3 = " + b1 + " " + b2 + " " + b3);
                }

                // solve, d1, d2, d3 actually 1/dx, which is more useful
                d1 = 1 / h11;
                l12 = d1 * h12;
                l13 = d1 * h13;
                s = h22 - l12 * h12;
                d2 = 1 / s;
                t = h23 - h12 * l13;
                l23 = d2 * t;
                d3 = 1 / (h33 - h13 * l13 - t * l23);

                if (debug) {
                    System.err.println(" l12,l13,l23 " + l12 + " " + l13 + " " + l23);
                    System.err.println(" d1,d2,d3 " + d1 + " " + d2 + " " + d3);
                }

                // we have L and D, now solve for y
                y1 = d1 * b1;
                y2 = d2 * (b2 - h12 * y1);
                y3 = d3 * (b3 - h13 * y1 - t * y2);

                if (debug) {
                    System.err.println(" y1,y2,y3 = " + y1 + " " + y2 + " " + y3);
                }

                // we have y, solve for n
                n.z = y3;
                n.y = (y2 - l23 * n.z);
                n.x = (y1 - l13 * n.z - l12 * n.y);

                if (debug) {
                    System.err.println("new point = " + n.x + " " + n.y + " " + n.z);
                    test_point(planes, n);

                    if (delta == null)
                        delta = new Vector3d();
                    delta.sub(n, p);
                    delta.normalize();
                    System.err.println("p->n direction: " + delta);
                    Matrix3d hMatrix = new Matrix3d();
                    // check using the the javax.vecmath routine
                    hMatrix.m00 = h11;
                    hMatrix.m01 = h12;
                    hMatrix.m02 = h13;
                    hMatrix.m10 = h12; // h21 = h12
                    hMatrix.m11 = h22;
                    hMatrix.m12 = h23;
                    hMatrix.m20 = h13; // h31 = h13
                    hMatrix.m21 = h23; // h32 = h22
                    hMatrix.m22 = h33;
                    hMatrix.invert();
                    Point3d check = new Point3d(b1, b2, b3);
                    hMatrix.transform(check);

                    System.err.println("check point = " + check.x + " " + check.y + " " + check.z);
                }

                // see if we have converged yet
                dist = (p.x - n.x) * (p.x - n.x) + (p.y - n.y) * (p.y - n.y) + (p.z - n.z) * (p.z - n.z);

                if (debug) {
                    System.err.println("p->n distance =" + dist);
                }

                if (dist < EPSILON) { // close enough
                    converged = true;
                    new_point.set(n);
                } else {
                    p.set(n);
                    count++;
                    if (count > 4) { // watch for cycling between two minimums
                        new_point.set(n);
                        converged = true;
                    }
                }
            }
        }
        if (debug) {
            System.err.println("returning pnt (" + new_point.x + " " + new_point.y + " " + new_point.z + ")");

            if (firstInside)
                System.err.println("input point inside polytope ");
        }
        return firstInside;
    }

    boolean intersect_ptope_sphere(BoundingPolytope polyTope, BoundingSphere sphere) {
        Point3d p = new Point3d();
        boolean inside;

        if (debug) {
            System.err.println("ptope_sphere intersect sphere =" + sphere);
        }
        inside = closest_point(sphere.center, polyTope.planes, p);
        if (debug) {
            System.err.println("ptope sphere intersect point =" + p);
        }
        if (!inside) {
            // if distance between polytope and sphere center is greater than
            // radius then no intersection
            if (p.distanceSquared(sphere.center) > sphere.radius * sphere.radius) {
                if (debug) {
                    System.err.println("ptope_sphere returns false");
                }
                return false;
            } else {
                if (debug) {
                    System.err.println("ptope_sphere returns true");
                }
                return true;
            }
        } else {
            if (debug) {
                System.err.println("ptope_sphere returns true");
            }
            return true;
        }
    }

    boolean intersect_ptope_abox(BoundingPolytope polyTope, BoundingBox box) {
        Vector4d planes[] = new Vector4d[6];

        if (debug) {
            System.err.println("ptope_abox, box = " + box);
        }
        planes[0] = new Vector4d(-1.0, 0.0, 0.0, box.lower.x);
        planes[1] = new Vector4d(1.0, 0.0, 0.0, -box.upper.x);
        planes[2] = new Vector4d(0.0, -1.0, 0.0, box.lower.y);
        planes[3] = new Vector4d(0.0, 1.0, 0.0, -box.upper.y);
        planes[4] = new Vector4d(0.0, 0.0, -1.0, box.lower.z);
        planes[5] = new Vector4d(0.0, 0.0, 1.0, -box.upper.z);

        BoundingPolytope pbox = new BoundingPolytope(planes);

        boolean result = intersect_ptope_ptope(polyTope, pbox);
        if (debug) {
            System.err.println("ptope_abox returns " + result);
        }
        return (result);
    }

    boolean intersect_ptope_ptope(BoundingPolytope poly1, BoundingPolytope poly2) {
        boolean intersect;
        Point3d p = new Point3d();
        Point3d g = new Point3d();
        Point3d gnew = new Point3d();
        Point3d pnew = new Point3d();

        intersect = false;

        p.x = 0.0;
        p.y = 0.0;
        p.z = 0.0;

        //  start from an arbitrary point on poly1
        closest_point(p, poly1.planes, g);

        // get the closest points on each polytope
        if (debug) {
            System.err.println("ptope_ptope: first g = " + g);
        }
        intersect = closest_point(g, poly2.planes, p);

        if (intersect) {
            return true;
        }

        if (debug) {
            System.err.println("first p = " + p + "\n");
        }
        intersect = closest_point(p, poly1.planes, gnew);
        if (debug) {
            System.err.println("gnew = " + gnew + " intersect=" + intersect);
        }

        // loop until the closest points on the two polytopes are not changing

        double prevDist = p.distanceSquared(g);
        double dist;

        while (!intersect) {

            dist = p.distanceSquared(gnew);

            if (dist < prevDist) {
                g.set(gnew);
                intersect = closest_point(g, poly2.planes, pnew);
                if (debug) {
                    System.err.println("pnew = " + pnew + " intersect=" + intersect);
                }
            } else {
                g.set(gnew);
                break;
            }
            prevDist = dist;
            dist = pnew.distanceSquared(g);

            if (dist < prevDist) {
                p.set(pnew);
                if (!intersect) {
                    intersect = closest_point(p, poly1.planes, gnew);
                    if (debug) {
                        System.err.println("gnew = " + gnew + " intersect=" + intersect);
                    }
                }
            } else {
                p.set(pnew);
                break;
            }
            prevDist = dist;
        }

        if (debug) {
            System.err.println("gnew=" + " " + gnew.x + " " + gnew.y + " " + gnew.z);
            System.err.println("pnew=" + " " + pnew.x + " " + pnew.y + " " + pnew.z);
        }
        return intersect;
    }

    synchronized void setWithLock(Bounds b) {
        this.set(b);
    }

    synchronized void getWithLock(Bounds b) {
        b.set(this);
    }

    // Return one of Pick Bounds type define in PickShape
    abstract int getPickType();
}