Package com.ardor3d.bounding

Source Code of com.ardor3d.bounding.BoundingBox

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.bounding;

import java.io.IOException;
import java.nio.FloatBuffer;

import com.ardor3d.intersection.IntersectionRecord;
import com.ardor3d.math.MathUtils;
import com.ardor3d.math.Matrix3;
import com.ardor3d.math.Plane;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.type.ReadOnlyMatrix3;
import com.ardor3d.math.type.ReadOnlyPlane;
import com.ardor3d.math.type.ReadOnlyPlane.Side;
import com.ardor3d.math.type.ReadOnlyRay3;
import com.ardor3d.math.type.ReadOnlyTransform;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.scenegraph.MeshData;
import com.ardor3d.util.export.InputCapsule;
import com.ardor3d.util.export.OutputCapsule;
import com.ardor3d.util.geom.BufferUtils;

/**
* <code>BoundingBox</code> defines an axis-aligned cube that defines a container for a group of vertices of a
* particular piece of geometry. This box defines a center and extents from that center along the x, y and z axis. <br>
* <br>
* A typical usage is to allow the class define the center and radius by calling either <code>containAABB</code> or
* <code>averagePoints</code>. A call to <code>computeFramePoint</code> in turn calls <code>containAABB</code>.
*/
public class BoundingBox extends BoundingVolume {

    private static final long serialVersionUID = 1L;

    private double _xExtent, _yExtent, _zExtent;

    /**
     * Default constructor instantiates a new <code>BoundingBox</code> object.
     */
    public BoundingBox() {}

    /**
     * Constructor instantiates a new <code>BoundingBox</code> object with given values.
     */
    public BoundingBox(final BoundingBox other) {
        this(other.getCenter(), other.getXExtent(), other.getYExtent(), other.getZExtent());
    }

    /**
     * Constructor instantiates a new <code>BoundingBox</code> object with given values.
     */
    public BoundingBox(final ReadOnlyVector3 c, final double x, final double y, final double z) {
        _center.set(c);
        setXExtent(x);
        setYExtent(y);
        setZExtent(z);
    }

    @Override
    public boolean equals(final Object other) {
        if (other == this) {
            return true;
        }
        if (!(other instanceof BoundingBox)) {
            return false;
        }
        final BoundingBox b = (BoundingBox) other;
        return _center.equals(b._center) && _xExtent == b._xExtent && _yExtent == b._yExtent && _zExtent == b._zExtent;
    }

    @Override
    public Type getType() {
        return Type.AABB;
    }

    public void setXExtent(final double xExtent) {
        _xExtent = xExtent;
    }

    public double getXExtent() {
        return _xExtent;
    }

    public void setYExtent(final double yExtent) {
        _yExtent = yExtent;
    }

    public double getYExtent() {
        return _yExtent;
    }

    public void setZExtent(final double zExtent) {
        _zExtent = zExtent;
    }

    public double getZExtent() {
        return _zExtent;
    }

    @Override
    public double getRadius() {
        return MathUtils.sqrt(_xExtent * _xExtent + _yExtent * _yExtent + _zExtent * _zExtent);
    }

    // Some transform matrices are not in decomposed form and in this
    // situation we need to use a different, more robust, algorithm
    // for computing the new bounding box.
    @Override
    public BoundingVolume transform(final ReadOnlyTransform transform, final BoundingVolume store) {

        if (transform.isRotationMatrix()) {
            return transformRotational(transform, store);
        }

        BoundingBox box;
        if (store == null || store.getType() != Type.AABB) {
            box = new BoundingBox();
        } else {
            box = (BoundingBox) store;
        }

        final Vector3[] corners = new Vector3[8];
        for (int i = 0; i < corners.length; i++) {
            corners[i] = Vector3.fetchTempInstance();
        }
        getCorners(corners);

        // Transform all of these points by the transform
        for (int i = 0; i < corners.length; i++) {
            transform.applyForward(corners[i]);
        }
        // Now compute based on these transformed points
        double minX = corners[0].getX();
        double minY = corners[0].getY();
        double minZ = corners[0].getZ();
        double maxX = minX;
        double maxY = minY;
        double maxZ = minZ;
        for (int i = 1; i < corners.length; i++) {
            final double curX = corners[i].getX();
            final double curY = corners[i].getY();
            final double curZ = corners[i].getZ();
            minX = Math.min(minX, curX);
            minY = Math.min(minY, curY);
            minZ = Math.min(minZ, curZ);
            maxX = Math.max(maxX, curX);
            maxY = Math.max(maxY, curY);
            maxZ = Math.max(maxZ, curZ);
        }

        final double ctrX = (maxX + minX) * 0.5;
        final double ctrY = (maxY + minY) * 0.5;
        final double ctrZ = (maxZ + minZ) * 0.5;

        box._center.set(ctrX, ctrY, ctrZ);
        box._xExtent = maxX - ctrX;
        box._yExtent = maxY - ctrY;
        box._zExtent = maxZ - ctrZ;

        for (int i = 0; i < corners.length; i++) {
            Vector3.releaseTempInstance(corners[i]);
        }

        return box;
    }

