Skip to content

Commit

Permalink
Added fromMinMax and transform for OBB
Browse files Browse the repository at this point in the history
  • Loading branch information
javagl committed Aug 29, 2024
1 parent 088c4a2 commit d44d57b
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 0 deletions.
90 changes: 90 additions & 0 deletions packages/engine/Source/Core/OrientedBoundingBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,96 @@ OrientedBoundingBox.fromPoints = function (positions, result) {
return result;
};

// A Cartesian3 that will store the scale factors for computing
// an oriented bounding box in fromMinMax
const scratchScaleFromMinMax = new Cartesian3();

/**
* Creates an oriented bounding box from the given minimum- and maximum
* point, stores it in the given result, and returns it.
*
* If the given result is `undefined`, then a new oriented bounding box
* will be created, filled, and returned.
*
* @param {Cartesian3} min The minimum point
* @param {Cartesian3} max The maximum point
* @param {OrientedBoundingBox} [result] The result
* @returns The result
*/
OrientedBoundingBox.fromMinMax = function (min, max, result) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("min", min);
Check.typeOf.object("max", max);
//>>includeEnd('debug');

if (!defined(result)) {
result = new OrientedBoundingBox();
}
Cartesian3.midpoint(min, max, result.center);
Cartesian3.subtract(max, min, scratchScaleFromMinMax);
Cartesian3.multiplyByScalar(
scratchScaleFromMinMax,
0.5,
scratchScaleFromMinMax
);
Matrix3.fromScale(scratchScaleFromMinMax, result.halfAxes);
return result;
};

// A Matrix3 that will store the rotation and scale components
// of a transform matrix in transform
const scratchRotationScaleTransform = new Matrix3();

/**
* Transforms the given oriented bounding box with the given matrix,
* stores the result in the given result parameter, and returns it.
*
* If the given result is `undefined`, then a new oriented bounding box
* will be created, filled, and returned.
*
* @param {OrientedBoundingBox} orientedBoundingBox The oriented bounding box
* @param {Matrix4} transform The transform matrix
* @param {OrientedBoundingBox} [result] The result
* @returns The result
*/
OrientedBoundingBox.transform = function (
orientedBoundingBox,
transform,
result
) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("orientedBoundingBox", orientedBoundingBox);
Check.typeOf.object("transform", transform);
//>>includeEnd('debug');

if (!defined(result)) {
result = new OrientedBoundingBox();
}
Matrix4.multiplyByPoint(transform, orientedBoundingBox.center, result.center);
Matrix4.getMatrix3(transform, scratchRotationScaleTransform);
Matrix3.multiply(
scratchRotationScaleTransform,
orientedBoundingBox.halfAxes,
result.halfAxes
);
return result;
};

/**
* Transforms this oriented bounding box with the given matrix,
* stores the result in the given result parameter, and returns it.
*
* If the given result is `undefined`, then a new oriented bounding box
* will be created, filled, and returned.
*
* @param {Matrix4} transform The transform matrix
* @param {OrientedBoundingBox} [result] The result
* @returns The result
*/
OrientedBoundingBox.prototype.transform = function (transform, result) {
return OrientedBoundingBox.transform(this, transform, result);
};

