From d44d57baffbb721c01a6e67db891d98540ce7747 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 29 Aug 2024 13:28:10 +0200 Subject: [PATCH 1/4] Added fromMinMax and transform for OBB --- .../engine/Source/Core/OrientedBoundingBox.js | 90 +++++++++++++ .../Specs/Core/OrientedBoundingBoxSpec.js | 125 ++++++++++++++++++ 2 files changed, 215 insertions(+) diff --git a/packages/engine/Source/Core/OrientedBoundingBox.js b/packages/engine/Source/Core/OrientedBoundingBox.js index c94354adfc3d..73b394193d90 100644 --- a/packages/engine/Source/Core/OrientedBoundingBox.js +++ b/packages/engine/Source/Core/OrientedBoundingBox.js @@ -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( diff --git a/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js b/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js index a424c6561c35..875981d49356 100644 --- a/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js +++ b/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable no-restricted-globals */ import { BoundingSphere, Cartesian3, @@ -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); From f60e7e2d45734cfcb95e9bdaa6a9018c9037f12d Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 31 Aug 2024 19:12:18 +0200 Subject: [PATCH 2/4] Add intersection test for OBB --- .../engine/Source/Core/OrientedBoundingBox.js | 225 ++++++++++++++++++ .../Specs/Core/OrientedBoundingBoxSpec.js | 203 ++++++++++++++++ 2 files changed, 428 insertions(+) diff --git a/packages/engine/Source/Core/OrientedBoundingBox.js b/packages/engine/Source/Core/OrientedBoundingBox.js index 73b394193d90..7bc543996826 100644 --- a/packages/engine/Source/Core/OrientedBoundingBox.js +++ b/packages/engine/Source/Core/OrientedBoundingBox.js @@ -330,6 +330,231 @@ OrientedBoundingBox.prototype.transform = function (transform, result) { return OrientedBoundingBox.transform(this, transform, result); }; +/** + * Computes the range that is covered by projecting the given points + * on the given axis. + * + * This will project all points on the given axis, compute the + * minimum- and maximum position of the projected points along + * this axis, store them as the `start`/`stop` of the given + * interval, and return the interval. + * + * If the given interval is `undefined`, then a new interval + * will be created, filled, and returned. + * + * (The axis will usually have unit length) + * + * @param {Cartesian3} axis The axis + * @param {Cartesian3[]} points The points + * @param {Interval} [result] The interval that will store the result + * @returns The result + */ +function computeProjectedRange(axis, points, result) { + let min = Number.MAX_VALUE; + let max = -Number.MAX_VALUE; + for (let i = 0; i < points.length; i++) { + const dot = Cartesian3.dot(points[i], axis); + min = Math.min(min, dot); + max = Math.max(max, dot); + } + if (!defined(result)) { + return new Interval(min, max); + } + result.start = min; + result.stop = max; + return result; +} + +// Scratch intervals for `areSeparatedAlongAxis` +const scratchIntervalA = new Interval(); +const scratchIntervalB = new Interval(); + +/** + * Returns whether the projections of the given points on the given + * axis are non-overlapping. + * + * This method will return `true` when the points are "touching" - + * i.e. it returns `true` if and only if they are really separated. + * + * The axis will usually have unit length (but does not need to). + * If the given axis has a length that is epsilon-equal to zero, + * then `false` is returned. + * + * @param {Cartesian3} axis The axis + * @param {Cartesian3[]} pointsA The first set of points + * @param {Cartesian3[]} pointsB The second set of points + * @returns Whether the projections are separated + */ +function areSeparatedAlongAxis(axis, pointsA, pointsB) { + // See https://gamma.cs.unc.edu/users/gottschalk/main.pdf section 4.3 + if (Cartesian3.equalsEpsilon(axis, Cartesian3.ZERO, Math.EPSILON10)) { + return false; + } + const rangeA = computeProjectedRange(axis, pointsA, scratchIntervalA); + const rangeB = computeProjectedRange(axis, pointsB, scratchIntervalB); + if (rangeA.start > rangeB.stop) { + return true; + } + if (rangeA.stop < rangeB.start) { + return true; + } + return false; +} + +// Scratch axes for OBB a in `intersect` +const scratchAx = new Cartesian3(); +const scratchAy = new Cartesian3(); +const scratchAz = new Cartesian3(); + +// Scratch axes for OBB B in `intersect` +const scratchBx = new Cartesian3(); +const scratchBy = new Cartesian3(); +const scratchBz = new Cartesian3(); + +// Scratch axis for cross products in `intersect` +const scratchCrossAxis = new Cartesian3(); + +// Scratch corners for OBB A in `intersect` +const scratchCornersA = [ + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), +]; + +// Scratch corners for OBB B in `intersect` +const scratchCornersB = [ + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), + new Cartesian3(), +]; + +/** + * Returns whether the given oriented bounding boxes are intersecting. + * + * This returns `true` when the given bounding boxes are intersecting, + * which includes the case that they are only "touching". + * + * @param {OrientedBoundingBox} obbA The first oriented bounding box + * @param {OrientedBoundingBox} obbB The second oriented bounding box + * @returns Whether the bounding boxes are intersecting + */ +OrientedBoundingBox.intersect = function (obbA, obbB) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("obbA", obbA); + Check.typeOf.object("obbB", obbB); + //>>includeEnd('debug'); + + const cornersA = OrientedBoundingBox.computeCorners(obbA, scratchCornersA); + const cornersB = OrientedBoundingBox.computeCorners(obbB, scratchCornersB); + + // For a list of the axes that have to be checked, see table 1 of + // https://www.geometrictools.com/Documentation/DynamicCollisionDetection.pdf + + // Checks along the main axes (matrix columns) + + const Ax = Matrix3.getColumn(obbA.halfAxes, 0, scratchAx); + if (areSeparatedAlongAxis(Ax, cornersA, cornersB)) { + return false; + } + + const Ay = Matrix3.getColumn(obbA.halfAxes, 1, scratchAy); + if (areSeparatedAlongAxis(Ay, cornersA, cornersB)) { + return false; + } + + const Az = Matrix3.getColumn(obbA.halfAxes, 2, scratchAz); + if (areSeparatedAlongAxis(Az, cornersA, cornersB)) { + return false; + } + + const Bx = Matrix3.getColumn(obbB.halfAxes, 0, scratchBx); + if (areSeparatedAlongAxis(Bx, cornersA, cornersB)) { + return false; + } + + const By = Matrix3.getColumn(obbB.halfAxes, 1, scratchBy); + if (areSeparatedAlongAxis(By, cornersA, cornersB)) { + return false; + } + + const Bz = Matrix3.getColumn(obbB.halfAxes, 2, scratchBz); + if (areSeparatedAlongAxis(Bz, cornersA, cornersB)) { + return false; + } + + // Checks along the cross products of the main axes + + const crossAxBx = Cartesian3.cross(Ax, Bx, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAxBx, cornersA, cornersB)) { + return false; + } + + const crossAxBy = Cartesian3.cross(Ax, By, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAxBy, cornersA, cornersB)) { + return false; + } + + const crossAxBz = Cartesian3.cross(Ax, Bz, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAxBz, cornersA, cornersB)) { + return false; + } + + const crossAyBx = Cartesian3.cross(Ay, Bx, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAyBx, cornersA, cornersB)) { + return false; + } + + const crossAyBy = Cartesian3.cross(Ay, By, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAyBy, cornersA, cornersB)) { + return false; + } + + const crossAyBz = Cartesian3.cross(Ay, Bz, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAyBz, cornersA, cornersB)) { + return false; + } + + const crossAzBx = Cartesian3.cross(Az, Bx, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAzBx, cornersA, cornersB)) { + return false; + } + + const crossAzBy = Cartesian3.cross(Az, By, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAzBy, cornersA, cornersB)) { + return false; + } + + const crossAzBz = Cartesian3.cross(Az, Bz, scratchCrossAxis); + if (areSeparatedAlongAxis(crossAzBz, cornersA, cornersB)) { + return false; + } + + return true; +}; + +/** + * Returns whether this oriented bounding box intersects the given one. + * + * This returns `true` when the bounding boxes are intersecting, + * which includes the case that they are only "touching". + * + * @param {OrientedBoundingBox} other The other oriented bounding box + * @returns Whether the bounding boxes are intersecting + */ +OrientedBoundingBox.prototype.intersect = function (other) { + return OrientedBoundingBox.intersect(this, other); +}; + const scratchOffset = new Cartesian3(); const scratchScale = new Cartesian3(); function fromPlaneExtents( diff --git a/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js b/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js index 875981d49356..295ff8576513 100644 --- a/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js +++ b/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js @@ -17,6 +17,51 @@ import { import createPackableSpecs from "../../../../Specs/createPackableSpecs.js"; +/** + * Creates an oriented bounding box for the intersection tests. + * + * This creates an oriented bounding box with the given size that + * is rotated around x, y, and z by the given angles (in degrees), + * and translated according to the given deltas. + * + * @param {number} sizeX The size in x-direction + * @param {number} sizeY The size in y-direction + * @param {number} sizeZ The size in z-direction + * @param {number} deltaX The delta in x-direction + * @param {number} deltaY The delta in y-direction + * @param {number} deltaZ The delta in z-direction + * @param {number} angleDegX The angle around X + * @param {number} angleDegY The angle around Y + * @param {number} angleDegZ The angle around Z + * @returns The oriented bounding box + */ +function createObb( + sizeX, + sizeY, + sizeZ, + deltaX, + deltaY, + deltaZ, + angleDegX, + angleDegY, + angleDegZ +) { + const center = new Cartesian3(deltaX, deltaY, deltaZ); + const halfAxes = Matrix3.clone(Matrix3.IDENTITY); + const rotX = Matrix3.fromRotationX(CesiumMath.toRadians(angleDegX)); + const rotY = Matrix3.fromRotationX(CesiumMath.toRadians(angleDegY)); + const rotZ = Matrix3.fromRotationX(CesiumMath.toRadians(angleDegZ)); + const scale = Matrix3.fromScale(new Cartesian3(sizeX, sizeY, sizeZ)); + Matrix3.multiply(halfAxes, rotX, halfAxes); + Matrix3.multiply(halfAxes, rotY, halfAxes); + Matrix3.multiply(halfAxes, rotZ, halfAxes); + Matrix3.multiply(halfAxes, scale, halfAxes); + const obb = new OrientedBoundingBox(); + obb.halfAxes = halfAxes; + obb.center = center; + return obb; +} + describe("Core/OrientedBoundingBox", function () { const positions = [ new Cartesian3(2.0, 0.0, 0.0), @@ -309,6 +354,164 @@ describe("Core/OrientedBoundingBox", function () { ); }); + fit("intersect throws without first argument", function () { + expect(function () { + const obbB = new OrientedBoundingBox(); + OrientedBoundingBox.intersect(undefined, obbB); + }).toThrowDeveloperError(); + }); + + fit("intersect throws without first argument", function () { + expect(function () { + const obbA = new OrientedBoundingBox(); + OrientedBoundingBox.intersect(obbA, undefined); + }).toThrowDeveloperError(); + }); + + fit("intersect detects separation along A0", function () { + const obbA = createObb(10, 20, 30, 100, 0, 0, 0, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects touching along A0", function () { + const obbA = createObb(10, 20, 30, 20, 0, 0, 0, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeTrue(); + }); + + fit("intersect detects separation along A1", function () { + const obbA = createObb(10, 20, 30, 0, 100, 0, 0, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects touching along A1", function () { + const obbA = createObb(10, 20, 30, 0, 40, 0, 0, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeTrue(); + }); + + fit("intersect detects separation along A2", function () { + const obbA = createObb(10, 20, 30, 0, 0, 100, 0, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects touching along A2", function () { + const obbA = createObb(10, 20, 30, 0, 0, 60, 0, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeTrue(); + }); + + fit("intersect detects separation along Bx", function () { + const obbA = createObb(10, 20, 30, 44, 0, 0, 0, 45, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along By", function () { + const obbA = createObb(10, 20, 30, 0, 58, 0, 45, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along Bz", function () { + const obbA = createObb(10, 20, 30, 0, 0, 72, 44, 0, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AxBx", function () { + const obbA = createObb(10, 20, 30, 0, 53, 40, 135, 135, 45); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AxBy", function () { + const obbA = createObb(10, 20, 30, 0, 0, 62, 45, 45, 47); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AxBz", function () { + const obbA = createObb(10, 20, 30, 0, 53, 35, 135, 45, 45); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AyBx", function () { + const obbA = createObb(10, 20, 30, 0, 40, 40, 45, 90, 135); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AyBy", function () { + const obbA = createObb(10, 20, 30, 35, 0, 56, 45, 45, 45); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AyBz", function () { + const obbA = createObb(10, 20, 30, 39, 39, 0, 45, 135, 136); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AzBx", function () { + const obbA = createObb(10, 20, 30, 0, 40, 44, 45, 45, 136); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AzBy", function () { + const obbA = createObb(10, 20, 30, 0, 0, 62, 45, 45, 0); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + + fit("intersect detects separation along AzBz", function () { + const obbA = createObb(10, 20, 30, 23, 28, 0, 45, 45, 45); + const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); + + const actual = OrientedBoundingBox.intersect(obbA, obbB); + expect(actual).toBeFalse(); + }); + 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); From 1ef07bec3ca43f1d511c3cb2a50cb7b153db7a96 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sun, 1 Sep 2024 16:43:29 +0200 Subject: [PATCH 3/4] Remove fit from specs (and fix copy-and-paste error) --- .../Specs/Core/OrientedBoundingBoxSpec.js | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js b/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js index 295ff8576513..75c84649493a 100644 --- a/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js +++ b/packages/engine/Specs/Core/OrientedBoundingBoxSpec.js @@ -1,4 +1,3 @@ -/* eslint-disable no-restricted-globals */ import { BoundingSphere, Cartesian3, @@ -49,8 +48,8 @@ function createObb( const center = new Cartesian3(deltaX, deltaY, deltaZ); const halfAxes = Matrix3.clone(Matrix3.IDENTITY); const rotX = Matrix3.fromRotationX(CesiumMath.toRadians(angleDegX)); - const rotY = Matrix3.fromRotationX(CesiumMath.toRadians(angleDegY)); - const rotZ = Matrix3.fromRotationX(CesiumMath.toRadians(angleDegZ)); + const rotY = Matrix3.fromRotationY(CesiumMath.toRadians(angleDegY)); + const rotZ = Matrix3.fromRotationZ(CesiumMath.toRadians(angleDegZ)); const scale = Matrix3.fromScale(new Cartesian3(sizeX, sizeY, sizeZ)); Matrix3.multiply(halfAxes, rotX, halfAxes); Matrix3.multiply(halfAxes, rotY, halfAxes); @@ -230,7 +229,7 @@ describe("Core/OrientedBoundingBox", function () { expect(box.center).toEqualEpsilon(translation, CesiumMath.EPSILON15); }); - fit("fromMinMax creates the right bounding box", function () { + it("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); @@ -244,13 +243,13 @@ describe("Core/OrientedBoundingBox", function () { expect(box.halfAxes).toEqualEpsilon(halfAxes, CesiumMath.EPSILON15); }); - fit("fromMinMax throws without min/max", function () { + it("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 () { + it("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(); @@ -267,7 +266,7 @@ describe("Core/OrientedBoundingBox", function () { expect(box.halfAxes).toEqualEpsilon(halfAxes, CesiumMath.EPSILON15); }); - fit("fromMinMax creates the right bounding box with a result parameter", function () { + it("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(); @@ -284,14 +283,14 @@ describe("Core/OrientedBoundingBox", function () { expect(box.halfAxes).toEqualEpsilon(halfAxes, CesiumMath.EPSILON15); }); - fit("transform throws without transform", function () { + it("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 () { + it("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); @@ -321,7 +320,7 @@ describe("Core/OrientedBoundingBox", function () { ); }); - fit("transform transforms the bounding box with transform with a result parameter", function () { + it("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); @@ -354,21 +353,21 @@ describe("Core/OrientedBoundingBox", function () { ); }); - fit("intersect throws without first argument", function () { + it("intersect throws without first argument", function () { expect(function () { const obbB = new OrientedBoundingBox(); OrientedBoundingBox.intersect(undefined, obbB); }).toThrowDeveloperError(); }); - fit("intersect throws without first argument", function () { + it("intersect throws without first argument", function () { expect(function () { const obbA = new OrientedBoundingBox(); OrientedBoundingBox.intersect(obbA, undefined); }).toThrowDeveloperError(); }); - fit("intersect detects separation along A0", function () { + it("intersect detects separation along A0", function () { const obbA = createObb(10, 20, 30, 100, 0, 0, 0, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -376,7 +375,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects touching along A0", function () { + it("intersect detects touching along A0", function () { const obbA = createObb(10, 20, 30, 20, 0, 0, 0, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -384,7 +383,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeTrue(); }); - fit("intersect detects separation along A1", function () { + it("intersect detects separation along A1", function () { const obbA = createObb(10, 20, 30, 0, 100, 0, 0, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -392,7 +391,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects touching along A1", function () { + it("intersect detects touching along A1", function () { const obbA = createObb(10, 20, 30, 0, 40, 0, 0, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -400,7 +399,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeTrue(); }); - fit("intersect detects separation along A2", function () { + it("intersect detects separation along A2", function () { const obbA = createObb(10, 20, 30, 0, 0, 100, 0, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -408,7 +407,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects touching along A2", function () { + it("intersect detects touching along A2", function () { const obbA = createObb(10, 20, 30, 0, 0, 60, 0, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -416,7 +415,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeTrue(); }); - fit("intersect detects separation along Bx", function () { + it("intersect detects separation along Bx", function () { const obbA = createObb(10, 20, 30, 44, 0, 0, 0, 45, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -424,7 +423,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along By", function () { + it("intersect detects separation along By", function () { const obbA = createObb(10, 20, 30, 0, 58, 0, 45, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -432,7 +431,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along Bz", function () { + it("intersect detects separation along Bz", function () { const obbA = createObb(10, 20, 30, 0, 0, 72, 44, 0, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -440,7 +439,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AxBx", function () { + it("intersect detects separation along AxBx", function () { const obbA = createObb(10, 20, 30, 0, 53, 40, 135, 135, 45); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -448,7 +447,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AxBy", function () { + it("intersect detects separation along AxBy", function () { const obbA = createObb(10, 20, 30, 0, 0, 62, 45, 45, 47); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -456,7 +455,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AxBz", function () { + it("intersect detects separation along AxBz", function () { const obbA = createObb(10, 20, 30, 0, 53, 35, 135, 45, 45); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -464,7 +463,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AyBx", function () { + it("intersect detects separation along AyBx", function () { const obbA = createObb(10, 20, 30, 0, 40, 40, 45, 90, 135); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -472,7 +471,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AyBy", function () { + it("intersect detects separation along AyBy", function () { const obbA = createObb(10, 20, 30, 35, 0, 56, 45, 45, 45); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -480,7 +479,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AyBz", function () { + it("intersect detects separation along AyBz", function () { const obbA = createObb(10, 20, 30, 39, 39, 0, 45, 135, 136); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -488,7 +487,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AzBx", function () { + it("intersect detects separation along AzBx", function () { const obbA = createObb(10, 20, 30, 0, 40, 44, 45, 45, 136); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -496,7 +495,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AzBy", function () { + it("intersect detects separation along AzBy", function () { const obbA = createObb(10, 20, 30, 0, 0, 62, 45, 45, 0); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); @@ -504,7 +503,7 @@ describe("Core/OrientedBoundingBox", function () { expect(actual).toBeFalse(); }); - fit("intersect detects separation along AzBz", function () { + it("intersect detects separation along AzBz", function () { const obbA = createObb(10, 20, 30, 23, 28, 0, 45, 45, 45); const obbB = createObb(10, 20, 30, 0, 0, 0, 0, 0, 0); From 7466d6ddfd56f1c11577222a5abda6017c91095f Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sun, 1 Sep 2024 16:53:48 +0200 Subject: [PATCH 4/4] Fix typo in comment --- packages/engine/Source/Core/OrientedBoundingBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Core/OrientedBoundingBox.js b/packages/engine/Source/Core/OrientedBoundingBox.js index 7bc543996826..d24dfe6938f0 100644 --- a/packages/engine/Source/Core/OrientedBoundingBox.js +++ b/packages/engine/Source/Core/OrientedBoundingBox.js @@ -374,7 +374,7 @@ const scratchIntervalB = new Interval(); * axis are non-overlapping. * * This method will return `true` when the points are "touching" - - * i.e. it returns `true` if and only if they are really separated. + * i.e. it returns `false` if and only if they are really separated. * * The axis will usually have unit length (but does not need to). * If the given axis has a length that is epsilon-equal to zero,