-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement/test new OrientedBoundingBox
- Loading branch information
Showing
2 changed files
with
767 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,331 @@ | ||
/*global define*/ | ||
define([ | ||
'./Cartesian3', | ||
'./defaultValue', | ||
'./defined', | ||
'./DeveloperError', | ||
'./Intersect', | ||
'./Matrix3' | ||
], function( | ||
Cartesian3, | ||
defaultValue, | ||
defined, | ||
DeveloperError, | ||
Intersect, | ||
Matrix3) { | ||
"use strict"; | ||
|
||
/** | ||
* Creates an instance of an OrientedBoundingBox. | ||
* An OrientedBoundingBox model of an object or set of objects, is a closed volume (a cuboid), which completely contains the object or the set of objects. | ||
* It is oriented, so it can provide an optimum fit, it can bound more tightly. | ||
* @alias OrientedBoundingBox | ||
* @constructor | ||
* | ||
* @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box. | ||
* @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box. | ||
* Equivalently, the transformation matrix, to rotate and scale a 2x2x2 | ||
* cube centered at the origin. | ||
* | ||
* @see OrientedBoundingBox.fromPoints | ||
* @see OrientedBoundingBox.fromBoundingRectangle | ||
* @see BoundingSphere | ||
* @see BoundingRectangle | ||
* @see ObjectOrientedBoundingBox | ||
* | ||
* @example | ||
* // Create an OrientedBoundingBox using a transformation matrix, a position where the box will be translated, and a scale. | ||
* var center = new Cesium.Cartesian3(1,0,0); | ||
* var halfAxes = Cesium.Matrix3.clone(Cesium.Matrix3.fromScale(new Cartesian3(1.0, 3.0, 2.0))); | ||
* | ||
* var obb = new Cesium.OrientedBoundingBox(center, halfAxes); | ||
*/ | ||
var OrientedBoundingBox = function(center, halfAxes) { | ||
/** | ||
* The center of the box. | ||
* @type {Cartesian3} | ||
* @default {@link Cartesian3.ZERO} | ||
*/ | ||
this.center = Cartesian3.clone(defaultValue(center, Cartesian3.ZERO)); | ||
/** | ||
* The transformation matrix, to rotate the box to the right position. | ||
* @type {Matrix3} | ||
* @default {@link Matrix3.IDENTITY} | ||
*/ | ||
this.halfAxes = Matrix3.clone(defaultValue(halfAxes, Matrix3.ZERO)); | ||
}; | ||
|
||
var scratchCartesian1 = new Cartesian3(); | ||
var scratchCartesian2 = new Cartesian3(); | ||
var scratchCartesian3 = new Cartesian3(); | ||
var scratchCartesian4 = new Cartesian3(); | ||
var scratchCartesian5 = new Cartesian3(); | ||
var scratchMatrix1 = new Matrix3(); | ||
var scratchRotation = new Matrix3(); | ||
var scratchCovarianceResult = new Matrix3(); | ||
var scratchEigenResult = { | ||
unitary : new Matrix3(), | ||
diagonal : new Matrix3() | ||
}; | ||
|
||
/** | ||
* Computes an instance of an OrientedBoundingBox of the given positions. | ||
* This is an implementation of Stefan Gottschalk's Collision Queries using Oriented Bounding Boxes solution (PHD thesis). | ||
* Reference: http://gamma.cs.unc.edu/users/gottschalk/main.pdf | ||
* | ||
* @param {Cartesian3[]} positions List of {@link Cartesian3} points that the bounding box will enclose. | ||
* @param {OrientedBoundingBox} [result] The object onto which to store the result. | ||
* @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided. | ||
* | ||
* @example | ||
* // Compute an object oriented bounding box enclosing two points. | ||
* var box = Cesium.OrientedBoundingBox.fromPoints([new Cesium.Cartesian3(2, 0, 0), new Cesium.Cartesian3(-2, 0, 0)]); | ||
*/ | ||
OrientedBoundingBox.fromPoints = function(positions, result) { | ||
if (!defined(result)) { | ||
result = new OrientedBoundingBox(); | ||
} | ||
|
||
if (!defined(positions) || positions.length === 0) { | ||
Cartesian3.clone(Cartesian3.ZERO, result.center); | ||
Matrix3.clone(Matrix3.ZERO, result.halfAxes); | ||
return result; | ||
} | ||
|
||
var i; | ||
var length = positions.length; | ||
|
||
var meanPoint = Cartesian3.clone(positions[0], scratchCartesian1); | ||
for (i = 1; i < length; i++) { | ||
Cartesian3.add(meanPoint, positions[i], meanPoint); | ||
} | ||
var invLength = 1.0 / length; | ||
Cartesian3.multiplyByScalar(meanPoint, invLength, meanPoint); | ||
|
||
var exx = 0.0; | ||
var exy = 0.0; | ||
var exz = 0.0; | ||
var eyy = 0.0; | ||
var eyz = 0.0; | ||
var ezz = 0.0; | ||
var p; | ||
|
||
for (i = 0; i < length; i++) { | ||
p = Cartesian3.subtract(positions[i], meanPoint, scratchCartesian2); | ||
exx += p.x * p.x; | ||
exy += p.x * p.y; | ||
exz += p.x * p.z; | ||
eyy += p.y * p.y; | ||
eyz += p.y * p.z; | ||
ezz += p.z * p.z; | ||
} | ||
|
||
exx *= invLength; | ||
exy *= invLength; | ||
exz *= invLength; | ||
eyy *= invLength; | ||
eyz *= invLength; | ||
ezz *= invLength; | ||
|
||
var covarianceMatrix = scratchCovarianceResult; | ||
covarianceMatrix[0] = exx; | ||
covarianceMatrix[1] = exy; | ||
covarianceMatrix[2] = exz; | ||
covarianceMatrix[3] = exy; | ||
covarianceMatrix[4] = eyy; | ||
covarianceMatrix[5] = eyz; | ||
covarianceMatrix[6] = exz; | ||
covarianceMatrix[7] = eyz; | ||
covarianceMatrix[8] = ezz; | ||
|
||
var eigenDecomposition = Matrix3.computeEigenDecomposition(covarianceMatrix, scratchEigenResult); | ||
var rotation = Matrix3.transpose(eigenDecomposition.unitary, scratchRotation); | ||
|
||
p = Cartesian3.subtract(positions[0], meanPoint, scratchCartesian2); | ||
var tempPoint = Matrix3.multiplyByVector(rotation, p, scratchCartesian3); | ||
var maxPoint = Cartesian3.clone(tempPoint, scratchCartesian4); | ||
var minPoint = Cartesian3.clone(tempPoint, scratchCartesian5); | ||
|
||
for (i = 1; i < length; i++) { | ||
p = Cartesian3.subtract(positions[i], meanPoint, p); | ||
Matrix3.multiplyByVector(rotation, p, tempPoint); | ||
Cartesian3.minimumByComponent(minPoint, tempPoint, minPoint); | ||
Cartesian3.maximumByComponent(maxPoint, tempPoint, maxPoint); | ||
} | ||
|
||
var center = Cartesian3.add(minPoint, maxPoint, scratchCartesian3); | ||
Cartesian3.multiplyByScalar(center, 0.5, center); | ||
Matrix3.multiplyByVector(rotation, center, center); | ||
Cartesian3.add(meanPoint, center, result.center); | ||
|
||
var scale = Cartesian3.subtract(maxPoint, minPoint, scratchCartesian3); | ||
Cartesian3.multiplyByScalar(scale, 0.5, scale); | ||
var scaleMat = Matrix3.fromScale(scale, scratchMatrix1); | ||
|
||
Matrix3.multiply(rotation, scaleMat, result.halfAxes); | ||
|
||
return result; | ||
}; | ||
|
||
/** | ||
* Computes an OrientedBoundingBox from a BoundingRectangle. | ||
* The BoundingRectangle is placed on the XY plane. | ||
* | ||
* @param {BoundingRectangle} boundingRectangle A bounding rectangle. | ||
* @param {Number} [rotation=0.0] The rotation of the bounding box in radians. | ||
* @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided. | ||
* | ||
* @example | ||
* // Compute an object oriented bounding box enclosing two points. | ||
* var box = Cesium.OrientedBoundingBox.fromBoundingRectangle(boundingRectangle, 0.0); | ||
*/ | ||
OrientedBoundingBox.fromBoundingRectangle = function(boundingRectangle, rotation, result) { | ||
//>>includeStart('debug', pragmas.debug); | ||
if (!defined(boundingRectangle)) { | ||
throw new DeveloperError('boundingRectangle is required'); | ||
} | ||
//>>includeEnd('debug'); | ||
|
||
if (!defined(result)) { | ||
result = new OrientedBoundingBox(); | ||
} | ||
|
||
var rotMat; | ||
if (defined(rotation)) { | ||
rotMat = Matrix3.fromRotationZ(rotation, scratchRotation); | ||
} else { | ||
rotMat = Matrix3.clone(Matrix3.IDENTITY, scratchRotation); | ||
} | ||
|
||
var scale = scratchCartesian1; | ||
scale.x = boundingRectangle.width * 0.5; | ||
scale.y = boundingRectangle.height * 0.5; | ||
scale.z = 0.0; | ||
var scaleMat = Matrix3.fromScale(scale, scratchMatrix1); | ||
Matrix3.multiply(rotMat, scaleMat, result.halfAxes); | ||
|
||
var translation = Matrix3.multiplyByVector(rotMat, scale, result.center); | ||
translation.x += boundingRectangle.x; | ||
translation.y += boundingRectangle.y; | ||
|
||
return result; | ||
}; | ||
|
||
/** | ||
* Duplicates a OrientedBoundingBox instance. | ||
* | ||
* @param {OrientedBoundingBox} box The bounding box to duplicate. | ||
* @param {OrientedBoundingBox} [result] The object onto which to store the result. | ||
* @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided. (Returns undefined if box is undefined) | ||
*/ | ||
OrientedBoundingBox.clone = function(box, result) { | ||
if (!defined(box)) { | ||
return undefined; | ||
} | ||
|
||
if (!defined(result)) { | ||
return new OrientedBoundingBox(box.center, box.halfAxes); | ||
} | ||
|
||
Cartesian3.clone(box.center, result.center); | ||
Matrix3.clone(box.halfAxes, result.halfAxes); | ||
|
||
return result; | ||
}; | ||
|
||
/** | ||
* Determines which side of a plane the oriented bounding box is located. | ||
* | ||
* @param {OrientedBoundingBox} box The oriented bounding box to test. | ||
* @param {Cartesian4} plane The coefficients of the plane in Hessian Normal Form, as `ax + by + cz + d = 0`, | ||
* where (a, b, c) must be a unit normal vector. | ||
* The coefficients a, b, c, and d are the components x, y, z, | ||
* and w of the {@link Cartesian4}, respectively. | ||
* @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane | ||
* the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is | ||
* on the opposite side, and {@link Intersect.INTERSECTING} if the box | ||
* intersects the plane. | ||
*/ | ||
OrientedBoundingBox.intersectPlane = function(box, plane) { | ||
//>>includeStart('debug', pragmas.debug); | ||
if (!defined(box)) { | ||
throw new DeveloperError('box is required.'); | ||
} | ||
|
||
if (!defined(plane)) { | ||
throw new DeveloperError('plane is required.'); | ||
} | ||
//>>includeEnd('debug'); | ||
|
||
var center = box.center; | ||
// plane is used as if it is its normal; the first three components are assumed to be normalized | ||
var radEffective = Math.abs(Cartesian3.dot(plane, Matrix3.getColumn(box.halfAxes, 0, scratchCartesian1))) + | ||
Math.abs(Cartesian3.dot(plane, Matrix3.getColumn(box.halfAxes, 1, scratchCartesian2))) + | ||
Math.abs(Cartesian3.dot(plane, Matrix3.getColumn(box.halfAxes, 2, scratchCartesian3))); | ||
var distanceToPlane = Cartesian3.dot(plane, center) + plane.w; | ||
|
||
if (distanceToPlane <= -radEffective) { | ||
// The entire box is on the negative side of the plane normal | ||
return Intersect.OUTSIDE; | ||
} else if (distanceToPlane >= radEffective) { | ||
// The entire box is on the positive side of the plane normal | ||
return Intersect.INSIDE; | ||
} | ||
return Intersect.INTERSECTING; | ||
}; | ||
|
||
/** | ||
* Determines which side of a plane the oriented bounding box is located. | ||
* | ||
* @param {Cartesian4} plane The coefficients of the plane in Hessian Normal Form, as `ax + by + cz + d = 0`, | ||
* where (a, b, c) must be a unit normal vector. | ||
* The coefficients a, b, c, and d are the components x, y, z, | ||
* and w of the {@link Cartesian4}, respectively. | ||
* @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane | ||
* the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is | ||
* on the opposite side, and {@link Intersect.INTERSECTING} if the box | ||
* intersects the plane. | ||
*/ | ||
OrientedBoundingBox.prototype.intersectPlane = function(plane) { | ||
return OrientedBoundingBox.intersectPlane(this, plane); | ||
}; | ||
|
||
/** | ||
* Compares the provided OrientedBoundingBox componentwise and returns | ||
* <code>true</code> if they are equal, <code>false</code> otherwise. | ||
* | ||
* @param {OrientedBoundingBox} left The first OrientedBoundingBox. | ||
* @param {OrientedBoundingBox} right The second OrientedBoundingBox. | ||
* @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. | ||
*/ | ||
OrientedBoundingBox.equals = function(left, right) { | ||
return (left === right) || | ||
((defined(left)) && | ||
(defined(right)) && | ||
Cartesian3.equals(left.center, right.center) && | ||
Matrix3.equals(left.halfAxes, right.halfAxes)); | ||
}; | ||
|
||
/** | ||
* Duplicates this OrientedBoundingBox instance. | ||
* | ||
* @param {OrientedBoundingBox} [result] The object onto which to store the result. | ||
* @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided. | ||
*/ | ||
OrientedBoundingBox.prototype.clone = function(result) { | ||
return OrientedBoundingBox.clone(this, result); | ||
}; | ||
|
||
/** | ||
* Compares this OrientedBoundingBox against the provided OrientedBoundingBox componentwise and returns | ||
* <code>true</code> if they are equal, <code>false</code> otherwise. | ||
* | ||
* @param {OrientedBoundingBox} [right] The right hand side OrientedBoundingBox. | ||
* @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. | ||
*/ | ||
OrientedBoundingBox.prototype.equals = function(right) { | ||
return OrientedBoundingBox.equals(this, right); | ||
}; | ||
|
||
return OrientedBoundingBox; | ||
}); |
Oops, something went wrong.