    public BoundingVolume transformRotational(final ReadOnlyTransform transform, final BoundingVolume store) {

        final ReadOnlyMatrix3 rotate = transform.getMatrix();
        final ReadOnlyVector3 scale = transform.getScale();
        final ReadOnlyVector3 translate = transform.getTranslation();

        BoundingBox box;
        if (store == null || store.getType() != Type.AABB) {
            box = new BoundingBox();
        } else {
            box = (BoundingBox) store;
        }

        _center.multiply(scale, box._center);
        rotate.applyPost(box._center, box._center);
        box._center.addLocal(translate);

        final Matrix3 transMatrix = Matrix3.fetchTempInstance();
        transMatrix.set(rotate);
        // Make the rotation matrix all positive to get the maximum x/y/z extent
        transMatrix.setValue(0, 0, Math.abs(transMatrix.getM00()));
        transMatrix.setValue(0, 1, Math.abs(transMatrix.getM01()));
        transMatrix.setValue(0, 2, Math.abs(transMatrix.getM02()));
        transMatrix.setValue(1, 0, Math.abs(transMatrix.getM10()));
        transMatrix.setValue(1, 1, Math.abs(transMatrix.getM11()));
        transMatrix.setValue(1, 2, Math.abs(transMatrix.getM12()));
        transMatrix.setValue(2, 0, Math.abs(transMatrix.getM20()));
        transMatrix.setValue(2, 1, Math.abs(transMatrix.getM21()));
        transMatrix.setValue(2, 2, Math.abs(transMatrix.getM22()));

        _compVect1.set(getXExtent() * scale.getX(), getYExtent() * scale.getY(), getZExtent() * scale.getZ());
        transMatrix.applyPost(_compVect1, _compVect1);
        // Assign the biggest rotations after scales.
        box.setXExtent(Math.abs(_compVect1.getX()));
        box.setYExtent(Math.abs(_compVect1.getY()));
        box.setZExtent(Math.abs(_compVect1.getZ()));

        Matrix3.releaseTempInstance(transMatrix);

        return box;
    }

    /**
     * <code>computeFromPoints</code> creates a new Bounding Box from a given set of points. It uses the
     * <code>containAABB</code> method as default.
     *
     * @param points
     *            the points to contain.
     */
    @Override
    public void computeFromPoints(final FloatBuffer points) {
        containAABB(points);
    }