const scratchOffset = new Cartesian3();
const scratchScale = new Cartesian3();
function fromPlaneExtents(
Expand Down
125 changes: 125 additions & 0 deletions packages/engine/Specs/Core/OrientedBoundingBoxSpec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-restricted-globals */
import {
BoundingSphere,
Cartesian3,
Expand Down Expand Up @@ -184,6 +185,130 @@ describe("Core/OrientedBoundingBox", function () {
expect(box.center).toEqualEpsilon(translation, CesiumMath.EPSILON15);
});

fit("fromMinMax creates the right bounding box", function () {
const min = new Cartesian3(-2.0, -3.0, -4.0);
const max = new Cartesian3(2.0, 3.0, 4.0);
const box = OrientedBoundingBox.fromMinMax(min, max);

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

const halfAxes = new Matrix3(2, 0, 0, 0, 3, 0, 0, 0, 4);
expect(box.halfAxes).toEqualEpsilon(halfAxes, CesiumMath.EPSILON15);
});

fit("fromMinMax throws without min/max", function () {
expect(function () {
OrientedBoundingBox.fromMinMax(undefined, undefined);
}).toThrowDeveloperError();
});

fit("fromMinMax creates the right bounding box with a result parameter", function () {
const min = new Cartesian3(-2.0, -3.0, -4.0);
const max = new Cartesian3(2.0, 3.0, 4.0);
const result = new OrientedBoundingBox();
const box = OrientedBoundingBox.fromMinMax(min, max, result);

expect(box).toBe(result);

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

const halfAxes = new Matrix3(2, 0, 0, 0, 3, 0, 0, 0, 4);
expect(box.halfAxes).toEqualEpsilon(halfAxes, CesiumMath.EPSILON15);
});

fit("fromMinMax creates the right bounding box with a result parameter", function () {
const min = new Cartesian3(-2.0, -3.0, -4.0);
const max = new Cartesian3(2.0, 3.0, 4.0);
const result = new OrientedBoundingBox();
const box = OrientedBoundingBox.fromMinMax(min, max, result);

expect(box).toBe(result);

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

const halfAxes = new Matrix3(2, 0, 0, 0, 3, 0, 0, 0, 4);
expect(box.halfAxes).toEqualEpsilon(halfAxes, CesiumMath.EPSILON15);
});

fit("transform throws without transform", function () {
expect(function () {
const box = new OrientedBoundingBox();
OrientedBoundingBox.transform(box, undefined);
}).toThrowDeveloperError();
});

fit("transform transforms the bounding box with transform", function () {
const center = new Cartesian3(1.0, 2.0, 3.0);
const halfAxes = new Matrix3(2, 0, 0, 0, 3, 0, 0, 0, 4);
const box = new OrientedBoundingBox(center, halfAxes);

const rotation = Matrix3.fromRotationX(CesiumMath.PI / 2.0);
const translation = new Cartesian3(2.0, 3.0, 4.0);
const transform = Matrix4.fromRotationTranslation(rotation, translation);

// The rotation transforms the center into (x, -z, y) = (1, -3, 2)
// Adding the translation of (2, 3, 4) results in (3, 0, 6)
const expectedCenter = new Cartesian3(3.0, 0.0, 6.0);
const expectedHalfAxes = new Matrix3(2, 0, 0, 0, 0, -4, 0, 3, 0);
const expectedBox = new OrientedBoundingBox(
expectedCenter,
expectedHalfAxes
);

const actualBox = OrientedBoundingBox.transform(box, transform);

expect(actualBox.center).toEqualEpsilon(
expectedBox.center,
CesiumMath.EPSILON15
);
expect(actualBox.halfAxes).toEqualEpsilon(
expectedBox.halfAxes,
CesiumMath.EPSILON15
);
});

fit("transform transforms the bounding box with transform with a result parameter", function () {
const center = new Cartesian3(1.0, 2.0, 3.0);
const halfAxes = new Matrix3(2, 0, 0, 0, 3, 0, 0, 0, 4);
const box = new OrientedBoundingBox(center, halfAxes);

const rotation = Matrix3.fromRotationX(CesiumMath.PI / 2.0);
const translation = new Cartesian3(2.0, 3.0, 4.0);
const transform = Matrix4.fromRotationTranslation(rotation, translation);

// The rotation transforms the center into (x, -z, y) = (1, -3, 2)
// Adding the translation of (2, 3, 4) results in (3, 0, 6)
const expectedCenter = new Cartesian3(3.0, 0.0, 6.0);
const expectedHalfAxes = new Matrix3(2, 0, 0, 0, 0, -4, 0, 3, 0);
const expectedBox = new OrientedBoundingBox(
expectedCenter,
expectedHalfAxes
);

const result = new OrientedBoundingBox();
const actualBox = OrientedBoundingBox.transform(box, transform, result);

expect(actualBox).toBe(result);

expect(actualBox.center).toEqualEpsilon(
expectedBox.center,
CesiumMath.EPSILON15
);
expect(actualBox.halfAxes).toEqualEpsilon(
expectedBox.halfAxes,
CesiumMath.EPSILON15
);
});

it("fromRectangle sets correct default ellipsoid", function () {
const rectangle = new Rectangle(-0.9, -1.2, 0.5, 0.7);
const box1 = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0);
Expand Down

0 comments on commit d44d57b

Please sign in to comment.