diff --git a/CHANGES.md b/CHANGES.md
index 6c187ed161fc..dc95330bf8e8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,8 +2,11 @@ Change Log
==========
### 1.11 - 2015-07-01
+
* Deprecated
* The STK World Terrain url `cesiumjs.org/stk-terrain/world` has been deprecated, use `assets.agi.com/stk-terrain/world` instead. A redirect will be in place until 1.14.
+ * Deprecated `AxisAlignedBoundingBox.intersect` and `BoundingSphere.intersect`. These will be removed in 1.13. Use `.intersectPlane` and, if necessary, `Plane.fromCartesian4`.
+ * Deprecated the `ObjectOrientedBoundingBox` class. It will be removed in 1.12. Use `OrientedBoundingBox` instead.
* Improved the algorithm that `Camera.viewRectangle` uses to select the position of the camera, so that the specified rectangle is now better centered on the screen [#2764](https://github.com/AnalyticalGraphicsInc/cesium/issues/2764).
* The performance statistics displayed by setting `scene.debugShowFramesPerSecond` to `true` can now be styled using the `cesium-performanceDisplay` CSS classes in `shared.css` [#2779](https://github.com/AnalyticalGraphicsInc/cesium/issues/2779).
* Fixed a crash when `viewer.zoomTo` or `viewer.flyTo` were called immediately before or during a scene morph [#2775](https://github.com/AnalyticalGraphicsInc/cesium/issues/2775).
@@ -14,6 +17,17 @@ Change Log
* The camera now zooms to the point under the mouse cursor.
* Fixed a bug in `ImageryLayer` that could cause an exception and the render loop to stop when the base layer did not cover the entire globe.
* Fixed flash/streak rendering artifacts when picking [#2790](https://github.com/AnalyticalGraphicsInc/cesium/issues/2790), [#2811](https://github.com/AnalyticalGraphicsInc/cesium/issues/2811).
+* Added `Plane.fromCartesian4` to convert old `Cartesian4` plane representations to the new `Plane` format.
+* Added `Plane.ORIGIN_XY_PLANE`/`ORIGIN_YZ_PLANE`/`ORIGIN_ZX_PLANE` constants for commonly-used planes.
+* Added `Matrix2`/`Matrix3`/`Matrix4.ZERO` constants for zero matrices.
+* Added `Matrix2`/`Matrix3.multiplyByScale` for multiplying against non-uniform scales.
+* Added `projectPointToNearestOnPlane` and `projectPointsToNearestOnPlane` to `EllipsoidTangentPlane` to project 3D points to the nearest 2D point on an `EllipsoidTangentPlane`.
+* Added `OrientedBoundingBox` class.
+* Added `EllipsoidTangentPlane.plane` property to get the `Plane` for the tangent plane.
+* Added `EllipsoidTangentPlane.xAxis`/`yAxis`/`zAxis` properties to get the local coordinate system of the tangent plane.
+* Add `QuantizedMeshTerrainData` constructor argument `orientedBoundingBox`.
+* Add `TerrainMesh.orientedBoundingBox` which holds the `OrientedBoundingBox` for the mesh for a single terrain tile.
+* Use `OrientedBoundingBox` when rendering terrain and imagery to improve performance of rendering and loading (by up to 50% of terrain/imagery tiles, depending on camera view).
### 1.10 - 2015-06-01
diff --git a/Source/Core/AxisAlignedBoundingBox.js b/Source/Core/AxisAlignedBoundingBox.js
index 072688aaa364..a169484752b5 100644
--- a/Source/Core/AxisAlignedBoundingBox.js
+++ b/Source/Core/AxisAlignedBoundingBox.js
@@ -3,14 +3,18 @@ define([
'./Cartesian3',
'./defaultValue',
'./defined',
+ './deprecationWarning',
'./DeveloperError',
- './Intersect'
+ './Intersect',
+ './Plane'
], function(
Cartesian3,
defaultValue,
defined,
+ deprecationWarning,
DeveloperError,
- Intersect) {
+ Intersect,
+ Plane) {
"use strict";
/**
@@ -162,15 +166,13 @@ define([
* Determines which side of a plane a box is located.
*
* @param {AxisAlignedBoundingBox} box The bounding box to test.
- * @param {Cartesian4} plane The coefficients of the plane in the form ax + by + cz + d = 0
- * where the coefficients a, b, c, and d are the components x, y, z, and w
- * of the {@link Cartesian4}, respectively.
+ * @param {Plane} plane The plane to test against.
* @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.
*/
- AxisAlignedBoundingBox.intersect = function(box, plane) {
+ AxisAlignedBoundingBox.intersectPlane = function(box, plane) {
//>>includeStart('debug', pragmas.debug);
if (!defined(box)) {
throw new DeveloperError('box is required.');
@@ -182,8 +184,9 @@ define([
intersectScratch = Cartesian3.subtract(box.maximum, box.minimum, intersectScratch);
var h = Cartesian3.multiplyByScalar(intersectScratch, 0.5, intersectScratch); //The positive half diagonal
- var e = h.x * Math.abs(plane.x) + h.y * Math.abs(plane.y) + h.z * Math.abs(plane.z);
- var s = Cartesian3.dot(box.center, plane) + plane.w; //signed distance from center
+ var normal = plane.normal;
+ var e = h.x * Math.abs(normal.x) + h.y * Math.abs(normal.y) + h.z * Math.abs(normal.z);
+ var s = Cartesian3.dot(box.center, normal) + plane.distance; //signed distance from center
if (s - e > 0) {
return Intersect.INSIDE;
@@ -197,6 +200,26 @@ define([
return Intersect.INTERSECTING;
};
+ var scratchPlane = new Plane(new Cartesian3(), 0.0);
+ /**
+ * Determines which side of a plane a box is located.
+ *
+ * @deprecated
+ * @param {AxisAlignedBoundingBox} box The bounding box to test.
+ * @param {Cartesian4} plane The coefficients of the plane in the form ax + by + cz + d = 0
+ * where 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.
+ */
+ AxisAlignedBoundingBox.intersect = function(box, plane) {
+ deprecationWarning('AxisAlignedBoundingBox.intersect', 'AxisAlignedBoundingBox.intersect() was deprecated in Cesium 1.11. It will be removed in 1.12. Use AxisAlignedBoundingBox.intersectPlane() instead.');
+ var p = Plane.fromCartesian4(plane, scratchPlane);
+ return AxisAlignedBoundingBox.intersectPlane(box, p);
+ };
+
/**
* Duplicates this AxisAlignedBoundingBox instance.
*
@@ -210,6 +233,20 @@ define([
/**
* Determines which side of a plane this box is located.
*
+ * @param {Plane} plane The plane to test against.
+ * @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.
+ */
+ AxisAlignedBoundingBox.prototype.intersectPlane = function(plane) {
+ return AxisAlignedBoundingBox.intersectPlane(this, plane);
+ };
+
+ /**
+ * Determines which side of a plane this box is located.
+ *
+ * @deprecated
* @param {Cartesian4} plane The coefficients of the plane in the form ax + by + cz + d = 0
* where the coefficients a, b, c, and d are the components x, y, z, and w
* of the {@link Cartesian4}, respectively.
diff --git a/Source/Core/BoundingSphere.js b/Source/Core/BoundingSphere.js
index 8a4b8cbcfede..09ca61880268 100644
--- a/Source/Core/BoundingSphere.js
+++ b/Source/Core/BoundingSphere.js
@@ -4,24 +4,28 @@ define([
'./Cartographic',
'./defaultValue',
'./defined',
+ './deprecationWarning',
'./DeveloperError',
'./Ellipsoid',
'./GeographicProjection',
'./Intersect',
'./Interval',
'./Matrix4',
+ './Plane',
'./Rectangle'
], function(
Cartesian3,
Cartographic,
defaultValue,
defined,
+ deprecationWarning,
DeveloperError,
Ellipsoid,
GeographicProjection,
Intersect,
Interval,
Matrix4,
+ Plane,
Rectangle) {
"use strict";
@@ -742,16 +746,13 @@ define([
* Determines which side of a plane a sphere is located.
*
* @param {BoundingSphere} sphere The bounding sphere 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.
+ * @param {Plane} plane The plane to test against.
* @returns {Intersect} {@link Intersect.INSIDE} if the entire sphere is on the side of the plane
* the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is
* on the opposite side, and {@link Intersect.INTERSECTING} if the sphere
* intersects the plane.
*/
- BoundingSphere.intersect = function(sphere, plane) {
+ BoundingSphere.intersectPlane = function(sphere, plane) {
//>>includeStart('debug', pragmas.debug);
if (!defined(sphere)) {
throw new DeveloperError('sphere is required.');
@@ -764,7 +765,8 @@ define([
var center = sphere.center;
var radius = sphere.radius;
- var distanceToPlane = Cartesian3.dot(plane, center) + plane.w;
+ var normal = plane.normal;
+ var distanceToPlane = Cartesian3.dot(normal, center) + plane.distance;
if (distanceToPlane < -radius) {
// The center point is negative side of the plane normal
@@ -776,6 +778,27 @@ define([
return Intersect.INSIDE;
};
+ var scratchPlane = new Plane(new Cartesian3(), 0.0);
+ /**
+ * Determines which side of a plane a sphere is located.
+ *
+ * @deprecated
+ * @param {BoundingSphere} sphere The bounding sphere 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 sphere is on the side of the plane
+ * the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is
+ * on the opposite side, and {@link Intersect.INTERSECTING} if the sphere
+ * intersects the plane.
+ */
+ BoundingSphere.intersect = function(sphere, plane) {
+ deprecationWarning('BoundingSphere.intersect', 'BoundingSphere.intersect() was deprecated in Cesium 1.11. It will be removed in 1.12. Use BoundingSphere.intersectPlane() instead.');
+ var p = Plane.fromCartesian4(plane, scratchPlane);
+ return BoundingSphere.intersectPlane(sphere, p);
+ };
+
/**
* Applies a 4x4 affine transformation matrix to a bounding sphere.
*
@@ -1042,6 +1065,20 @@ define([
/**
* Determines which side of a plane the sphere is located.
*
+ * @param {Plane} plane The plane to test against.
+ * @returns {Intersect} {@link Intersect.INSIDE} if the entire sphere is on the side of the plane
+ * the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is
+ * on the opposite side, and {@link Intersect.INTERSECTING} if the sphere
+ * intersects the plane.
+ */
+ BoundingSphere.prototype.intersectPlane = function(plane) {
+ return BoundingSphere.intersectPlane(this, plane);
+ };
+
+ /**
+ * Determines which side of a plane the sphere is located.
+ *
+ * @deprecated
* @param {Cartesian4} plane The coefficients of the plane in the for ax + by + cz + d = 0
* where the coefficients a, b, c, and d are the components x, y, z,
* and w of the {@link Cartesian4}, respectively.
diff --git a/Source/Core/CesiumTerrainProvider.js b/Source/Core/CesiumTerrainProvider.js
index 834a842d0421..0b15c98a3ee4 100644
--- a/Source/Core/CesiumTerrainProvider.js
+++ b/Source/Core/CesiumTerrainProvider.js
@@ -16,6 +16,9 @@ define([
'./IndexDatatype',
'./loadArrayBuffer',
'./loadJson',
+ './Math',
+ './Matrix3',
+ './OrientedBoundingBox',
'./QuantizedMeshTerrainData',
'./RuntimeError',
'./TerrainProvider',
@@ -38,6 +41,9 @@ define([
IndexDatatype,
loadArrayBuffer,
loadJson,
+ CesiumMath,
+ Matrix3,
+ OrientedBoundingBox,
QuantizedMeshTerrainData,
RuntimeError,
TerrainProvider,
@@ -417,11 +423,26 @@ define([
var skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0;
+ var rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level);
+ var orientedBoundingBox;
+ if (rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
+ // Here, rectangle.width < pi/2, and rectangle.height < pi
+ // (though it would still work with rectangle.width up to pi)
+
+ // The skirt is not included in the OBB computation. If this ever
+ // causes any rendering artifacts (cracks), they are expected to be
+ // minor and in the corners of the screen. It's possible that this
+ // might need to be changed - just change to `minimumHeight - skirtHeight`
+ // A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`.
+ orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, provider._tilingScheme.ellipsoid);
+ }
+
return new QuantizedMeshTerrainData({
center : center,
minimumHeight : minimumHeight,
maximumHeight : maximumHeight,
boundingSphere : boundingSphere,
+ orientedBoundingBox : orientedBoundingBox,
horizonOcclusionPoint : horizonOcclusionPoint,
quantizedVertices : encodedVertexBuffer,
encodedNormals : encodedNormalBuffer,
@@ -713,4 +734,4 @@ define([
};
return CesiumTerrainProvider;
-});
\ No newline at end of file
+});
diff --git a/Source/Core/EllipsoidTangentPlane.js b/Source/Core/EllipsoidTangentPlane.js
index d00cdfbde2c4..a57adbbe9c83 100644
--- a/Source/Core/EllipsoidTangentPlane.js
+++ b/Source/Core/EllipsoidTangentPlane.js
@@ -10,6 +10,7 @@ define([
'./DeveloperError',
'./Ellipsoid',
'./IntersectionTests',
+ './Matrix3',
'./Matrix4',
'./Plane',
'./Ray',
@@ -25,6 +26,7 @@ define([
DeveloperError,
Ellipsoid,
IntersectionTests,
+ Matrix3,
Matrix4,
Plane,
Ray,
@@ -91,6 +93,54 @@ define([
get : function() {
return this._origin;
}
+ },
+
+ /**
+ * Gets the plane which is tangent to the ellipsoid.
+ * @memberof EllipsoidTangentPlane.prototype
+ * @readonly
+ * @type {Plane}
+ */
+ plane : {
+ get : function() {
+ return this._plane;
+ }
+ },
+
+ /**
+ * Gets the local X-axis (east) of the tangent plane.
+ * @memberof EllipsoidTangentPlane.prototype
+ * @readonly
+ * @type {Cartesian3}
+ */
+ xAxis : {
+ get : function() {
+ return this._xAxis;
+ }
+ },
+
+ /**
+ * Gets the local Y-axis (north) of the tangent plane.
+ * @memberof EllipsoidTangentPlane.prototype
+ * @readonly
+ * @type {Cartesian3}
+ */
+ yAxis : {
+ get : function() {
+ return this._yAxis;
+ }
+ },
+
+ /**
+ * Gets the local Z-axis (up) of the tangent plane.
+ * @member EllipsoidTangentPlane.prototype
+ * @readonly
+ * @type {Cartesian3}
+ */
+ zAxis : {
+ get : function() {
+ return this._plane.normal;
+ }
}
});
@@ -113,15 +163,15 @@ define([
return new EllipsoidTangentPlane(box.center, ellipsoid);
};
- var projectPointOntoPlaneRay = new Ray();
- var projectPointOntoPlaneCartesian3 = new Cartesian3();
+ var scratchProjectPointOntoPlaneRay = new Ray();
+ var scratchProjectPointOntoPlaneCartesian3 = new Cartesian3();
/**
- * Computes the projection of the provided 3D position onto the 2D plane.
+ * Computes the projection of the provided 3D position onto the 2D plane, radially outward from the {@link EllipsoidTangentPlane.ellipsoid} coordinate system origin.
*
* @param {Cartesian3} cartesian The point to project.
* @param {Cartesian2} [result] The object onto which to store the result.
- * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided.
+ * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided. Undefined if there is no intersection point
*/
EllipsoidTangentPlane.prototype.projectPointOntoPlane = function(cartesian, result) {
//>>includeStart('debug', pragmas.debug);
@@ -130,14 +180,14 @@ define([
}
//>>includeEnd('debug');
- var ray = projectPointOntoPlaneRay;
+ var ray = scratchProjectPointOntoPlaneRay;
ray.origin = cartesian;
Cartesian3.normalize(cartesian, ray.direction);
- var intersectionPoint = IntersectionTests.rayPlane(ray, this._plane, projectPointOntoPlaneCartesian3);
+ var intersectionPoint = IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3);
if (!defined(intersectionPoint)) {
Cartesian3.negate(ray.direction, ray.direction);
- intersectionPoint = IntersectionTests.rayPlane(ray, this._plane, projectPointOntoPlaneCartesian3);
+ intersectionPoint = IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3);
}
if (defined(intersectionPoint)) {
@@ -156,7 +206,10 @@ define([
};
/**
- * Computes the projection of the provided 3D positions onto the 2D plane.
+ * Computes the projection of the provided 3D positions onto the 2D plane (where possible), radially outward from the global origin.
+ * The resulting array may be shorter than the input array - if a single projection is impossible it will not be included.
+ *
+ * @see EllipsoidTangentPlane.projectPointOntoPlane
*
* @param {Cartesian3[]} cartesians The array of points to project.
* @param {Cartesian2[]} [result] The array of Cartesian2 instances onto which to store results.
@@ -186,6 +239,70 @@ define([
return result;
};
+ /**
+ * Computes the projection of the provided 3D position onto the 2D plane, along the plane normal.
+ *
+ * @param {Cartesian3} cartesian The point to project.
+ * @param {Cartesian2} [result] The object onto which to store the result.
+ * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided.
+ */
+ EllipsoidTangentPlane.prototype.projectPointToNearestOnPlane = function(cartesian, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(cartesian)) {
+ throw new DeveloperError('cartesian is required.');
+ }
+ //>>includeEnd('debug');
+
+ if (!defined(result)) {
+ result = new Cartesian2();
+ }
+
+ var ray = scratchProjectPointOntoPlaneRay;
+ ray.origin = cartesian;
+ Cartesian3.clone(this._plane.normal, ray.direction);
+
+ var intersectionPoint = IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3);
+ if (!defined(intersectionPoint)) {
+ Cartesian3.negate(ray.direction, ray.direction);
+ intersectionPoint = IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3);
+ }
+
+ var v = Cartesian3.subtract(intersectionPoint, this._origin, intersectionPoint);
+ var x = Cartesian3.dot(this._xAxis, v);
+ var y = Cartesian3.dot(this._yAxis, v);
+
+ result.x = x;
+ result.y = y;
+ return result;
+ };
+
+ /**
+ * Computes the projection of the provided 3D positions onto the 2D plane, along the plane normal.
+ *
+ * @see EllipsoidTangentPlane.projectPointToNearestOnPlane
+ *
+ * @param {Cartesian3[]} cartesians The array of points to project.
+ * @param {Cartesian2[]} [result] The array of Cartesian2 instances onto which to store results.
+ * @returns {Cartesian2[]} The modified result parameter or a new array of Cartesian2 instances if none was provided. This will have the same length as cartesians
.
+ */
+ EllipsoidTangentPlane.prototype.projectPointsToNearestOnPlane = function(cartesians, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(cartesians)) {
+ throw new DeveloperError('cartesians is required.');
+ }
+ //>>includeEnd('debug');
+
+ if (!defined(result)) {
+ result = [];
+ }
+
+ var length = cartesians.length;
+ result.length = length;
+ for (var i = 0; i < length; i++) {
+ result[i] = this.projectPointToNearestOnPlane(cartesians[i], result[i]);
+ }
+ return result;
+ };
var projectPointsOntoEllipsoidScratch = new Cartesian3();
/**
diff --git a/Source/Core/GeometryPipeline.js b/Source/Core/GeometryPipeline.js
index 0121df21091c..7256c4b4c2c1 100644
--- a/Source/Core/GeometryPipeline.js
+++ b/Source/Core/GeometryPipeline.js
@@ -2402,7 +2402,7 @@ define([
var boundingSphere = geometry.boundingSphere;
if (defined(boundingSphere)) {
var minX = boundingSphere.center.x - boundingSphere.radius;
- if (minX > 0 || BoundingSphere.intersect(boundingSphere, Cartesian4.UNIT_Y) !== Intersect.INTERSECTING) {
+ if (minX > 0 || BoundingSphere.intersectPlane(boundingSphere, Plane.ORIGIN_ZX_PLANE) !== Intersect.INTERSECTING) {
return instance;
}
}
diff --git a/Source/Core/HeightmapTerrainData.js b/Source/Core/HeightmapTerrainData.js
index 35255264c8f0..8a6291febad7 100644
--- a/Source/Core/HeightmapTerrainData.js
+++ b/Source/Core/HeightmapTerrainData.js
@@ -211,7 +211,9 @@ define([
result.minimumHeight,
result.maximumHeight,
result.boundingSphere3D,
- result.occludeePointInScaledSpace);
+ result.occludeePointInScaledSpace,
+ 6,
+ result.orientedBoundingBox);
});
};
diff --git a/Source/Core/Matrix2.js b/Source/Core/Matrix2.js
index 20c1a1e53a4e..fe2a84d530f2 100644
--- a/Source/Core/Matrix2.js
+++ b/Source/Core/Matrix2.js
@@ -671,6 +671,41 @@ define([
return result;
};
+ /**
+ * Computes the product of a matrix times a (non-uniform) scale, as if the scale were a scale matrix.
+ *
+ * @param {Matrix2} matrix The matrix on the left-hand side.
+ * @param {Cartesian2} scale The non-uniform scale on the right-hand side.
+ * @param {Matrix2} result The object onto which to store the result.
+ * @returns {Matrix2} The modified result parameter.
+ *
+ * @see Matrix2.fromScale
+ * @see Matrix2.multiplyByUniformScale
+ *
+ * @example
+ * // Instead of Cesium.Matrix2.multiply(m, Cesium.Matrix2.fromScale(scale), m);
+ * Cesium.Matrix2.multiplyByScale(m, scale, m);
+ */
+ Matrix2.multiplyByScale = function(matrix, scale, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(matrix)) {
+ throw new DeveloperError('matrix is required');
+ }
+ if (!defined(scale)) {
+ throw new DeveloperError('scale is required');
+ }
+ if (!defined(result)) {
+ throw new DeveloperError('result is required');
+ }
+ //>>includeEnd('debug');
+
+ result[0] = matrix[0] * scale.x;
+ result[1] = matrix[1] * scale.x;
+ result[2] = matrix[2] * scale.y;
+ result[3] = matrix[3] * scale.y;
+ return result;
+ };
+
/**
* Creates a negated copy of the provided matrix.
*
@@ -812,6 +847,15 @@ define([
Matrix2.IDENTITY = freezeObject(new Matrix2(1.0, 0.0,
0.0, 1.0));
+ /**
+ * An immutable Matrix2 instance initialized to the zero matrix.
+ *
+ * @type {Matrix2}
+ * @constant
+ */
+ Matrix2.ZERO = freezeObject(new Matrix2(0.0, 0.0,
+ 0.0, 0.0));
+
/**
* The index into Matrix2 for column 0, row 0.
*
diff --git a/Source/Core/Matrix3.js b/Source/Core/Matrix3.js
index 5a4c770f73d8..1449b1eda217 100644
--- a/Source/Core/Matrix3.js
+++ b/Source/Core/Matrix3.js
@@ -961,6 +961,46 @@ define([
return result;
};
+ /**
+ * Computes the product of a matrix times a (non-uniform) scale, as if the scale were a scale matrix.
+ *
+ * @param {Matrix3} matrix The matrix on the left-hand side.
+ * @param {Cartesian3} scale The non-uniform scale on the right-hand side.
+ * @param {Matrix3} result The object onto which to store the result.
+ * @returns {Matrix3} The modified result parameter.
+ *
+ * @see Matrix3.fromScale
+ * @see Matrix3.multiplyByUniformScale
+ *
+ * @example
+ * // Instead of Cesium.Matrix3.multiply(m, Cesium.Matrix3.fromScale(scale), m);
+ * Cesium.Matrix3.multiplyByScale(m, scale, m);
+ */
+ Matrix3.multiplyByScale = function(matrix, scale, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(matrix)) {
+ throw new DeveloperError('matrix is required');
+ }
+ if (!defined(scale)) {
+ throw new DeveloperError('scale is required');
+ }
+ if (!defined(result)) {
+ throw new DeveloperError('result is required');
+ }
+ //>>includeEnd('debug');
+
+ result[0] = matrix[0] * scale.x;
+ result[1] = matrix[1] * scale.x;
+ result[2] = matrix[2] * scale.x;
+ result[3] = matrix[3] * scale.y;
+ result[4] = matrix[4] * scale.y;
+ result[5] = matrix[5] * scale.y;
+ result[6] = matrix[6] * scale.z;
+ result[7] = matrix[7] * scale.z;
+ result[8] = matrix[8] * scale.z;
+ return result;
+ };
+
/**
* Creates a negated copy of the provided matrix.
*
@@ -1354,6 +1394,16 @@ define([
0.0, 1.0, 0.0,
0.0, 0.0, 1.0));
+ /**
+ * An immutable Matrix3 instance initialized to the zero matrix.
+ *
+ * @type {Matrix3}
+ * @constant
+ */
+ Matrix3.ZERO = freezeObject(new Matrix3(0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0));
+
/**
* The index into Matrix3 for column 0, row 0.
*
diff --git a/Source/Core/Matrix4.js b/Source/Core/Matrix4.js
index 306d91f56061..80bc49d24329 100644
--- a/Source/Core/Matrix4.js
+++ b/Source/Core/Matrix4.js
@@ -2544,6 +2544,17 @@ define([
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0));
+ /**
+ * An immutable Matrix4 instance initialized to the zero matrix.
+ *
+ * @type {Matrix4}
+ * @constant
+ */
+ Matrix4.ZERO = freezeObject(new Matrix4(0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0));
+
/**
* The index into Matrix4 for column 0, row 0.
*
diff --git a/Source/Core/ObjectOrientedBoundingBox.js b/Source/Core/ObjectOrientedBoundingBox.js
index fae0d3605528..bbce966665d6 100644
--- a/Source/Core/ObjectOrientedBoundingBox.js
+++ b/Source/Core/ObjectOrientedBoundingBox.js
@@ -4,12 +4,14 @@ define([
'./defaultValue',
'./defined',
'./DeveloperError',
+ './deprecationWarning',
'./Matrix3'
], function(
Cartesian3,
defaultValue,
defined,
DeveloperError,
+ deprecationWarning,
Matrix3) {
"use strict";
@@ -19,6 +21,7 @@ define([
* It is oriented, so it can provide an optimum fit, it can bound more tightly.
* @alias ObjectOrientedBoundingBox
* @constructor
+ * @deprecated
*
* @param {Matrix3} [rotation=Matrix3.IDENTITY] The transformation matrix, to rotate the box to the right position.
* @param {Cartesian3} [translation=Cartesian3.ZERO] The position of the box.
@@ -38,6 +41,8 @@ define([
* var oobb = new Cesium.ObjectOrientedBoundingBox(rotation, translation, scale);
*/
var ObjectOrientedBoundingBox = function(rotation, translation, scale) {
+ deprecationWarning('ObjectOrientedBoundingBox', 'ObjectOrientedBoundingBox was deprecated in Cesium 1.11. It will be removed in 1.12. Use OrientedBoundingBox instead.');
+
/**
* The transformation matrix, to rotate the box to the right position.
* @type {Matrix3}
@@ -362,9 +367,9 @@ define([
return (left === right) ||
((defined(left)) &&
(defined(right)) &&
- Cartesian3.equals(left.transformedPosition, right.transformedPosition) &&
- Matrix3.equals(left.transformMatrix, right.transformMatrix) &&
- Cartesian3.equals(left.rectangle, right.rectangle));
+ Cartesian3.equals(left.translation, right.translation) &&
+ Matrix3.equals(left.rotation, right.rotation) &&
+ Cartesian3.equals(left.scale, right.scale));
};
/**
diff --git a/Source/Core/OrientedBoundingBox.js b/Source/Core/OrientedBoundingBox.js
new file mode 100644
index 000000000000..e9bf50db32f6
--- /dev/null
+++ b/Source/Core/OrientedBoundingBox.js
@@ -0,0 +1,330 @@
+/*global define*/
+define([
+ './Cartesian2',
+ './Cartesian3',
+ './Cartographic',
+ './defaultValue',
+ './defined',
+ './DeveloperError',
+ './Ellipsoid',
+ './EllipsoidTangentPlane',
+ './Intersect',
+ './Plane',
+ './Rectangle',
+ './Math',
+ './Matrix3'
+ ], function(
+ Cartesian2,
+ Cartesian3,
+ Cartographic,
+ defaultValue,
+ defined,
+ DeveloperError,
+ Ellipsoid,
+ EllipsoidTangentPlane,
+ Intersect,
+ Plane,
+ Rectangle,
+ CesiumMath,
+ Matrix3) {
+ "use strict";
+
+ /**
+ * Creates an instance of an OrientedBoundingBox.
+ * An OrientedBoundingBox of some object is a closed and convex cuboid. It can provide a tighter bounding volume than {@link BoundingSphere} or {@link AxisAlignedBoundingBox} in many cases.
+ * @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 BoundingSphere
+ * @see BoundingRectangle
+ *
+ * @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.0, 0.0);
+ * var halfAxes = Cesium.Matrix3.fromScale(new Cartesian3(1.0, 3.0, 2.0), new Matrix3());
+ *
+ * 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 scratchOffset = new Cartesian3();
+ var scratchScale = new Cartesian3();
+ /**
+ * Computes an OrientedBoundingBox given extents in the east-north-up space of the tangent plane.
+ *
+ * @param {Number} minimumX Minimum X extent in tangent plane space.
+ * @param {Number} maximumX Maximum X extent in tangent plane space.
+ * @param {Number} minimumY Minimum Y extent in tangent plane space.
+ * @param {Number} maximumY Maximum Y extent in tangent plane space.
+ * @param {Number} minimumZ Minimum Z extent in tangent plane space.
+ * @param {Number} maximumZ Maximum Z extent in tangent plane space.
+ * @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.
+ */
+ var fromTangentPlaneExtents = function(tangentPlane, minimumX, maximumX, minimumY, maximumY, minimumZ, maximumZ, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(minimumX) ||
+ !defined(maximumX) ||
+ !defined(minimumY) ||
+ !defined(maximumY) ||
+ !defined(minimumZ) ||
+ !defined(maximumZ)) {
+ throw new DeveloperError('all extents (minimum/maximum X/Y/Z) are required.');
+ }
+ //>>includeEnd('debug');
+
+ if (!defined(result)) {
+ result = new OrientedBoundingBox();
+ }
+
+ var halfAxes = result.halfAxes;
+ Matrix3.setColumn(halfAxes, 0, tangentPlane.xAxis, halfAxes);
+ Matrix3.setColumn(halfAxes, 1, tangentPlane.yAxis, halfAxes);
+ Matrix3.setColumn(halfAxes, 2, tangentPlane.zAxis, halfAxes);
+
+ var centerOffset = scratchOffset;
+ centerOffset.x = (minimumX + maximumX) / 2.0;
+ centerOffset.y = (minimumY + maximumY) / 2.0;
+ centerOffset.z = (minimumZ + maximumZ) / 2.0;
+
+ var scale = scratchScale;
+ scale.x = (maximumX - minimumX) / 2.0;
+ scale.y = (maximumY - minimumY) / 2.0;
+ scale.z = (maximumZ - minimumZ) / 2.0;
+
+ var center = result.center;
+ centerOffset = Matrix3.multiplyByVector(halfAxes, centerOffset, centerOffset);
+ Cartesian3.add(tangentPlane.origin, centerOffset, center);
+ Matrix3.multiplyByScale(halfAxes, scale, halfAxes);
+
+ return result;
+ };
+
+ var scratchRectangleCenterCartographic = new Cartographic();
+ var scratchRectangleCenter = new Cartesian3();
+ var perimeterCartographicScratch = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
+ var perimeterCartesianScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
+ var perimeterProjectedScratch = [new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2()];
+ /**
+ * Computes an OrientedBoundingBox that bounds a {@link Rectangle} on the surface of an {@link Ellipsoid}.
+ * There are no guarantees about the orientation of the bounding box.
+ *
+ * @param {Rectangle} rectangle The cartographic rectangle on the surface of the ellipsoid.
+ * @param {Number} [minimumHeight=0.0] The minimum height (elevation) within the tile.
+ * @param {Number} [maximumHeight=0.0] The maximum height (elevation) within the tile.
+ * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle is defined.
+ * @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.
+ *
+ * @exception {DeveloperError} rectangle.width must be between 0 and pi.
+ * @exception {DeveloperError} rectangle.height must be between 0 and pi.
+ * @exception {DeveloperError} ellipsoid must be an ellipsoid of revolution (radii.x == radii.y
)
+ */
+ OrientedBoundingBox.fromRectangle = function(rectangle, minimumHeight, maximumHeight, ellipsoid, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(rectangle)) {
+ throw new DeveloperError('rectangle is required');
+ }
+ if (rectangle.width < 0.0 || rectangle.width > CesiumMath.PI) {
+ throw new DeveloperError('Rectangle width must be between 0 and pi');
+ }
+ if (rectangle.height < 0.0 || rectangle.height > CesiumMath.PI) {
+ throw new DeveloperError('Rectangle height must be between 0 and pi');
+ }
+ if (defined(ellipsoid) && !CesiumMath.equalsEpsilon(ellipsoid.radii.x, ellipsoid.radii.y, CesiumMath.EPSILON15)) {
+ throw new DeveloperError('Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)');
+ }
+ //>>includeEnd('debug');
+
+ minimumHeight = defaultValue(minimumHeight, 0.0);
+ maximumHeight = defaultValue(maximumHeight, 0.0);
+ ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
+
+ // The bounding box will be aligned with the tangent plane at the center of the rectangle.
+ var tangentPointCartographic = Rectangle.center(rectangle, scratchRectangleCenterCartographic);
+ var tangentPoint = ellipsoid.cartographicToCartesian(tangentPointCartographic, scratchRectangleCenter);
+ var tangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid);
+ var plane = tangentPlane.plane;
+
+ // Corner arrangement:
+ // N/+y
+ // [0] [1] [2]
+ // W/-x [7] [3] E/+x
+ // [6] [5] [4]
+ // S/-y
+ // "C" refers to the central lat/long, which by default aligns with the tangent point (above).
+ // If the rectangle spans the equator, CW and CE are instead aligned with the equator.
+ var perimeterNW = perimeterCartographicScratch[0];
+ var perimeterNC = perimeterCartographicScratch[1];
+ var perimeterNE = perimeterCartographicScratch[2];
+ var perimeterCE = perimeterCartographicScratch[3];
+ var perimeterSE = perimeterCartographicScratch[4];
+ var perimeterSC = perimeterCartographicScratch[5];
+ var perimeterSW = perimeterCartographicScratch[6];
+ var perimeterCW = perimeterCartographicScratch[7];
+
+ var lonCenter = tangentPointCartographic.longitude;
+ var latCenter = (rectangle.south < 0.0 && rectangle.north > 0.0) ? 0.0 : tangentPointCartographic.latitude;
+ perimeterSW.latitude = perimeterSC.latitude = perimeterSE.latitude = rectangle.south;
+ perimeterCW.latitude = perimeterCE.latitude = latCenter;
+ perimeterNW.latitude = perimeterNC.latitude = perimeterNE.latitude = rectangle.north;
+ perimeterSW.longitude = perimeterCW.longitude = perimeterNW.longitude = rectangle.west;
+ perimeterSC.longitude = perimeterNC.longitude = lonCenter;
+ perimeterSE.longitude = perimeterCE.longitude = perimeterNE.longitude = rectangle.east;
+
+ // Compute XY extents using the rectangle at maximum height
+ perimeterNE.height = perimeterNC.height = perimeterNW.height = perimeterCW.height = perimeterSW.height = perimeterSC.height = perimeterSE.height = perimeterCE.height = maximumHeight;
+
+ ellipsoid.cartographicArrayToCartesianArray(perimeterCartographicScratch, perimeterCartesianScratch);
+ tangentPlane.projectPointsToNearestOnPlane(perimeterCartesianScratch, perimeterProjectedScratch);
+ // See the `perimeterXX` definitions above for what these are
+ var minX = Math.min(perimeterProjectedScratch[6].x, perimeterProjectedScratch[7].x, perimeterProjectedScratch[0].x);
+ var maxX = Math.max(perimeterProjectedScratch[2].x, perimeterProjectedScratch[3].x, perimeterProjectedScratch[4].x);
+ var minY = Math.min(perimeterProjectedScratch[4].y, perimeterProjectedScratch[5].y, perimeterProjectedScratch[6].y);
+ var maxY = Math.max(perimeterProjectedScratch[0].y, perimeterProjectedScratch[1].y, perimeterProjectedScratch[2].y);
+
+ // Compute minimum Z using the rectangle at minimum height
+ perimeterNE.height = perimeterNW.height = perimeterSE.height = perimeterSW.height = minimumHeight;
+ ellipsoid.cartographicArrayToCartesianArray(perimeterCartographicScratch, perimeterCartesianScratch);
+ var minZ = Math.min(Plane.getPointDistance(plane, perimeterCartesianScratch[0]),
+ Plane.getPointDistance(plane, perimeterCartesianScratch[2]),
+ Plane.getPointDistance(plane, perimeterCartesianScratch[4]),
+ Plane.getPointDistance(plane, perimeterCartesianScratch[6]));
+ var maxZ = maximumHeight; // Since the tangent plane touches the surface at height = 0, this is okay
+
+ return fromTangentPlaneExtents(tangentPlane, minX, maxX, minY, maxY, minZ, maxZ, 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 {Plane} plane The plane to test against.
+ * @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;
+ var normal = plane.normal;
+ var halfAxes = box.halfAxes;
+ var normalX = normal.x, normalY = normal.y, normalZ = normal.z;
+ // plane is used as if it is its normal; the first three components are assumed to be normalized
+ var radEffective = Math.abs(normalX * halfAxes[Matrix3.COLUMN0ROW0] + normalY * halfAxes[Matrix3.COLUMN0ROW1] + normalZ * halfAxes[Matrix3.COLUMN0ROW2]) +
+ Math.abs(normalX * halfAxes[Matrix3.COLUMN1ROW0] + normalY * halfAxes[Matrix3.COLUMN1ROW1] + normalZ * halfAxes[Matrix3.COLUMN1ROW2]) +
+ Math.abs(normalX * halfAxes[Matrix3.COLUMN2ROW0] + normalY * halfAxes[Matrix3.COLUMN2ROW1] + normalZ * halfAxes[Matrix3.COLUMN2ROW2]);
+ var distanceToPlane = Cartesian3.dot(normal, center) + plane.distance;
+
+ 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 {Plane} plane The plane to test against.
+ * @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
+ * true
if they are equal, false
otherwise.
+ *
+ * @param {OrientedBoundingBox} left The first OrientedBoundingBox.
+ * @param {OrientedBoundingBox} right The second OrientedBoundingBox.
+ * @returns {Boolean} true
if left and right are equal, false
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
+ * true
if they are equal, false
otherwise.
+ *
+ * @param {OrientedBoundingBox} [right] The right hand side OrientedBoundingBox.
+ * @returns {Boolean} true
if they are equal, false
otherwise.
+ */
+ OrientedBoundingBox.prototype.equals = function(right) {
+ return OrientedBoundingBox.equals(this, right);
+ };
+
+ return OrientedBoundingBox;
+});
diff --git a/Source/Core/Plane.js b/Source/Core/Plane.js
index 732f88e9a471..979e67ac97eb 100644
--- a/Source/Core/Plane.js
+++ b/Source/Core/Plane.js
@@ -2,11 +2,13 @@
define([
'./Cartesian3',
'./defined',
- './DeveloperError'
+ './DeveloperError',
+ './freezeObject'
], function(
Cartesian3,
defined,
- DeveloperError) {
+ DeveloperError,
+ freezeObject) {
"use strict";
/**
@@ -95,6 +97,33 @@ define([
return result;
};
+ var scratchNormal = new Cartesian3();
+ /**
+ * Creates a plane from the general equation
+ *
+ * @param {Cartesian4} coefficients The plane's normal (normalized).
+ * @param {Plane} [result] The object onto which to store the result.
+ * @returns {Plane} A new plane instance or the modified result parameter.
+ */
+ Plane.fromCartesian4 = function(coefficients, result) {
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(coefficients)) {
+ throw new DeveloperError('coefficients is required.');
+ }
+ //>>includeEnd('debug');
+
+ var normal = Cartesian3.fromCartesian4(coefficients, scratchNormal);
+ var distance = coefficients.w;
+
+ if (!defined(result)) {
+ return new Plane(normal, distance);
+ } else {
+ Cartesian3.clone(normal, result.normal);
+ result.distance = distance;
+ return result;
+ }
+ };
+
/**
* Computes the signed shortest distance of a point to a plane.
* The sign of the distance determines which side of the plane the point
@@ -119,5 +148,29 @@ define([
return Cartesian3.dot(plane.normal, point) + plane.distance;
};
+ /**
+ * A constant initialized to the XY plane passing through the origin, with normal in positive Z.
+ *
+ * @type {Plane}
+ * @constant
+ */
+ Plane.ORIGIN_XY_PLANE = freezeObject(new Plane(Cartesian3.UNIT_Z, 0.0));
+
+ /**
+ * A constant initialized to the YZ plane passing through the origin, with normal in positive X.
+ *
+ * @type {Plane}
+ * @constant
+ */
+ Plane.ORIGIN_YZ_PLANE = freezeObject(new Plane(Cartesian3.UNIT_X, 0.0));
+
+ /**
+ * A constant initialized to the ZX plane passing through the origin, with normal in positive Y.
+ *
+ * @type {Plane}
+ * @constant
+ */
+ Plane.ORIGIN_ZX_PLANE = freezeObject(new Plane(Cartesian3.UNIT_Y, 0.0));
+
return Plane;
});
diff --git a/Source/Core/QuantizedMeshTerrainData.js b/Source/Core/QuantizedMeshTerrainData.js
index c0501d29cc41..5458bd0baea6 100644
--- a/Source/Core/QuantizedMeshTerrainData.js
+++ b/Source/Core/QuantizedMeshTerrainData.js
@@ -10,6 +10,7 @@ define([
'./IndexDatatype',
'./Intersections2D',
'./Math',
+ './OrientedBoundingBox',
'./TaskProcessor',
'./TerrainMesh'
], function(
@@ -23,6 +24,7 @@ define([
IndexDatatype,
Intersections2D,
CesiumMath,
+ OrientedBoundingBox,
TaskProcessor,
TerrainMesh) {
"use strict";
@@ -44,6 +46,7 @@ define([
* @param {Number} options.minimumHeight The minimum terrain height within the tile, in meters above the ellipsoid.
* @param {Number} options.maximumHeight The maximum terrain height within the tile, in meters above the ellipsoid.
* @param {BoundingSphere} options.boundingSphere A sphere bounding all of the vertices in the mesh.
+ * @param {OrientedBoundingBox} [options.orientedBoundingBox] An OrientedBoundingBox bounding all of the vertices in the mesh.
* @param {Cartesian3} options.horizonOcclusionPoint The horizon occlusion point of the mesh. If this point
* is below the horizon, the entire tile is assumed to be below the horizon as well.
* The point is expressed in ellipsoid-scaled coordinates.
@@ -88,6 +91,7 @@ define([
* indices : new Uint16Array([0, 3, 1,
* 0, 2, 3]),
* boundingSphere : new Cesium.BoundingSphere(new Cesium.Cartesian3(1.0, 2.0, 3.0), 10000),
+ * orientedBoundingBox : new Cesium.OrientedBoundingBox(new Cesium.Cartesian3(1.0, 2.0, 3.0), Matrix3.fromRotationX(Cesium.Math.PI, new Matrix3())),
* horizonOcclusionPoint : new Cesium.Cartesian3(3.0, 2.0, 1.0),
* westIndices : [0, 1],
* southIndices : [0, 1],
@@ -154,6 +158,7 @@ define([
this._minimumHeight = options.minimumHeight;
this._maximumHeight = options.maximumHeight;
this._boundingSphere = options.boundingSphere;
+ this._orientedBoundingBox = options.orientedBoundingBox;
this._horizonOcclusionPoint = options.horizonOcclusionPoint;
var vertexCount = this._quantizedVertices.length / 3;
@@ -291,7 +296,8 @@ define([
that._maximumHeight,
that._boundingSphere,
that._horizonOcclusionPoint,
- defined(that._encodedNormals) ? 7 : 6);
+ defined(that._encodedNormals) ? 7 : 6,
+ that._orientedBoundingBox);
});
};
@@ -388,6 +394,7 @@ define([
minimumHeight : result.minimumHeight,
maximumHeight : result.maximumHeight,
boundingSphere : BoundingSphere.clone(result.boundingSphere),
+ orientedBoundingBox : OrientedBoundingBox.clone(result.orientedBoundingBox),
horizonOcclusionPoint : Cartesian3.clone(result.horizonOcclusionPoint),
westIndices : result.westIndices,
southIndices : result.southIndices,
@@ -504,4 +511,4 @@ define([
};
return QuantizedMeshTerrainData;
-});
\ No newline at end of file
+});
diff --git a/Source/Core/TerrainMesh.js b/Source/Core/TerrainMesh.js
index 3e60cb6a0ece..3432d5537279 100644
--- a/Source/Core/TerrainMesh.js
+++ b/Source/Core/TerrainMesh.js
@@ -25,8 +25,9 @@ define([
* scaled space, and used for horizon culling. If this point is below the horizon,
* the tile is considered to be entirely below the horizon.
* @param {Number} [vertexStride=6] The number of components in each vertex.
+ * @param {OrientedBoundingBox} [orientedBoundingBox] A bounding sphere that completely contains the tile.
*/
- var TerrainMesh = function TerrainMesh(center, vertices, indices, minimumHeight, maximumHeight, boundingSphere3D, occludeePointInScaledSpace, vertexStride) {
+ var TerrainMesh = function TerrainMesh(center, vertices, indices, minimumHeight, maximumHeight, boundingSphere3D, occludeePointInScaledSpace, vertexStride, orientedBoundingBox) {
/**
* The center of the tile. Vertex positions are specified relative to this center.
* @type {Cartesian3}
@@ -82,6 +83,12 @@ define([
* @type {Cartesian3}
*/
this.occludeePointInScaledSpace = occludeePointInScaledSpace;
+
+ /**
+ * A bounding box that completely contains the tile.
+ * @type {OrientedBoundingBox}
+ */
+ this.orientedBoundingBox = orientedBoundingBox;
};
return TerrainMesh;
diff --git a/Source/Renderer/DrawCommand.js b/Source/Renderer/DrawCommand.js
index d7af2e289009..f8fdfcb972b6 100644
--- a/Source/Renderer/DrawCommand.js
+++ b/Source/Renderer/DrawCommand.js
@@ -31,6 +31,17 @@ define([
*/
this.boundingVolume = options.boundingVolume;
+ /**
+ * The oriented bounding box of the geometry in world space. If this is defined, it is used instead of
+ * {@link DrawCommand#boundingVolume} for plane intersection testing.
+ *
+ * @type {OrientedBoundingBox}
+ * @default undefined
+ *
+ * @see DrawCommand#debugShowBoundingVolume
+ */
+ this.orientedBoundingBox = options.orientedBoundingBox;
+
/**
* When true
, the renderer frustum and horizon culls the command based on its {@link DrawCommand#boundingVolume}.
* If the command was already culled, set this to false
for a performance improvement.
@@ -186,4 +197,4 @@ define([
};
return DrawCommand;
-});
\ No newline at end of file
+});
diff --git a/Source/Scene/CullingVolume.js b/Source/Scene/CullingVolume.js
index e053d9677cb4..09fc1aa0597e 100644
--- a/Source/Scene/CullingVolume.js
+++ b/Source/Scene/CullingVolume.js
@@ -2,13 +2,17 @@
define([
'../Core/defaultValue',
'../Core/defined',
+ '../Core/Cartesian3',
'../Core/DeveloperError',
- '../Core/Intersect'
+ '../Core/Intersect',
+ '../Core/Plane'
], function(
defaultValue,
defined,
+ Cartesian3,
DeveloperError,
- Intersect) {
+ Intersect,
+ Plane) {
"use strict";
/**
@@ -30,6 +34,7 @@ define([
this.planes = defaultValue(planes, []);
};
+ var scratchPlane = new Plane(new Cartesian3(), 0.0);
/**
* Determines whether a bounding volume intersects the culling volume.
*
@@ -46,7 +51,7 @@ define([
var planes = this.planes;
var intersecting = false;
for (var k = 0, len = planes.length; k < len; ++k) {
- var result = boundingVolume.intersect(planes[k]);
+ var result = boundingVolume.intersectPlane(Plane.fromCartesian4(planes[k], scratchPlane));
if (result === Intersect.OUTSIDE) {
return Intersect.OUTSIDE;
} else if (result === Intersect.INTERSECTING) {
diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js
index a9a8eb8ebc1d..229a08ffee37 100644
--- a/Source/Scene/GlobeSurfaceTile.js
+++ b/Source/Scene/GlobeSurfaceTile.js
@@ -124,6 +124,7 @@ define([
this.maximumHeight = 0.0;
this.boundingSphere3D = new BoundingSphere();
this.boundingSphere2D = new BoundingSphere();
+ this.orientedBoundingBox = undefined;
this.occludeePointInScaledSpace = new Cartesian3();
this.loadedTerrain = undefined;
diff --git a/Source/Scene/GlobeSurfaceTileProvider.js b/Source/Scene/GlobeSurfaceTileProvider.js
index ac40863482bd..12dc7f86570a 100644
--- a/Source/Scene/GlobeSurfaceTileProvider.js
+++ b/Source/Scene/GlobeSurfaceTileProvider.js
@@ -1,22 +1,28 @@
/*global define*/
define([
'../Core/BoundingSphere',
+ '../Core/BoxOutlineGeometry',
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartesian4',
'../Core/Color',
+ '../Core/ColorGeometryInstanceAttribute',
+ '../Core/defaultValue',
'../Core/defined',
'../Core/defineProperties',
'../Core/destroyObject',
'../Core/DeveloperError',
'../Core/Event',
'../Core/FeatureDetection',
+ '../Core/GeometryInstance',
'../Core/GeometryPipeline',
'../Core/IndexDatatype',
'../Core/Intersect',
'../Core/Matrix4',
+ '../Core/OrientedBoundingBox',
'../Core/PrimitiveType',
'../Core/Rectangle',
+ '../Core/SphereOutlineGeometry',
'../Core/Visibility',
'../Core/WebMercatorProjection',
'../Renderer/BufferUsage',
@@ -24,6 +30,8 @@ define([
'../Scene/BlendingState',
'../Scene/DepthFunction',
'../Scene/Pass',
+ '../Scene/PerInstanceColorAppearance',
+ '../Scene/Primitive',
'../ThirdParty/when',
'./GlobeSurfaceTile',
'./ImageryLayer',
@@ -32,22 +40,28 @@ define([
'./SceneMode'
], function(
BoundingSphere,
+ BoxOutlineGeometry,
Cartesian2,
Cartesian3,
Cartesian4,
Color,
+ ColorGeometryInstanceAttribute,
+ defaultValue,
defined,
defineProperties,
destroyObject,
DeveloperError,
Event,
FeatureDetection,
+ GeometryInstance,
GeometryPipeline,
IndexDatatype,
Intersect,
Matrix4,
+ OrientedBoundingBox,
PrimitiveType,
Rectangle,
+ SphereOutlineGeometry,
Visibility,
WebMercatorProjection,
BufferUsage,
@@ -55,6 +69,8 @@ define([
BlendingState,
DepthFunction,
Pass,
+ PerInstanceColorAppearance,
+ Primitive,
when,
GlobeSurfaceTile,
ImageryLayer,
@@ -395,7 +411,7 @@ define([
var cullingVolume = frameState.cullingVolume;
- var boundingVolume = surfaceTile.boundingSphere3D;
+ var boundingVolume = defaultValue(surfaceTile.orientedBoundingBox, surfaceTile.boundingSphere3D);
if (frameState.mode !== SceneMode.SCENE3D) {
boundingVolume = boundingSphereScratch;
@@ -794,6 +810,74 @@ define([
return context.createVertexArray(vertexArray._attributes, wireframeIndexBuffer);
}
+ var getDebugOrientedBoundingBox;
+ var getDebugBoundingSphere;
+ var debugDestroyPrimitive;
+
+ (function() {
+ var instanceOBB = new GeometryInstance({
+ geometry: BoxOutlineGeometry.fromDimensions({ dimensions: new Cartesian3(2.0, 2.0, 2.0) })
+ });
+ var instanceSphere = new GeometryInstance({
+ geometry: new SphereOutlineGeometry({ radius: 1.0 })
+ });
+ var modelMatrix = new Matrix4();
+ var previousVolume;
+ var primitive;
+
+ function createDebugPrimitive(instance) {
+ return new Primitive({
+ geometryInstances : instance,
+ appearance : new PerInstanceColorAppearance({
+ translucent : false,
+ flat : true
+ }),
+ asynchronous : false
+ });
+ }
+
+ getDebugOrientedBoundingBox = function(obb, color) {
+ if (obb === previousVolume) {
+ return primitive;
+ }
+ debugDestroyPrimitive();
+
+ previousVolume = obb;
+ modelMatrix = Matrix4.fromRotationTranslation(obb.halfAxes, obb.center, modelMatrix);
+
+ instanceOBB.modelMatrix = modelMatrix;
+ instanceOBB.attributes.color = ColorGeometryInstanceAttribute.fromColor(color);
+
+ primitive = createDebugPrimitive(instanceOBB);
+ return primitive;
+ };
+
+ getDebugBoundingSphere = function(sphere, color) {
+ if (sphere === previousVolume) {
+ return primitive;
+ }
+ debugDestroyPrimitive();
+
+ previousVolume = sphere;
+ modelMatrix = Matrix4.fromTranslation(sphere.center, modelMatrix);
+ modelMatrix = Matrix4.multiplyByUniformScale(modelMatrix, sphere.radius, modelMatrix);
+
+ instanceSphere.modelMatrix = modelMatrix;
+ instanceSphere.attributes.color = ColorGeometryInstanceAttribute.fromColor(color);
+
+ primitive = createDebugPrimitive(instanceSphere);
+ return primitive;
+ };
+
+ debugDestroyPrimitive = function() {
+ if (defined(primitive)) {
+ primitive.destroy();
+ primitive = undefined;
+ previousVolume = undefined;
+ }
+ };
+ })();
+
var otherPassesInitialColor = new Cartesian4(0.0, 0.0, 0.0, 0.0);
function addDrawCommandsForTile(tileProvider, tile, context, frameState, commandList) {
@@ -882,6 +966,10 @@ define([
var initialColor = tileProvider._firstPassInitialColor;
+ if (!defined(tileProvider._debug.boundingSphereTile)) {
+ debugDestroyPrimitive();
+ }
+
do {
var numberOfDayTextures = 0;
@@ -893,6 +981,7 @@ define([
command.owner = tile;
command.cull = false;
command.boundingVolume = new BoundingSphere();
+ command.orientedBoundingBox = undefined;
uniformMap = createTileUniformMap();
@@ -907,7 +996,16 @@ define([
++tileProvider._usedDrawCommands;
- command.debugShowBoundingVolume = (tile === tileProvider._debug.boundingSphereTile);
+ if (tile === tileProvider._debug.boundingSphereTile) {
+ // If a debug primitive already exists for this tile, it will not be
+ // re-created, to avoid allocation every frame. If it were possible
+ // to have more than one selected tile, this would have to change.
+ if (defined(surfaceTile.orientedBoundingBox)) {
+ getDebugOrientedBoundingBox(surfaceTile.orientedBoundingBox, Color.RED).update(context, frameState, commandList);
+ } else if (defined(surfaceTile.boundingSphere3D)) {
+ getDebugBoundingSphere(surfaceTile.boundingSphere3D, Color.RED).update(context, frameState, commandList);
+ }
+ }
Cartesian4.clone(initialColor, uniformMap.initialColor);
uniformMap.oceanNormalMap = oceanNormalMap;
@@ -1002,6 +1100,7 @@ define([
}
var boundingVolume = command.boundingVolume;
+ var orientedBoundingBox = command.orientedBoundingBox;
if (frameState.mode !== SceneMode.SCENE3D) {
BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, surfaceTile.minimumHeight, surfaceTile.maximumHeight, boundingVolume);
@@ -1011,7 +1110,8 @@ define([
boundingVolume = BoundingSphere.union(surfaceTile.boundingSphere3D, boundingVolume, boundingVolume);
}
} else {
- BoundingSphere.clone(surfaceTile.boundingSphere3D, boundingVolume);
+ command.boundingVolume = BoundingSphere.clone(surfaceTile.boundingSphere3D, boundingVolume);
+ command.orientedBoundingBox = OrientedBoundingBox.clone(surfaceTile.orientedBoundingBox, orientedBoundingBox);
}
commandList.push(command);
diff --git a/Source/Scene/PolylineCollection.js b/Source/Scene/PolylineCollection.js
index 0142d7ae1bab..25f8cae2bc12 100644
--- a/Source/Scene/PolylineCollection.js
+++ b/Source/Scene/PolylineCollection.js
@@ -16,6 +16,7 @@ define([
'../Core/Intersect',
'../Core/Math',
'../Core/Matrix4',
+ '../Core/Plane',
'../Renderer/BufferUsage',
'../Renderer/DrawCommand',
'../Renderer/ShaderSource',
@@ -44,6 +45,7 @@ define([
Intersect,
CesiumMath,
Matrix4,
+ Plane,
BufferUsage,
DrawCommand,
ShaderSource,
@@ -1046,7 +1048,7 @@ define([
function intersectsIDL(polyline) {
return Cartesian3.dot(Cartesian3.UNIT_X, polyline._boundingVolume.center) < 0 ||
- polyline._boundingVolume.intersect(Cartesian4.UNIT_Y) === Intersect.INTERSECTING;
+ polyline._boundingVolume.intersectPlane(Plane.ORIGIN_ZX_PLANE) === Intersect.INTERSECTING;
}
PolylineBucket.prototype.getPolylinePositionsLength = function(polyline) {
diff --git a/Source/Scene/TileTerrain.js b/Source/Scene/TileTerrain.js
index df4d44027d56..0ba7fce95f97 100644
--- a/Source/Scene/TileTerrain.js
+++ b/Source/Scene/TileTerrain.js
@@ -6,6 +6,7 @@ define([
'../Core/defined',
'../Core/DeveloperError',
'../Core/IndexDatatype',
+ '../Core/OrientedBoundingBox',
'../Core/TileProviderError',
'../Renderer/BufferUsage',
'../ThirdParty/when',
@@ -18,6 +19,7 @@ define([
defined,
DeveloperError,
IndexDatatype,
+ OrientedBoundingBox,
TileProviderError,
BufferUsage,
when,
@@ -78,6 +80,7 @@ define([
surfaceTile.minimumHeight = mesh.minimumHeight;
surfaceTile.maximumHeight = mesh.maximumHeight;
surfaceTile.boundingSphere3D = BoundingSphere.clone(mesh.boundingSphere3D, surfaceTile.boundingSphere3D);
+ surfaceTile.orientedBoundingBox = OrientedBoundingBox.clone(mesh.orientedBoundingBox, surfaceTile.orientedBoundingBox);
tile.data.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace);
diff --git a/Source/Widgets/CesiumInspector/CesiumInspector.js b/Source/Widgets/CesiumInspector/CesiumInspector.js
index 0a2c415ecf36..b873546627d6 100644
--- a/Source/Widgets/CesiumInspector/CesiumInspector.js
+++ b/Source/Widgets/CesiumInspector/CesiumInspector.js
@@ -301,7 +301,7 @@ define([
tbsCheck.type = 'checkbox';
tbsCheck.setAttribute('data-bind', 'checked: tileBoundingSphere, enable: hasPickedTile, click: showTileBoundingSphere');
tileBoundingSphere.appendChild(tbsCheck);
- tileBoundingSphere.appendChild(document.createTextNode('Show bounding sphere'));
+ tileBoundingSphere.appendChild(document.createTextNode('Show bounding volume'));
var renderTile = document.createElement('div');
pickTileRequired.appendChild(renderTile);
diff --git a/Source/Workers/createVerticesFromHeightmap.js b/Source/Workers/createVerticesFromHeightmap.js
index 7fad38e31f49..48ed1d95c9b5 100644
--- a/Source/Workers/createVerticesFromHeightmap.js
+++ b/Source/Workers/createVerticesFromHeightmap.js
@@ -4,6 +4,8 @@ define([
'../Core/Ellipsoid',
'../Core/EllipsoidalOccluder',
'../Core/HeightmapTessellator',
+ '../Core/Math',
+ '../Core/OrientedBoundingBox',
'../Core/Rectangle',
'./createTaskProcessorWorker'
], function(
@@ -11,6 +13,8 @@ define([
Ellipsoid,
EllipsoidalOccluder,
HeightmapTessellator,
+ CesiumMath,
+ OrientedBoundingBox,
Rectangle,
createTaskProcessorWorker) {
"use strict";
@@ -36,6 +40,12 @@ define([
var statistics = HeightmapTessellator.computeVertices(parameters);
var boundingSphere3D = BoundingSphere.fromVertices(vertices, parameters.relativeToCenter, numberOfAttributes);
+ var orientedBoundingBox;
+ if (parameters.rectangle.width < CesiumMath.PI_OVER_TWO + CesiumMath.EPSILON5) {
+ // Here, rectangle.width < pi/2, and rectangle.height < pi
+ // (though it would still work with rectangle.width up to pi)
+ orientedBoundingBox = OrientedBoundingBox.fromRectangle(parameters.rectangle, statistics.minimumHeight, statistics.maximumHeight, parameters.ellipsoid);
+ }
var ellipsoid = parameters.ellipsoid;
var occluder = new EllipsoidalOccluder(ellipsoid);
@@ -49,6 +59,7 @@ define([
gridWidth : arrayWidth,
gridHeight : arrayHeight,
boundingSphere3D : boundingSphere3D,
+ orientedBoundingBox : orientedBoundingBox,
occludeePointInScaledSpace : occludeePointInScaledSpace
};
}
diff --git a/Source/Workers/upsampleQuantizedTerrainMesh.js b/Source/Workers/upsampleQuantizedTerrainMesh.js
index 034d5f9c5a70..7dbcb81441ac 100644
--- a/Source/Workers/upsampleQuantizedTerrainMesh.js
+++ b/Source/Workers/upsampleQuantizedTerrainMesh.js
@@ -11,6 +11,7 @@ define([
'../Core/IndexDatatype',
'../Core/Intersections2D',
'../Core/Math',
+ '../Core/OrientedBoundingBox',
'./createTaskProcessorWorker'
], function(
AttributeCompression,
@@ -24,6 +25,7 @@ define([
IndexDatatype,
Intersections2D,
CesiumMath,
+ OrientedBoundingBox,
createTaskProcessorWorker) {
"use strict";
@@ -42,6 +44,7 @@ define([
var normalsScratch = [];
var horizonOcclusionPointScratch = new Cartesian3();
var boundingSphereScratch = new BoundingSphere();
+ var orientedBoundingBoxScratch = new OrientedBoundingBox();
function upsampleQuantizedTerrainMesh(parameters, transferableObjects) {
var isEastChild = parameters.isEastChild;
@@ -239,6 +242,7 @@ define([
}
var boundingSphere = BoundingSphere.fromVertices(cartesianVertices, Cartesian3.ZERO, 3, boundingSphereScratch);
+ var orientedBoundingBox = OrientedBoundingBox.fromRectangle(rectangle, minimumHeight, maximumHeight, ellipsoid, orientedBoundingBoxScratch);
var occluder = new EllipsoidalOccluder(ellipsoid);
var horizonOcclusionPoint = occluder.computeHorizonCullingPointFromVertices(boundingSphere.center, cartesianVertices, 3, boundingSphere.center, horizonOcclusionPointScratch);
@@ -285,6 +289,7 @@ define([
eastIndices : eastIndices,
northIndices : northIndices,
boundingSphere : boundingSphere,
+ orientedBoundingBox : orientedBoundingBox,
horizonOcclusionPoint : horizonOcclusionPoint
};
}
diff --git a/Specs/Core/AxisAlignedBoundingBoxSpec.js b/Specs/Core/AxisAlignedBoundingBoxSpec.js
index acfa831e21c3..f6ac5c5d7446 100644
--- a/Specs/Core/AxisAlignedBoundingBoxSpec.js
+++ b/Specs/Core/AxisAlignedBoundingBoxSpec.js
@@ -3,12 +3,14 @@ defineSuite([
'Core/AxisAlignedBoundingBox',
'Core/Cartesian3',
'Core/Cartesian4',
- 'Core/Intersect'
+ 'Core/Intersect',
+ 'Core/Plane'
], function(
AxisAlignedBoundingBox,
Cartesian3,
Cartesian4,
- Intersect) {
+ Intersect,
+ Plane) {
"use strict";
/*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn*/
@@ -115,45 +117,53 @@ defineSuite([
expect(box.center).toEqual(positions[0]);
});
- it('intersect works with box on the positive side of a plane', function() {
+ it('intersectPlane works with box on the positive side of a plane', function() {
var box = new AxisAlignedBoundingBox(Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), Cartesian3.ZERO);
var normal = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3());
var position = Cartesian3.UNIT_X;
- var plane = new Cartesian4(normal.x, normal.y, normal.z, -Cartesian3.dot(normal, position));
- expect(box.intersect(plane)).toEqual(Intersect.INSIDE);
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(box.intersectPlane(plane)).toEqual(Intersect.INSIDE);
});
- it('intersect works with box on the negative side of a plane', function() {
+ it('intersectPlane works with box on the negative side of a plane', function() {
var box = new AxisAlignedBoundingBox(Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), Cartesian3.ZERO);
var normal = Cartesian3.UNIT_X;
var position = Cartesian3.UNIT_X;
- var plane = new Cartesian4(normal.x, normal.y, normal.z, -Cartesian3.dot(normal, position));
- expect(box.intersect(plane)).toEqual(Intersect.OUTSIDE);
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(box.intersectPlane(plane)).toEqual(Intersect.OUTSIDE);
});
- it('intersect works with box intersecting a plane', function() {
+ it('intersectPlane works with box intersecting a plane', function() {
var box = new AxisAlignedBoundingBox(Cartesian3.ZERO, Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()));
var normal = Cartesian3.UNIT_X;
var position = Cartesian3.UNIT_X;
- var plane = new Cartesian4(normal.x, normal.y, normal.z, -Cartesian3.dot(normal, position));
- expect(box.intersect(plane)).toEqual(Intersect.INTERSECTING);
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(box.intersectPlane(plane)).toEqual(Intersect.INTERSECTING);
+ });
+
+ it('intersect works the same as intersectPlane in one case', function() {
+ var box = new AxisAlignedBoundingBox(Cartesian3.ZERO, Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()));
+ var normal = Cartesian3.UNIT_X;
+ var position = Cartesian3.UNIT_X;
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(box.intersect(new Cartesian4(1.0, 0.0, 0.0, -1.0))).toEqual(box.intersectPlane(plane));
});
it('clone returns undefined with no parameter', function() {
expect(AxisAlignedBoundingBox.clone()).toBeUndefined();
});
- it('intersect throws without a box', function() {
- var plane = new Cartesian4();
+ it('intersectPlane throws without a box', function() {
+ var plane = new Plane(Cartesian3.UNIT_X, 0.0);
expect(function() {
- AxisAlignedBoundingBox.intersect(undefined, plane);
+ AxisAlignedBoundingBox.intersectPlane(undefined, plane);
}).toThrowDeveloperError();
});
- it('intersect throws without a plane', function() {
+ it('intersectPlane throws without a plane', function() {
var box = new AxisAlignedBoundingBox();
expect(function() {
- AxisAlignedBoundingBox.intersect(box, undefined);
+ AxisAlignedBoundingBox.intersectPlane(box, undefined);
}).toThrowDeveloperError();
});
});
diff --git a/Specs/Core/BoundingSphereSpec.js b/Specs/Core/BoundingSphereSpec.js
index 1ce8e61dd3b4..fb2971ac6068 100644
--- a/Specs/Core/BoundingSphereSpec.js
+++ b/Specs/Core/BoundingSphereSpec.js
@@ -10,6 +10,7 @@ defineSuite([
'Core/Interval',
'Core/Math',
'Core/Matrix4',
+ 'Core/Plane',
'Core/Rectangle',
'Specs/createPackableSpecs'
], function(
@@ -23,6 +24,7 @@ defineSuite([
Interval,
CesiumMath,
Matrix4,
+ Plane,
Rectangle,
createPackableSpecs) {
"use strict";
@@ -384,28 +386,36 @@ defineSuite([
expect(sphere).toEqual(expected);
});
- it('sphere on the positive side of a plane', function() {
+ it('intersectPlane with sphere on the positive side of a plane', function() {
var sphere = new BoundingSphere(Cartesian3.ZERO, 0.5);
var normal = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3());
var position = Cartesian3.UNIT_X;
- var plane = new Cartesian4(normal.x, normal.y, normal.z, -Cartesian3.dot(normal, position));
- expect(sphere.intersect(plane)).toEqual(Intersect.INSIDE);
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(sphere.intersectPlane(plane)).toEqual(Intersect.INSIDE);
});
- it('sphere on the negative side of a plane', function() {
+ it('intersectPlane with sphere on the negative side of a plane', function() {
var sphere = new BoundingSphere(Cartesian3.ZERO, 0.5);
var normal = Cartesian3.UNIT_X;
var position = Cartesian3.UNIT_X;
- var plane = new Cartesian4(normal.x, normal.y, normal.z, -Cartesian3.dot(normal, position));
- expect(sphere.intersect(plane)).toEqual(Intersect.OUTSIDE);
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(sphere.intersectPlane(plane)).toEqual(Intersect.OUTSIDE);
});
- it('sphere intersecting a plane', function() {
+ it('intersectPlane with sphere intersecting a plane', function() {
var sphere = new BoundingSphere(Cartesian3.UNIT_X, 0.5);
var normal = Cartesian3.UNIT_X;
var position = Cartesian3.UNIT_X;
- var plane = new Cartesian4(normal.x, normal.y, normal.z, -Cartesian3.dot(normal, position));
- expect(sphere.intersect(plane)).toEqual(Intersect.INTERSECTING);
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(sphere.intersectPlane(plane)).toEqual(Intersect.INTERSECTING);
+ });
+
+ it('intersect works the same as intersectPlane in one case', function() {
+ var sphere = new BoundingSphere(Cartesian3.UNIT_X, 0.5);
+ var normal = Cartesian3.UNIT_X;
+ var position = Cartesian3.UNIT_X;
+ var plane = new Plane(normal, -Cartesian3.dot(normal, position));
+ expect(sphere.intersect(new Cartesian4(1.0, 0.0, 0.0, -1.0))).toEqual(sphere.intersectPlane(plane));
});
it('expands to contain another sphere', function() {
@@ -599,17 +609,17 @@ defineSuite([
}).toThrowDeveloperError();
});
- it('intersect throws without a sphere', function() {
- var plane = new Cartesian4();
+ it('intersectPlane throws without a sphere', function() {
+ var plane = new Plane(Cartesian3.UNIT_X, 0.0);
expect(function() {
- BoundingSphere.intersect(undefined, plane);
+ BoundingSphere.intersectPlane(undefined, plane);
}).toThrowDeveloperError();
});
- it('intersect throws without a plane', function() {
+ it('intersectPlane throws without a plane', function() {
var sphere = new BoundingSphere();
expect(function() {
- BoundingSphere.intersect(sphere, undefined);
+ BoundingSphere.intersectPlane(sphere, undefined);
}).toThrowDeveloperError();
});
diff --git a/Specs/Core/EllipsoidTangentPlaneSpec.js b/Specs/Core/EllipsoidTangentPlaneSpec.js
index 175dd38fb696..bf88122417c1 100644
--- a/Specs/Core/EllipsoidTangentPlaneSpec.js
+++ b/Specs/Core/EllipsoidTangentPlaneSpec.js
@@ -3,12 +3,18 @@ defineSuite([
'Core/EllipsoidTangentPlane',
'Core/Cartesian2',
'Core/Cartesian3',
- 'Core/Ellipsoid'
+ 'Core/Ellipsoid',
+ 'Core/Math',
+ 'Core/Matrix3',
+ 'Core/OrientedBoundingBox'
], function(
EllipsoidTangentPlane,
Cartesian2,
Cartesian3,
- Ellipsoid) {
+ Ellipsoid,
+ CesiumMath,
+ Matrix3,
+ OrientedBoundingBox) {
"use strict";
/*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn*/
@@ -32,7 +38,7 @@ defineSuite([
expect(tangentPlane.origin).toEqual(Cartesian3.UNIT_X);
});
- it('projectPointOntoPlane returns undefined for points not on the plane', function () {
+ it('projectPointOntoPlane returns undefined for unsolvable projections', function () {
var ellipsoid = Ellipsoid.UNIT_SPHERE;
var origin = new Cartesian3(1, 0, 0);
var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
@@ -92,7 +98,7 @@ defineSuite([
expect(returnedResults).toEqual(expectedResults);
});
- it('projectPointsOntoPlane works when some points not on plane', function () {
+ it('projectPointsOntoPlane works when some points cannot be projected', function () {
var ellipsoid = Ellipsoid.UNIT_SPHERE;
var origin = new Cartesian3(1, 0, 0);
var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
@@ -129,6 +135,68 @@ defineSuite([
expect(returnedResults).toEqual(expectedResults);
});
+ it('projectPointToNearestOnPlane works without a result parameter', function () {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+ var origin = new Cartesian3(1, 0, 0);
+ var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
+
+ var positions = new Cartesian3(1, 0, 1);
+ var expectedResult = new Cartesian2(0, 1);
+ var returnedResult = tangentPlane.projectPointToNearestOnPlane(positions);
+ expect(returnedResult).toEqual(expectedResult);
+ });
+
+ it('projectPointToNearestOnPlane works projecting from various distances', function () {
+ var ellipsoid = Ellipsoid.ZERO;
+ var origin = new Cartesian3(1, 0, 0);
+ var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
+
+ expect(tangentPlane.projectPointToNearestOnPlane(new Cartesian3(2, 0, 0))).toEqual(new Cartesian2(0, 0));
+ expect(tangentPlane.projectPointToNearestOnPlane(new Cartesian3(1, 0, 0))).toEqual(new Cartesian2(0, 0));
+ expect(tangentPlane.projectPointToNearestOnPlane(new Cartesian3(0, 0, 0))).toEqual(new Cartesian2(0, 0));
+ expect(tangentPlane.projectPointToNearestOnPlane(new Cartesian3(-1, 0, 0))).toEqual(new Cartesian2(0, 0));
+ });
+
+ it('projectPointToNearestOnPlane works with a result parameter', function () {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+ var origin = new Cartesian3(1, 0, 0);
+ var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
+
+ var positions = new Cartesian3(1, 0, 1);
+ var expectedResult = new Cartesian2(0, 1);
+ var result = new Cartesian2();
+ var returnedResult = tangentPlane.projectPointToNearestOnPlane(positions, result);
+ expect(result).toBe(returnedResult);
+ expect(returnedResult).toEqual(expectedResult);
+ });
+
+ it('projectPointsToNearestOnPlane works without a result parameter', function () {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+ var origin = new Cartesian3(1, 0, 0);
+ var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
+
+ var positions = [new Cartesian3(1, 0, 1), new Cartesian3(1, 0, 0), new Cartesian3(1, 1, 0)];
+ var expectedResults = [new Cartesian2(0, 1), new Cartesian2(0, 0), new Cartesian2(1, 0)];
+ var returnedResults = tangentPlane.projectPointsToNearestOnPlane(positions);
+ expect(returnedResults).toEqual(expectedResults);
+ });
+
+ it('projectPointsToNearestOnPlane works with a result parameter', function () {
+ var ellipsoid = Ellipsoid.UNIT_SPHERE;
+ var origin = new Cartesian3(1, 0, 0);
+ var tangentPlane = new EllipsoidTangentPlane(origin, ellipsoid);
+
+ var positions = [new Cartesian3(1, 0, 1), new Cartesian3(1, 0, 0), new Cartesian3(1, 1, 0)];
+ var expectedResults = [new Cartesian2(0, 1), new Cartesian2(0, 0), new Cartesian2(1, 0)];
+
+ var index0 = new Cartesian2();
+ var result = [index0];
+ var returnedResults = tangentPlane.projectPointsToNearestOnPlane(positions, result);
+ expect(result).toBe(returnedResults);
+ expect(result[0]).toBe(index0);
+ expect(returnedResults).toEqual(expectedResults);
+ });
+
it('constructor throws without origin', function() {
expect(function() {
return new EllipsoidTangentPlane(undefined, Ellipsoid.WGS84);
@@ -161,6 +229,20 @@ defineSuite([
}).toThrowDeveloperError();
});
+ it('projectPointToNearestOnPlane throws without cartesian', function() {
+ var tangentPlane = new EllipsoidTangentPlane(Cartesian3.UNIT_X, Ellipsoid.UNIT_SPHERE);
+ expect(function() {
+ return tangentPlane.projectPointToNearestOnPlane(undefined);
+ }).toThrowDeveloperError();
+ });
+
+ it('projectPointsToNearestOnPlane throws without cartesians', function() {
+ var tangentPlane = new EllipsoidTangentPlane(Cartesian3.UNIT_X, Ellipsoid.UNIT_SPHERE);
+ expect(function() {
+ return tangentPlane.projectPointsToNearestOnPlane(undefined);
+ }).toThrowDeveloperError();
+ });
+
it('projectPointsOntoEllipsoid throws without cartesians', function() {
var tangentPlane = new EllipsoidTangentPlane(Cartesian3.UNIT_X, Ellipsoid.UNIT_SPHERE);
expect(function() {
diff --git a/Specs/Core/Matrix2Spec.js b/Specs/Core/Matrix2Spec.js
index b4213e59856b..52331f275aeb 100644
--- a/Specs/Core/Matrix2Spec.js
+++ b/Specs/Core/Matrix2Spec.js
@@ -367,6 +367,25 @@ defineSuite([
expect(left).toEqual(expected);
});
+ it('multiplyByScale works', function() {
+ var m = new Matrix2(2, 3, 6, 7);
+ var scale = new Cartesian2(2.0, 3.0);
+ var expected = Matrix2.multiply(m, Matrix2.fromScale(scale), new Matrix2());
+ var result = new Matrix2();
+ var returnedResult = Matrix2.multiplyByScale(m, scale, result);
+ expect(returnedResult).toBe(result);
+ expect(result).toEqual(expected);
+ });
+
+ it('multiplyByScale works with "this" result parameter', function() {
+ var m = new Matrix2(1, 2, 5, 6);
+ var scale = new Cartesian2(1.0, 2.0);
+ var expected = Matrix2.multiply(m, Matrix2.fromScale(scale), new Matrix2());
+ var returnedResult = Matrix2.multiplyByScale(m, scale, m);
+ expect(returnedResult).toBe(m);
+ expect(m).toEqual(expected);
+ });
+
it('multiplyByVector works', function() {
var left = new Matrix2(1, 2, 3, 4);
var right = new Cartesian2(5, 6);
@@ -656,6 +675,19 @@ defineSuite([
}).toThrowDeveloperError();
});
+ it('multiplyByScale throws with no matrix parameter', function() {
+ expect(function() {
+ Matrix2.multiplyByScale(undefined, new Cartesian2());
+ }).toThrowDeveloperError();
+ });
+
+ it('multiplyByScale throws with no scale parameter', function() {
+ var m = new Matrix2();
+ expect(function() {
+ Matrix2.multiplyByScale(m, undefined);
+ }).toThrowDeveloperError();
+ });
+
it('multiplyByVector throws with no matrix parameter', function() {
var cartesian = new Cartesian2();
expect(function() {
@@ -737,6 +769,12 @@ defineSuite([
}).toThrowDeveloperError();
});
+ it('multiplyByScale throws without result parameter', function() {
+ expect(function() {
+ Matrix2.multiplyByScale(new Matrix2(), new Cartesian2());
+ }).toThrowDeveloperError();
+ });
+
it('multiplyByVector throws without result parameter', function() {
expect(function() {
Matrix2.multiplyByVector(new Matrix2(), new Cartesian2());
diff --git a/Specs/Core/Matrix3Spec.js b/Specs/Core/Matrix3Spec.js
index 17499aa7d289..b70a41bfb410 100644
--- a/Specs/Core/Matrix3Spec.js
+++ b/Specs/Core/Matrix3Spec.js
@@ -530,6 +530,25 @@ defineSuite([
expect(left).toEqual(expected);
});
+ it('multiplyByScale works', function() {
+ var m = new Matrix3(2, 3, 4, 6, 7, 8, 10, 11, 12);
+ var scale = new Cartesian3(2.0, 3.0, 4.0);
+ var expected = Matrix3.multiply(m, Matrix3.fromScale(scale), new Matrix3());
+ var result = new Matrix3();
+ var returnedResult = Matrix3.multiplyByScale(m, scale, result);
+ expect(returnedResult).toBe(result);
+ expect(result).toEqual(expected);
+ });
+
+ it('multiplyByScale works with "this" result parameter', function() {
+ var m = new Matrix3(1, 2, 3, 5, 6, 7, 9, 10, 11);
+ var scale = new Cartesian3(1.0, 2.0, 3.0);
+ var expected = Matrix3.multiply(m, Matrix3.fromScale(scale), new Matrix3());
+ var returnedResult = Matrix3.multiplyByScale(m, scale, m);
+ expect(returnedResult).toBe(m);
+ expect(m).toEqual(expected);
+ });
+
it('multiplyByVector works', function() {
var left = new Matrix3(1, 2, 3, 4, 5, 6, 7, 8, 9);
var right = new Cartesian3(10, 11, 12);
@@ -937,6 +956,19 @@ defineSuite([
}).toThrowDeveloperError();
});
+ it('multiplyByScale throws with no matrix parameter', function() {
+ expect(function() {
+ Matrix3.multiplyByScale(undefined, new Cartesian3());
+ }).toThrowDeveloperError();
+ });
+
+ it('multiplyByScale throws with no scale parameter', function() {
+ var m = new Matrix3();
+ expect(function() {
+ Matrix3.multiplyByScale(m, undefined);
+ }).toThrowDeveloperError();
+ });
+
it('multiplyByVector throws with no matrix parameter', function() {
var cartesian = new Cartesian3();
expect(function() {
@@ -1054,6 +1086,12 @@ defineSuite([
}).toThrowDeveloperError();
});
+ it('multiplyByScale throws without result parameter', function() {
+ expect(function() {
+ Matrix3.multiplyByScale(new Matrix3(), new Cartesian3());
+ }).toThrowDeveloperError();
+ });
+
it('multiplyByVector throws without result parameter', function() {
expect(function() {
Matrix3.multiplyByVector(new Matrix3(), new Cartesian3());
diff --git a/Specs/Core/Matrix4Spec.js b/Specs/Core/Matrix4Spec.js
index 2b95adf077a1..5dc0e0f0d458 100644
--- a/Specs/Core/Matrix4Spec.js
+++ b/Specs/Core/Matrix4Spec.js
@@ -662,6 +662,14 @@ defineSuite([
var returnedResult = Matrix4.multiplyByScale(m, scale, result);
expect(returnedResult).toBe(result);
expect(result).toEqual(expected);
+
+ m = new Matrix4(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 1);
+ scale = new Cartesian3(2.0, 3.0, 4.0);
+ expected = Matrix4.multiply(m, Matrix4.fromScale(scale), new Matrix4());
+ result = new Matrix4();
+ returnedResult = Matrix4.multiplyByScale(m, scale, result);
+ expect(returnedResult).toBe(result);
+ expect(result).toEqual(expected);
});
it('multiplyByScale works with "this" result parameter', function() {
diff --git a/Specs/Core/OrientedBoundingBoxSpec.js b/Specs/Core/OrientedBoundingBoxSpec.js
new file mode 100644
index 000000000000..e55d78762cc3
--- /dev/null
+++ b/Specs/Core/OrientedBoundingBoxSpec.js
@@ -0,0 +1,487 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/OrientedBoundingBox',
+ 'Core/BoundingRectangle',
+ 'Core/Cartesian3',
+ 'Core/Cartesian4',
+ 'Core/Ellipsoid',
+ 'Core/EllipsoidTangentPlane',
+ 'Core/Intersect',
+ 'Core/Math',
+ 'Core/Matrix3',
+ 'Core/Plane',
+ 'Core/Quaternion',
+ 'Core/Rectangle'
+ ], function(
+ OrientedBoundingBox,
+ BoundingRectangle,
+ Cartesian3,
+ Cartesian4,
+ Ellipsoid,
+ EllipsoidTangentPlane,
+ Intersect,
+ CesiumMath,
+ Matrix3,
+ Plane,
+ Quaternion,
+ Rectangle) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn*/
+
+ 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)
+ ];
+
+ var positions2 = [
+ new Cartesian3(4.0, 0.0, 0.0),
+ new Cartesian3(0.0, 3.0, 0.0),
+ new Cartesian3(0.0, 0.0, 2.0),
+ new Cartesian3(-4.0, 0.0, 0.0),
+ new Cartesian3(0.0, -3.0, 0.0),
+ new Cartesian3(0.0, 0.0, -2.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('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);
+
+ var rotScale = Matrix3.ZERO;
+ 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('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();
+ var box2 = 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);
+ });
+});
diff --git a/Specs/Core/PlaneSpec.js b/Specs/Core/PlaneSpec.js
index 5c92f1ea8f35..1d7d52dedbb6 100644
--- a/Specs/Core/PlaneSpec.js
+++ b/Specs/Core/PlaneSpec.js
@@ -1,10 +1,12 @@
/*global defineSuite*/
defineSuite([
'Core/Plane',
- 'Core/Cartesian3'
+ 'Core/Cartesian3',
+ 'Core/Cartesian4'
], function(
Plane,
- Cartesian3) {
+ Cartesian3,
+ Cartesian4) {
"use strict";
/*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn*/
@@ -47,6 +49,21 @@ defineSuite([
expect(plane.distance).toEqual(-Cartesian3.dot(normal, point));
});
+ it('constructs from a Cartesian4 without result', function() {
+ var result = Plane.fromCartesian4(Cartesian4.UNIT_X);
+
+ expect(result.normal).toEqual(Cartesian3.UNIT_X);
+ expect(result.distance).toEqual(0.0);
+ });
+
+ it('constructs from a Cartesian4 with result', function() {
+ var result = new Plane(Cartesian3.UNIT_X, 0.0);
+ Plane.fromCartesian4(Cartesian4.UNIT_X, result);
+
+ expect(result.normal).toEqual(Cartesian3.UNIT_X);
+ expect(result.distance).toEqual(0.0);
+ });
+
it('fromPointNormal throws without a point', function() {
expect(function() {
return Plane.fromPointNormal(undefined, Cartesian3.UNIT_X);
@@ -59,6 +76,12 @@ defineSuite([
}).toThrowDeveloperError();
});
+ it('fromCartesian4 throws without coefficients', function() {
+ expect(function() {
+ return Plane.fromCartesian4(undefined);
+ }).toThrowDeveloperError();
+ });
+
it('gets the distance to a point', function() {
var plane = new Plane(new Cartesian3(1.0, 2.0, 3.0), 12.34);
var point = new Cartesian3(4.0, 5.0, 6.0);
@@ -66,10 +89,17 @@ defineSuite([
expect(Plane.getPointDistance(plane, point)).toEqual(Cartesian3.dot(plane.normal, point) + plane.distance);
});
+ it('getPointDistance throws without a plane', function() {
+ var point = Cartesian3.ZERO;
+ expect(function() {
+ return Plane.getPointDistance(undefined, point);
+ }).toThrowDeveloperError();
+ });
+
it('getPointDistance throws without a point', function() {
var plane = new Plane(Cartesian3.UNIT_X, 0.0);
expect(function() {
- return Plane.getPointDistance();
+ return Plane.getPointDistance(plane, undefined);
}).toThrowDeveloperError();
});
});