    @Override
    public void computeFromPrimitives(final MeshData data, final int section, final int[] indices, final int start,
            final int end) {
        if (end - start <= 0) {
            return;
        }

        final Vector3 min = _compVect1
                .set(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
        final Vector3 max = _compVect2
                .set(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);

        final int vertsPerPrimitive = data.getIndexMode(section).getVertexCount();
        Vector3[] store = new Vector3[vertsPerPrimitive];

        for (int i = start; i < end; i++) {
            store = data.getPrimitiveVertices(indices[i], section, store);
            for (int j = 0; j < store.length; j++) {
                checkMinMax(min, max, store[j]);
            }
        }

        _center.set(min.addLocal(max));
        _center.multiplyLocal(0.5);

        setXExtent(max.getX() - _center.getX());
        setYExtent(max.getY() - _center.getY());
        setZExtent(max.getZ() - _center.getZ());
    }

    private void checkMinMax(final Vector3 min, final Vector3 max, final ReadOnlyVector3 point) {
        if (point.getX() < min.getX()) {
            min.setX(point.getX());
        }
        if (point.getX() > max.getX()) {
            max.setX(point.getX());
        }

        if (point.getY() < min.getY()) {
            min.setY(point.getY());
        }
        if (point.getY() > max.getY()) {
            max.setY(point.getY());
        }

        if (point.getZ() < min.getZ()) {
            min.setZ(point.getZ());
        }
        if (point.getZ() > max.getZ()) {
            max.setZ(point.getZ());
        }
    }

    /**
     * <code>containAABB</code> creates a minimum-volume axis-aligned bounding box of the points, then selects the
     * smallest enclosing sphere of the box with the sphere centered at the boxes center.
     *
     * @param points
     *            the list of points.
     */
    public void containAABB(final FloatBuffer points) {
        if (points == null) {
            return;
        }

        points.rewind();
        if (points.remaining() <= 2) {
            return;
        }

        BufferUtils.populateFromBuffer(_compVect1, points, 0);
        double minX = _compVect1.getX(), minY = _compVect1.getY(), minZ = _compVect1.getZ();
        double maxX = _compVect1.getX(), maxY = _compVect1.getY(), maxZ = _compVect1.getZ();

        for (int i = 1, len = points.remaining() / 3; i < len; i++) {
            BufferUtils.populateFromBuffer(_compVect1, points, i);

            if (_compVect1.getX() < minX) {
                minX = _compVect1.getX();
            } else if (_compVect1.getX() > maxX) {
                maxX = _compVect1.getX();
            }

            if (_compVect1.getY() < minY) {
                minY = _compVect1.getY();
            } else if (_compVect1.getY() > maxY) {
                maxY = _compVect1.getY();
            }

            if (_compVect1.getZ() < minZ) {
                minZ = _compVect1.getZ();
            } else if (_compVect1.getZ() > maxZ) {
                maxZ = _compVect1.getZ();
            }
        }

        _center.set(minX + maxX, minY + maxY, minZ + maxZ);
        _center.multiplyLocal(0.5);

        setXExtent(maxX - _center.getX());
        setYExtent(maxY - _center.getY());
        setZExtent(maxZ - _center.getZ());
    }

    /**
     * <code>whichSide</code> takes a plane (typically provided by a view frustum) to determine which side this bound is
     * on.
     *
     * @param plane
     *            the plane to check against.
     */
    @Override
    public Side whichSide(final ReadOnlyPlane plane) {
        final ReadOnlyVector3 normal = plane.getNormal();
        final double radius = Math.abs(getXExtent() * normal.getX()) + Math.abs(getYExtent() * normal.getY())
                + Math.abs(getZExtent() * normal.getZ());

        final double distance = plane.pseudoDistance(_center);

        if (distance < -radius) {
            return Plane.Side.Inside;
        } else if (distance > radius) {
            return Plane.Side.Outside;
        } else {
            return Plane.Side.Neither;
        }
    }

    /**
     * <code>merge</code> combines this sphere with a second bounding sphere. This new sphere contains both bounding
     * spheres and is returned.
     *
     * @param volume
     *            the sphere to combine with this sphere.
     * @return the new sphere
     */
    @Override
    public BoundingVolume merge(final BoundingVolume volume) {
        if (volume == null) {
            return this;
        }

        switch (volume.getType()) {
            case AABB: {
                final BoundingBox vBox = (BoundingBox) volume;
                return merge(vBox._center, vBox.getXExtent(), vBox.getYExtent(), vBox.getZExtent(), new BoundingBox(
                        new Vector3(0, 0, 0), 0, 0, 0));
            }

            case Sphere: {
                final BoundingSphere vSphere = (BoundingSphere) volume;
                return merge(vSphere._center, vSphere.getRadius(), vSphere.getRadius(), vSphere.getRadius(),
                        new BoundingBox(new Vector3(0, 0, 0), 0, 0, 0));
            }

            case OBB: {
                final OrientedBoundingBox box = (OrientedBoundingBox) volume;
                final BoundingBox rVal = (BoundingBox) this.clone(null);
                return rVal.mergeOBB(box);
            }

            default:
                return null;
        }
    }

    /**
     * <code>mergeLocal</code> combines this sphere with a second bounding sphere locally. Altering this sphere to
     * contain both the original and the additional sphere volumes;
     *
     * @param volume
     *            the sphere to combine with this sphere.
     * @return this
     */
    @Override
    public BoundingVolume mergeLocal(final BoundingVolume volume) {
        if (volume == null) {
            return this;
        }

        switch (volume.getType()) {
            case AABB: {
                final BoundingBox vBox = (BoundingBox) volume;
                return merge(vBox._center, vBox.getXExtent(), vBox.getYExtent(), vBox.getZExtent(), this);
            }

            case Sphere: {
                final BoundingSphere vSphere = (BoundingSphere) volume;
                return merge(vSphere._center, vSphere.getRadius(), vSphere.getRadius(), vSphere.getRadius(), this);
            }

            case OBB: {
                return mergeOBB((OrientedBoundingBox) volume);
            }

            default:
                return null;
        }
    }

    @Override
    public BoundingVolume asType(final Type newType) {
        if (newType == null) {
            return null;
        }

        switch (newType) {
            case AABB: {
                return this.clone(null);
            }

            case Sphere: {
                final BoundingSphere sphere = new BoundingSphere(0, _center);
                return sphere.merge(this);
            }

            case OBB: {
                final OrientedBoundingBox obb = new OrientedBoundingBox();
                obb.setCenter(_center);
                return obb.merge(this);
            }

            default:
                return null;
        }
    }

    /**
     * Merges this AABB with the given OBB.
     *
     * @param volume
     *            the OBB to merge this AABB with.
     * @return This AABB extended to fit the given OBB.
     */
    private BoundingBox mergeOBB(final OrientedBoundingBox volume) {
        // check for infinite bounds to prevent NaN values
        if (Double.isInfinite(getXExtent()) || Double.isInfinite(getYExtent()) || Double.isInfinite(getZExtent())
                || Vector3.isInfinite(volume.getExtent())) {
            setCenter(Vector3.ZERO);
            setXExtent(Double.POSITIVE_INFINITY);
            setYExtent(Double.POSITIVE_INFINITY);
            setZExtent(Double.POSITIVE_INFINITY);
            return this;
        }

        if (!volume.correctCorners) {
            volume.computeCorners();
        }

        double minX, minY, minZ;
        double maxX, maxY, maxZ;

        minX = _center.getX() - getXExtent();
        minY = _center.getY() - getYExtent();
        minZ = _center.getZ() - getZExtent();

        maxX = _center.getX() + getXExtent();
        maxY = _center.getY() + getYExtent();
        maxZ = _center.getZ() + getZExtent();

        for (int i = 1; i < volume._vectorStore.length; i++) {
            final Vector3 temp = volume._vectorStore[i];
            if (temp.getX() < minX) {
                minX = temp.getX();
            } else if (temp.getX() > maxX) {
                maxX = temp.getX();
            }

            if (temp.getY() < minY) {
                minY = temp.getY();
            } else if (temp.getY() > maxY) {
                maxY = temp.getY();
            }

            if (temp.getZ() < minZ) {
                minZ = temp.getZ();
            } else if (temp.getZ() > maxZ) {
                maxZ = temp.getZ();
            }
        }

        _center.set(minX + maxX, minY + maxY, minZ + maxZ);
        _center.multiplyLocal(0.5);

        setXExtent(maxX - _center.getX());
        setYExtent(maxY - _center.getY());
        setZExtent(maxZ - _center.getZ());
        return this;
    }

    /**
     * <code>merge</code> combines this bounding box with another box which is defined by the center, x, y, z extents.
     *
     * @param boxCenter
     *            the center of the box to merge with
     * @param boxX
     *            the x extent of the box to merge with.
     * @param boxY
     *            the y extent of the box to merge with.
     * @param boxZ
     *            the z extent of the box to merge with.
     * @param store
     *            the box to store our results in.
     * @return the resulting merged box.
     */
    private BoundingBox merge(final Vector3 boxCenter, final double boxX, final double boxY, final double boxZ,
            final BoundingBox store) {
        // check for infinite bounds to prevent NaN values
        if (Double.isInfinite(getXExtent()) || Double.isInfinite(getYExtent()) || Double.isInfinite(getZExtent())
                || Double.isInfinite(boxX) || Double.isInfinite(boxY) || Double.isInfinite(boxZ)) {
            store.setCenter(Vector3.ZERO);
            store.setXExtent(Double.POSITIVE_INFINITY);
            store.setYExtent(Double.POSITIVE_INFINITY);
            store.setZExtent(Double.POSITIVE_INFINITY);
            return store;
        }

        _compVect1.setX(_center.getX() - getXExtent());
        if (_compVect1.getX() > boxCenter.getX() - boxX) {
            _compVect1.setX(boxCenter.getX() - boxX);
        }
        _compVect1.setY(_center.getY() - getYExtent());
        if (_compVect1.getY() > boxCenter.getY() - boxY) {
            _compVect1.setY(boxCenter.getY() - boxY);
        }
        _compVect1.setZ(_center.getZ() - getZExtent());
        if (_compVect1.getZ() > boxCenter.getZ() - boxZ) {
            _compVect1.setZ(boxCenter.getZ() - boxZ);
        }

        _compVect2.setX(_center.getX() + getXExtent());
        if (_compVect2.getX() < boxCenter.getX() + boxX) {
            _compVect2.setX(boxCenter.getX() + boxX);
        }
        _compVect2.setY(_center.getY() + getYExtent());
        if (_compVect2.getY() < boxCenter.getY() + boxY) {
            _compVect2.setY(boxCenter.getY() + boxY);
        }
        _compVect2.setZ(_center.getZ() + getZExtent());
        if (_compVect2.getZ() < boxCenter.getZ() + boxZ) {
            _compVect2.setZ(boxCenter.getZ() + boxZ);
        }

        store._center.set(_compVect2).addLocal(_compVect1).multiplyLocal(0.5);

        store.setXExtent(_compVect2.getX() - store._center.getX());
        store.setYExtent(_compVect2.getY() - store._center.getY());
        store.setZExtent(_compVect2.getZ() - store._center.getZ());

        return store;
    }

    /**
     * <code>clone</code> creates a new BoundingBox object containing the same data as this one.
     *
     * @param store
     *            where to store the cloned information. if null or wrong class, a new store is created.
     * @return the new BoundingBox
     */
    @Override
    public BoundingVolume clone(final BoundingVolume store) {
        if (store != null && store.getType() == Type.AABB) {
            final BoundingBox rVal = (BoundingBox) store;
            rVal._center.set(_center);
            rVal.setXExtent(_xExtent);
            rVal.setYExtent(_yExtent);
            rVal.setZExtent(_zExtent);
            rVal._checkPlane = _checkPlane;
            return rVal;
        }

        final BoundingBox rVal = new BoundingBox(_center, getXExtent(), getYExtent(), getZExtent());
        return rVal;
    }

    /**
     * <code>toString</code> returns the string representation of this object. The form is:
     * "Radius: RRR.SSSS Center: <Vector>".
     *
     * @return the string representation of this.
     */
    @Override
    public String toString() {
        return "com.ardor3d.scene.BoundingBox [Center: " + _center + "  xExtent: " + getXExtent() + "  yExtent: "
                + getYExtent() + "  zExtent: " + getZExtent() + "]";
    }

    @Override
    public boolean intersects(final BoundingVolume bv) {
        if (bv == null) {
            return false;
        }

        return bv.intersectsBoundingBox(this);
    }

    @Override
    public boolean intersectsSphere(final BoundingSphere bs) {
        if (!Vector3.isValid(_center) || !Vector3.isValid(bs._center)) {
            return false;
        }

        if (Math.abs(_center.getX() - bs.getCenter().getX()) < bs.getRadius() + getXExtent()
                && Math.abs(_center.getY() - bs.getCenter().getY()) < bs.getRadius() + getYExtent()
                && Math.abs(_center.getZ() - bs.getCenter().getZ()) < bs.getRadius() + getZExtent()) {
            return true;
        }

        return false;
    }

    @Override
    public boolean intersectsBoundingBox(final BoundingBox bb) {
        if (!Vector3.isValid(_center) || !Vector3.isValid(bb._center)) {
            return false;
        }

        if (_center.getX() + getXExtent() < bb._center.getX() - bb.getXExtent()
                || _center.getX() - getXExtent() > bb._center.getX() + bb.getXExtent()) {
            return false;
        } else if (_center.getY() + getYExtent() < bb._center.getY() - bb.getYExtent()
                || _center.getY() - getYExtent() > bb._center.getY() + bb.getYExtent()) {
            return false;
        } else if (_center.getZ() + getZExtent() < bb._center.getZ() - bb.getZExtent()
                || _center.getZ() - getZExtent() > bb._center.getZ() + bb.getZExtent()) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public boolean intersectsOrientedBoundingBox(final OrientedBoundingBox obb) {
        return obb.intersectsBoundingBox(this);
    }

    @Override
    public boolean intersects(final ReadOnlyRay3 ray) {
        if (!Vector3.isValid(_center)) {
            return false;
        }

        final Vector3 diff = ray.getOrigin().subtract(_center, _compVect1);

        final ReadOnlyVector3 direction = ray.getDirection();

        final double[] t = { 0.0, Double.POSITIVE_INFINITY };

        // Check for degenerate cases and pad using zero tolerance. Should give close enough result.
        double x = getXExtent();
        if (x < MathUtils.ZERO_TOLERANCE && x >= 0) {
            x = MathUtils.ZERO_TOLERANCE;
        }
        double y = getYExtent();
        if (y < MathUtils.ZERO_TOLERANCE && y >= 0) {
            y = MathUtils.ZERO_TOLERANCE;
        }
        double z = getZExtent();
        if (z < MathUtils.ZERO_TOLERANCE && z >= 0) {
            z = MathUtils.ZERO_TOLERANCE;
        }

        // Special case.
        if (Double.isInfinite(x) && Double.isInfinite(y) && Double.isInfinite(z)) {
            return true;
        }

        final boolean notEntirelyClipped = clip(direction.getX(), -diff.getX() - x, t)
                && clip(-direction.getX(), diff.getX() - x, t) && clip(direction.getY(), -diff.getY() - y, t)
                && clip(-direction.getY(), diff.getY() - y, t) && clip(direction.getZ(), -diff.getZ() - z, t)
                && clip(-direction.getZ(), diff.getZ() - z, t);

        return (notEntirelyClipped && (t[0] != 0.0 || t[1] != Double.POSITIVE_INFINITY));
    }

    @Override
    public IntersectionRecord intersectsWhere(final ReadOnlyRay3 ray) {
        if (!Vector3.isValid(_center)) {
            return null;
        }

        final Vector3 diff = ray.getOrigin().subtract(_center, _compVect1);

        final ReadOnlyVector3 direction = ray.getDirection();

        final double[] t = { 0.0, Double.POSITIVE_INFINITY };

        // Check for degenerate cases and pad using zero tolerance. Should give close enough result.
        double x = getXExtent();
        if (x < MathUtils.ZERO_TOLERANCE && x >= 0) {
            x = MathUtils.ZERO_TOLERANCE;
        }
        double y = getYExtent();
        if (y < MathUtils.ZERO_TOLERANCE && y >= 0) {
            y = MathUtils.ZERO_TOLERANCE;
        }
        double z = getZExtent();
        if (z < MathUtils.ZERO_TOLERANCE && z >= 0) {
            z = MathUtils.ZERO_TOLERANCE;
        }

        final boolean notEntirelyClipped = clip(direction.getX(), -diff.getX() - x, t)
                && clip(-direction.getX(), diff.getX() - x, t) && clip(direction.getY(), -diff.getY() - y, t)
                && clip(-direction.getY(), diff.getY() - y, t) && clip(direction.getZ(), -diff.getZ() - z, t)
                && clip(-direction.getZ(), diff.getZ() - z, t);

        if (notEntirelyClipped && (t[0] != 0.0 || t[1] != Double.POSITIVE_INFINITY)) {
            if (t[1] > t[0]) {
                final double[] distances = t;
                final Vector3[] points = new Vector3[] {
                        new Vector3(ray.getDirection()).multiplyLocal(distances[0]).addLocal(ray.getOrigin()),
                        new Vector3(ray.getDirection()).multiplyLocal(distances[1]).addLocal(ray.getOrigin()) };
                return new IntersectionRecord(distances, points);
            }

            final double[] distances = new double[] { t[0] };
            final Vector3[] points = new Vector3[] { new Vector3(ray.getDirection()).multiplyLocal(distances[0])
                    .addLocal(ray.getOrigin()), };
            return new IntersectionRecord(distances, points);
        }

        return null;

    }

    @Override
    public boolean contains(final ReadOnlyVector3 point) {
        return Math.abs(_center.getX() - point.getX()) < getXExtent()
                && Math.abs(_center.getY() - point.getY()) < getYExtent()
                && Math.abs(_center.getZ() - point.getZ()) < getZExtent();
    }

    @Override
    public double distanceToEdge(final ReadOnlyVector3 point) {
        // compute coordinates of point in box coordinate system
        final Vector3 closest = point.subtract(_center, _compVect1);

        // project test point onto box
        double sqrDistance = 0.0;
        double delta;

        if (closest.getX() < -getXExtent()) {
            delta = closest.getX() + getXExtent();
            sqrDistance += delta * delta;
            closest.setX(-getXExtent());
        } else if (closest.getX() > getXExtent()) {
            delta = closest.getX() - getXExtent();
            sqrDistance += delta * delta;
            closest.setX(getXExtent());
        }

        if (closest.getY() < -getYExtent()) {
            delta = closest.getY() + getYExtent();
            sqrDistance += delta * delta;
            closest.setY(-getYExtent());
        } else if (closest.getY() > getYExtent()) {
            delta = closest.getY() - getYExtent();
            sqrDistance += delta * delta;
            closest.setY(getYExtent());
        }

        if (closest.getZ() < -getZExtent()) {
            delta = closest.getZ() + getZExtent();
            sqrDistance += delta * delta;
            closest.setZ(-getZExtent());
        } else if (closest.getZ() > getZExtent()) {
            delta = closest.getZ() - getZExtent();
            sqrDistance += delta * delta;
            closest.setZ(getZExtent());
        }

        return Math.sqrt(sqrDistance);
    }

    /**
     * Get our corners using the bounding center and extents.
     *
     * @param store
     *            An optional store. Must be at least length of 8. If null, one will be created for you.
     * @return array filled with our corners.
     * @throws ArrayIndexOutOfBoundsException
     *             if our store is length < 8.
     */
    public Vector3[] getCorners(Vector3[] store) {
        if (store == null) {
            store = new Vector3[8];
            for (int i = 0; i < store.length; i++) {
                store[i] = new Vector3();
            }
        }
        store[0].set(_center.getX() + _xExtent, _center.getY() + _yExtent, _center.getZ() + _zExtent);
        store[1].set(_center.getX() + _xExtent, _center.getY() + _yExtent, _center.getZ() - _zExtent);
        store[2].set(_center.getX() + _xExtent, _center.getY() - _yExtent, _center.getZ() + _zExtent);
        store[3].set(_center.getX() + _xExtent, _center.getY() - _yExtent, _center.getZ() - _zExtent);
        store[4].set(_center.getX() - _xExtent, _center.getY() + _yExtent, _center.getZ() + _zExtent);
        store[5].set(_center.getX() - _xExtent, _center.getY() + _yExtent, _center.getZ() - _zExtent);
        store[6].set(_center.getX() - _xExtent, _center.getY() - _yExtent, _center.getZ() + _zExtent);
        store[7].set(_center.getX() - _xExtent, _center.getY() - _yExtent, _center.getZ() - _zExtent);
        return store;
    }

    /**
     * <code>clip</code> determines if a line segment intersects the current test plane.
     *
     * @param denom
     *            the denominator of the line segment.
     * @param numer
     *            the numerator of the line segment.
     * @param t
     *            test values of the plane.
     * @return true if the line segment intersects the plane, false otherwise.
     */
    private boolean clip(final double denom, final double numer, final double[] t) {
        // Return value is 'true' if line segment intersects the current test
        // plane. Otherwise 'false' is returned in which case the line segment
        // is entirely clipped.
        if (denom > 0.0) {
            if (numer > denom * t[1]) {
                return false;
            }
            if (numer > denom * t[0]) {
                t[0] = numer / denom;
            }
            return true;
        } else if (denom < 0.0) {
            if (numer > denom * t[0]) {
                return false;
            }
            if (numer > denom * t[1]) {
                t[1] = numer / denom;
            }
            return true;
        } else {
            return numer <= 0.0;
        }
    }

    /**
     * Query extent.
     *
     * @param store
     *            where extent gets stored - null to return a new vector
     * @return store / new vector
     */
    public Vector3 getExtent(Vector3 store) {
        if (store == null) {
            store = new Vector3();
        }
        store.set(getXExtent(), getYExtent(), getZExtent());
        return store;
    }

    @Override
    public void write(final OutputCapsule capsule) throws IOException {
        super.write(capsule);
        capsule.write(getXExtent(), "xExtent", 0);
        capsule.write(getYExtent(), "yExtent", 0);
        capsule.write(getZExtent(), "zExtent", 0);
    }

    @Override
    public void read(final InputCapsule capsule) throws IOException {
        super.read(capsule);
        setXExtent(capsule.readDouble("xExtent", 0));
        setYExtent(capsule.readDouble("yExtent", 0));
        setZExtent(capsule.readDouble("zExtent", 0));
    }

    @Override
    public double getVolume() {
        return (8 * getXExtent() * getYExtent() * getZExtent());
    }
}
TOP

Related Classes of com.ardor3d.bounding.BoundingBox

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.