/*global defineSuite*/
defineSuite([
        'Core/OrientedBoundingBox',
        'Core/BoundingRectangle',
        'Core/BoundingSphere',
        'Core/Cartesian3',
        'Core/Cartesian4',
        'Core/Ellipsoid',
        'Core/EllipsoidTangentPlane',
        'Core/Intersect',
        'Core/Math',
        'Core/Matrix3',
        'Core/Occluder',
        'Core/Plane',
        'Core/Quaternion',
        'Core/Rectangle'
    ], function(
        OrientedBoundingBox,
        BoundingRectangle,
        BoundingSphere,
        Cartesian3,
        Cartesian4,
        Ellipsoid,
        EllipsoidTangentPlane,
        Intersect,
        CesiumMath,
        Matrix3,
        Occluder,
        Plane,
        Quaternion,
        Rectangle) {
    "use strict";

    var positions = [
        new Cartesian3(2.0, 0.0, 0.0),
        new Cartesian3(0.0, 3.0, 0.0),
        new Cartesian3(0.0, 0.0, 4.0),
        new Cartesian3(-2.0, 0.0, 0.0),
        new Cartesian3(0.0, -3.0, 0.0),
        new Cartesian3(0.0, 0.0, -4.0)
    ];

    function rotatePositions(positions, axis, angle) {
        var points = [];

        var quaternion = Quaternion.fromAxisAngle(axis, angle);
        var rotation = Matrix3.fromQuaternion(quaternion);

        for (var i = 0; i < positions.length; ++i) {
            points.push(Matrix3.multiplyByVector(rotation, positions[i], new Cartesian3()));
        }

        return {
            points : points,
            rotation : rotation
        };
    }

    function translatePositions(positions, translation) {
        var points = [];
        for (var i = 0; i < positions.length; ++i) {
            points.push(Cartesian3.add(translation, positions[i], new Cartesian3()));
        }

        return points;
    }

    it('constructor sets expected default values', function() {
        var box = new OrientedBoundingBox();
        expect(box.center).toEqual(Cartesian3.ZERO);
        expect(box.halfAxes).toEqual(Matrix3.ZERO);
    });

    it('fromPoints constructs empty box with undefined positions', function() {
        var box = OrientedBoundingBox.fromPoints(undefined);
        expect(box.halfAxes).toEqual(Matrix3.ZERO);
        expect(box.center).toEqual(Cartesian3.ZERO);
    });

    it('fromPoints constructs empty box with empty positions', function() {
        var box = OrientedBoundingBox.fromPoints([]);
        expect(box.halfAxes).toEqual(Matrix3.ZERO);
        expect(box.center).toEqual(Cartesian3.ZERO);
    });

    it('fromPoints correct scale', function() {
        var box = OrientedBoundingBox.fromPoints(positions);
        expect(box.halfAxes).toEqual(Matrix3.fromScale(new Cartesian3(2.0, 3.0, 4.0)));
        expect(box.center).toEqual(Cartesian3.ZERO);
    });

    it('fromPoints correct translation', function() {
        var translation = new Cartesian3(10.0, -20.0, 30.0);
        var points = translatePositions(positions, translation);
        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqual(Matrix3.fromScale(new Cartesian3(2.0, 3.0, 4.0)));
        expect(box.center).toEqual(translation);
    });

    it('fromPoints rotation about z', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(3.0, 2.0, 4.0), new Matrix3()), CesiumMath.EPSILON15);
        expect(box.center).toEqualEpsilon(Cartesian3.ZERO, CesiumMath.EPSILON15);
    });

    it('fromPoints rotation about y', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_Y, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(4.0, 3.0, 2.0), new Matrix3()), CesiumMath.EPSILON15);
        expect(box.center).toEqualEpsilon(Cartesian3.ZERO, CesiumMath.EPSILON15);
    });

    it('fromPoints rotation about x', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_X, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(2.0, 4.0, 3.0), new Matrix3()), CesiumMath.EPSILON15);
        expect(box.center).toEqualEpsilon(Cartesian3.ZERO, CesiumMath.EPSILON15);
    });

    it('fromPoints rotation and translation', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;

        var translation = new Cartesian3(-40.0, 20.0, -30.0);
        points = translatePositions(points, translation);

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(3.0, 2.0, 4.0), new Matrix3()), CesiumMath.EPSILON14);
        expect(box.center).toEqualEpsilon(translation, CesiumMath.EPSILON15);
    });

    it('fromRectangle sets correct default ellipsoid', function() {
        var rectangle = new Rectangle(-0.9, -1.2, 0.5, 0.7);
        var box1 = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0);
        var box2 = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, Ellipsoid.WGS84);

        expect(box1.center).toEqualEpsilon(box2.center, CesiumMath.EPSILON15);

        expect(box1.halfAxes).toEqualEpsilon(box2.halfAxes, CesiumMath.EPSILON15);
    });

    it('fromRectangle sets correct default heights', function() {
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        var box = OrientedBoundingBox.fromRectangle(rectangle, undefined, undefined, Ellipsoid.UNIT_SPHERE);

        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);

        var rotScale = Matrix3.ZERO;
        expect(box.halfAxes).toEqualEpsilon(rotScale, CesiumMath.EPSILON15);
    });

    it('fromRectangle throws without rectangle', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        expect(function() {
            OrientedBoundingBox.fromRectangle(undefined, 0.0, 0.0, ellipsoid);
        }).toThrowDeveloperError();
    });

    it('fromRectangle throws with invalid rectangles', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(1.0, -1.0, -1.0, 1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-1.0, 1.0, 1.0, -1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-1.0, 1.0, -2.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-2.0, 2.0, -1.0, 1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-2.0, -2.0, 2.0, 1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-2.0, -2.0, 1.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-1.0, -2.0, 2.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-2.0, -1.0, 2.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
    });

    it('fromRectangle throws with non-revolution ellipsoids', function() {
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        expect(function() { return OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, new Ellipsoid(1.01, 1.00, 1.01)); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, new Ellipsoid(1.00, 1.01, 1.01)); }).toThrowDeveloperError();
    });

    it('fromRectangle creates an OrientedBoundingBox without a result parameter', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        var box = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, ellipsoid);

        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);

        var rotScale = Matrix3.ZERO;
        expect(box.halfAxes).toEqualEpsilon(rotScale, CesiumMath.EPSILON15);
    });

    it('fromRectangle creates an OrientedBoundingBox with a result parameter', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        var result = new OrientedBoundingBox();
        var box = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, ellipsoid, result);
        expect(box).toBe(result);

        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);

        var rotScale = Matrix3.ZERO;
        expect(box.halfAxes).toEqualEpsilon(rotScale, CesiumMath.EPSILON15);
    });

    it('fromRectangle for rectangles with heights', function() {
        var d90 = CesiumMath.PI_OVER_TWO;

        var box;

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), 1.0, 1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), -1.0, -1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), -1.0, 1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 1, 0, 0, 0, 0, 0, 0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), 0.0, 1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 1, 2, 0, 0, 0, 2, 0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), -1.0, -1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), -1.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 0.5, 1, 0, 0, 0, 1, 0), CesiumMath.EPSILON15);
    });

    it('fromRectangle for interesting, degenerate, and edge-case rectangles', function() {
        var d45 = CesiumMath.PI_OVER_FOUR;
        var d30 = CesiumMath.PI_OVER_SIX;
        var d90 = CesiumMath.PI_OVER_TWO;
        var d135 = 3 * CesiumMath.PI_OVER_FOUR;
        var d180 = CesiumMath.PI;
        var sqrt3 = Math.sqrt(3.0);

        var box;

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(d180, 0.0, -d180, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(-1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(d180, 0.0, d180, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(-1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, d90, 0.0, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.0, 1.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, d180, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.5, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(-1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d30, d90, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.1875 * sqrt3, 0.0, 0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, -sqrt3/4, 5*sqrt3/16, 1, 0, 0, 0, 3/4, 5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d30), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.1875 * sqrt3, 0.0, -0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, sqrt3/4, 5*sqrt3/16, 1, 0, 0, 0, 3/4, -5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d30, d180, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.1875 * sqrt3, 0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(-1, 0, 0, 0, -sqrt3/4, 5*sqrt3/16, 0, 3/4, 5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d90, d180, d30), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.1875 * sqrt3, -0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(-1, 0, 0, 0, sqrt3/4, 5*sqrt3/16, 0, 3/4, -5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d45, 0.0, d45, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3((1.0 + Math.SQRT1_2) / 2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5 * (1.0 - Math.SQRT1_2), Math.SQRT1_2, 0.0, 0.0, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(d135, 0.0, -d135, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(-(1.0 + Math.SQRT1_2) / 2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, -0.5 * (1.0 - Math.SQRT1_2), -Math.SQRT1_2, 0.0, 0.0, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d45, 0.0, d45), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3((1.0 + Math.SQRT1_2) / 2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5 * (1.0 - Math.SQRT1_2), 0.0, 0.0, 0.0, 0.0, Math.SQRT1_2, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, 0.0, d90, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d90, 0.0, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0), CesiumMath.EPSILON15);
    });

    var intersectPlaneTestCornersEdgesFaces = function(center, axes) {
        var SQRT1_2 = Math.pow(1.0 / 2.0, 1 / 2.0);
        var SQRT3_4 = Math.pow(3.0 / 4.0, 1 / 2.0);

        var box = new OrientedBoundingBox(center, Matrix3.multiplyByScalar(axes, 0.5, new Matrix3()));

        var planeNormXform = function(nx, ny, nz, dist) {
            var n = new Cartesian3(nx, ny, nz);
            var arb = new Cartesian3(357, 924, 258);
            var p0 = Cartesian3.normalize(n, new Cartesian3());
            Cartesian3.multiplyByScalar(p0, -dist, p0);
            var tang = Cartesian3.cross(n, arb, new Cartesian3());
            Cartesian3.normalize(tang, tang);
            var binorm = Cartesian3.cross(n, tang, new Cartesian3());
            Cartesian3.normalize(binorm, binorm);

            Matrix3.multiplyByVector(axes, p0, p0);
            Matrix3.multiplyByVector(axes, tang, tang);
            Matrix3.multiplyByVector(axes, binorm, binorm);
            Cartesian3.cross(tang, binorm, n);
            Cartesian3.normalize(n, n);

            Cartesian3.add(p0, center, p0);
            var d = -Cartesian3.dot(p0, n);
            if (Math.abs(d) > 0.0001 && Cartesian3.magnitudeSquared(n) > 0.0001) {
                return new Plane(n, d);
            } else {
                return undefined;
            }
        };

        var pl;

        // Tests against faces

        pl = planeNormXform(+1.0, +0.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +0.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +1.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, -1.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +0.0, +1.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +0.0, -1.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }

        pl = planeNormXform(+1.0, +0.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, +1.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, -1.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +0.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, +1.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, -1.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +0.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +0.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +1.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, -1.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +0.0, +1.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +0.0, -1.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }

        // Tests against edges

        pl = planeNormXform(+1.0, +1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, -1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, -1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, +0.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, +0.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +0.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +0.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +1.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +1.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, -1.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, -1.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }

        pl = planeNormXform(+1.0, +1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, -1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, -1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, +0.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, +0.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +0.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +0.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +1.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +1.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, -1.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, -1.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }

        // Tests against corners

        pl = planeNormXform(+1.0, +1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, +1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, -1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, -1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, -1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, -1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }

        pl = planeNormXform(+1.0, +1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, +1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, -1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, -1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, -1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, -1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }

    };

    it('intersectPlane works with untransformed box', function() {
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.IDENTITY);
    });

    it('intersectPlane works with off-center box', function() {
        intersectPlaneTestCornersEdgesFaces(new Cartesian3(1.0, 0.0, 0.0), Matrix3.IDENTITY);
        intersectPlaneTestCornersEdgesFaces(new Cartesian3(0.7, -1.8, 12.0), Matrix3.IDENTITY);
    });

    it('intersectPlane works with rotated box', function() {
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO,
                Matrix3.fromQuaternion(Quaternion.fromAxisAngle(new Cartesian3(0.5, 1.5, -1.2), 1.2), new Matrix3()));
    });

    it('intersectPlane works with scaled box', function() {
        var m = new Matrix3();
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(1.5, 0.4, 20.6), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(0.0, 0.4, 20.6), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(1.5, 0.0, 20.6), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(1.5, 0.4, 0.0), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(0.0, 0.0, 0.0), m));
    });

    it('intersectPlane works with this arbitrary box', function() {
        var m = Matrix3.fromScale(new Cartesian3(1.5, 80.4, 2.6), new Matrix3());
        var n = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(new Cartesian3(0.5, 1.5, -1.2), 1.2), new Matrix3());
        Matrix3.multiply(m, n, n);
        intersectPlaneTestCornersEdgesFaces(new Cartesian3(-5.1, 0.0, 0.1), n);
    });

    it('intersectPlane fails without box parameter', function() {
        var plane = new Cartesian4(1.0, 0.0, 0.0, 0.0);
        expect(function() {
            OrientedBoundingBox.intersectPlane(undefined, plane);
        }).toThrowDeveloperError();
    });

    it('intersectPlane fails without plane parameter', function() {
        var box = new OrientedBoundingBox(Cartesian3.IDENTITY, Matrix3.ZERO);
        expect(function() {
            OrientedBoundingBox.intersectPlane(box, undefined);
        }).toThrowDeveloperError();
    });

    it('distanceSquaredTo', function() {
        var r0 = Matrix3.fromRotationZ(CesiumMath.toRadians(-45.0));
        var r1 = Matrix3.fromRotationY(CesiumMath.toRadians(45.0));

        var rotation = Matrix3.multiply(r1, r0, r0);
        var scale = new Cartesian3(2.0, 3.0, 4.0);
        var rotationScale = Matrix3.multiplyByScale(rotation, scale, rotation);

        var center = new Cartesian3(4.0, 3.0, 2.0);

        var obb = new OrientedBoundingBox(center, rotationScale);

        var halfAxes = obb.halfAxes;
        var xAxis = Matrix3.getColumn(halfAxes, 0, new Cartesian3());
        var yAxis = Matrix3.getColumn(halfAxes, 1, new Cartesian3());
        var zAxis = Matrix3.getColumn(halfAxes, 2, new Cartesian3());

        // from positive x direction
        var cartesian = Cartesian3.multiplyByScalar(xAxis, 2.0, new Cartesian3());
        Cartesian3.add(cartesian, center, cartesian);

        var d = Cartesian3.distance(cartesian, center) - scale.x;
        var expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);

        // from negative x direction
        Cartesian3.multiplyByScalar(xAxis, -2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.x;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);

        // from positive y direction
        Cartesian3.multiplyByScalar(yAxis, 2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.y;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);

        // from negative y direction
        Cartesian3.multiplyByScalar(yAxis, -2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.y;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);

        // from positive z direction
        Cartesian3.multiplyByScalar(zAxis, 2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.z;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);

        // from negative z direction
        Cartesian3.multiplyByScalar(zAxis, -2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.z;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);

        // from corner point
        Cartesian3.add(xAxis, yAxis, cartesian);
        Cartesian3.add(zAxis, cartesian, cartesian);

        var cornerDistance = Cartesian3.magnitude(cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - cornerDistance;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON14);
    });

    it('distanceSquaredTo throws without box', function() {
        expect(function() {
            OrientedBoundingBox.distanceSquaredTo(undefined, new Cartesian3());
        }).toThrowDeveloperError();
    });

    it('distanceSquaredTo throws without cartesian', function() {
        expect(function() {
            OrientedBoundingBox.distanceSquaredTo(new OrientedBoundingBox(), undefined);
        }).toThrowDeveloperError();
    });

    it('computePlaneDistances', function() {
        var r0 = Matrix3.fromRotationZ(CesiumMath.toRadians(-45.0));
        var r1 = Matrix3.fromRotationY(CesiumMath.toRadians(45.0));

        var rotation = Matrix3.multiply(r1, r0, r0);
        var scale = new Cartesian3(2.0, 3.0, 4.0);
        var rotationScale = Matrix3.multiplyByScale(rotation, scale, rotation);

        var center = new Cartesian3(4.0, 3.0, 2.0);

        var obb = new OrientedBoundingBox(center, rotationScale);

        var halfAxes = obb.halfAxes;
        var xAxis = Matrix3.getColumn(halfAxes, 0, new Cartesian3());
        var yAxis = Matrix3.getColumn(halfAxes, 1, new Cartesian3());
        var zAxis = Matrix3.getColumn(halfAxes, 2, new Cartesian3());

        // from x direction
        var position = Cartesian3.multiplyByScalar(xAxis, 2.0, new Cartesian3());
        Cartesian3.add(position, center, position);

        var direction = Cartesian3.negate(xAxis, new Cartesian3());
        Cartesian3.normalize(direction, direction);

        var d = Cartesian3.distance(position, center);
        var expectedNear = d - scale.x;
        var expectedFar = d + scale.x;

        var distances = obb.computePlaneDistances(position, direction);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);

        // from y direction
        Cartesian3.multiplyByScalar(yAxis, 2.0, position);
        Cartesian3.add(position, center, position);

        Cartesian3.negate(yAxis, direction);
        Cartesian3.normalize(direction, direction);

        d = Cartesian3.distance(position, center);
        expectedNear = d - scale.y;
        expectedFar = d + scale.y;

        obb.computePlaneDistances(position, direction, distances);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);

        // from z direction
        Cartesian3.multiplyByScalar(zAxis, 2.0, position);
        Cartesian3.add(position, center, position);

        Cartesian3.negate(zAxis, direction);
        Cartesian3.normalize(direction, direction);

        d = Cartesian3.distance(position, center);
        expectedNear = d - scale.z;
        expectedFar = d + scale.z;

        obb.computePlaneDistances(position, direction, distances);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);

        // from corner point
        Cartesian3.add(xAxis, yAxis, position);
        Cartesian3.add(zAxis, position, position);

        Cartesian3.negate(position, direction);
        Cartesian3.normalize(direction, direction);

        var cornerDistance = Cartesian3.magnitude(position);
        Cartesian3.add(position, center, position);

        d = Cartesian3.distance(position, center);
        expectedNear = d - cornerDistance;
        expectedFar = d + cornerDistance;

        obb.computePlaneDistances(position, direction, distances);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);
    });

    it('computePlaneDistances throws without a box', function() {
        expect(function() {
            OrientedBoundingBox.computePlaneDistances(undefined, new Cartesian3(), new Cartesian3());
        }).toThrowDeveloperError();
    });

    it('computePlaneDistances throws without a position', function() {
        expect(function() {
            OrientedBoundingBox.computePlaneDistances(new OrientedBoundingBox(), undefined, new Cartesian3());
        }).toThrowDeveloperError();
    });

    it('computePlaneDistances throws without a direction', function() {
        expect(function() {
            OrientedBoundingBox.computePlaneDistances(new OrientedBoundingBox(), new Cartesian3(), undefined);
        }).toThrowDeveloperError();
    });

    it('isOccluded', function() {
        var occluderSphere = new BoundingSphere(new Cartesian3(0, 0, -1.5), 0.5);
        var occluder = new Occluder(occluderSphere, Cartesian3.ZERO);

        var radius = 0.25 / Math.sqrt(2.0);
        var halfAxes = Matrix3.multiplyByScale(Matrix3.IDENTITY, new Cartesian3(radius, radius, radius), new Matrix3());
        var obb = new OrientedBoundingBox(new Cartesian3(0, 0, -2.75), halfAxes);
        expect(obb.isOccluded(occluder)).toEqual(true);

        occluderSphere = new BoundingSphere(new Cartesian3(0, 0, -2.75), 0.25);
        occluder = new Occluder(occluderSphere, Cartesian3.ZERO);

        radius = 0.5 / Math.sqrt(2.0);
        halfAxes = Matrix3.multiplyByScale(Matrix3.IDENTITY, new Cartesian3(radius, radius, radius), new Matrix3());
        obb = new OrientedBoundingBox(new Cartesian3(0, 0, -1.5), halfAxes);
        expect(obb.isOccluded(occluder)).toEqual(false);
    });

    it('isOccluded throws without a box', function() {
        expect(function() {
            OrientedBoundingBox.isOccluded(undefined, new Occluder(new BoundingSphere(), new Cartesian3()));
        }).toThrowDeveloperError();
    });

    it('isOccluded throws without a occluder', function() {
        expect(function() {
            OrientedBoundingBox.isOccluded(new OrientedBoundingBox(), undefined);
        }).toThrowDeveloperError();
    });

    it('clone without a result parameter', function() {
        var box = new OrientedBoundingBox();
        var result = OrientedBoundingBox.clone(box);
        expect(box).not.toBe(result);
        expect(box).toEqual(result);
        expect(box.clone()).toEqual(box);
    });

    it('clone with a result parameter', function() {
        var box = new OrientedBoundingBox();
        var box2 = new OrientedBoundingBox();
        var result = new OrientedBoundingBox();
        var returnedResult = OrientedBoundingBox.clone(box, result);
        expect(result).toBe(returnedResult);
        expect(box).not.toBe(result);
        expect(box).toEqual(result);
        expect(box.clone(box2)).toBe(box2);
        expect(box.clone(box2)).toEqual(box2);
    });

    it('clone undefined OBB with a result parameter', function() {
        var box = new OrientedBoundingBox();
        expect(OrientedBoundingBox.clone(undefined, box)).toBe(undefined);
    });

    it('clone undefined OBB without a result parameter', function() {
        expect(OrientedBoundingBox.clone(undefined)).toBe(undefined);
    });

    it('equals works in all cases', function() {
        var box = new OrientedBoundingBox();
        expect(box.equals(new OrientedBoundingBox())).toEqual(true);
        expect(box.equals(undefined)).toEqual(false);
    });
});
