UNPKG

cesium

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,340 lines (1,167 loc) 502 kB
/* This file is automatically rebuilt by the Cesium build process. */ define(['exports', './Matrix2-fc7e9822', './RuntimeError-c581ca93', './defaultValue-94c3e563', './ComponentDatatype-4a60b8d6', './_commonjsHelpers-3aae1032-f55dc0c4', './combine-761d9c3f'], (function (exports, Matrix2, RuntimeError, defaultValue, ComponentDatatype, _commonjsHelpers3aae1032, combine) { 'use strict'; /** * A simple map projection where longitude and latitude are linearly mapped to X and Y by multiplying * them by the {@link Ellipsoid#maximumRadius}. This projection * is commonly known as geographic, equirectangular, equidistant cylindrical, or plate carrée. It * is also known as EPSG:4326. * * @alias GeographicProjection * @constructor * * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid. * * @see WebMercatorProjection */ function GeographicProjection(ellipsoid) { this._ellipsoid = defaultValue.defaultValue(ellipsoid, Matrix2.Ellipsoid.WGS84); this._semimajorAxis = this._ellipsoid.maximumRadius; this._oneOverSemimajorAxis = 1.0 / this._semimajorAxis; } Object.defineProperties(GeographicProjection.prototype, { /** * Gets the {@link Ellipsoid}. * * @memberof GeographicProjection.prototype * * @type {Ellipsoid} * @readonly */ ellipsoid: { get: function () { return this._ellipsoid; }, }, }); /** * Projects a set of {@link Cartographic} coordinates, in radians, to map coordinates, in meters. * X and Y are the longitude and latitude, respectively, multiplied by the maximum radius of the * ellipsoid. Z is the unmodified height. * * @param {Cartographic} cartographic The coordinates to project. * @param {Cartesian3} [result] An instance into which to copy the result. If this parameter is * undefined, a new instance is created and returned. * @returns {Cartesian3} The projected coordinates. If the result parameter is not undefined, the * coordinates are copied there and that instance is returned. Otherwise, a new instance is * created and returned. */ GeographicProjection.prototype.project = function (cartographic, result) { // Actually this is the special case of equidistant cylindrical called the plate carree const semimajorAxis = this._semimajorAxis; const x = cartographic.longitude * semimajorAxis; const y = cartographic.latitude * semimajorAxis; const z = cartographic.height; if (!defaultValue.defined(result)) { return new Matrix2.Cartesian3(x, y, z); } result.x = x; result.y = y; result.z = z; return result; }; /** * Unprojects a set of projected {@link Cartesian3} coordinates, in meters, to {@link Cartographic} * coordinates, in radians. Longitude and Latitude are the X and Y coordinates, respectively, * divided by the maximum radius of the ellipsoid. Height is the unmodified Z coordinate. * * @param {Cartesian3} cartesian The Cartesian position to unproject with height (z) in meters. * @param {Cartographic} [result] An instance into which to copy the result. If this parameter is * undefined, a new instance is created and returned. * @returns {Cartographic} The unprojected coordinates. If the result parameter is not undefined, the * coordinates are copied there and that instance is returned. Otherwise, a new instance is * created and returned. */ GeographicProjection.prototype.unproject = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); if (!defaultValue.defined(cartesian)) { throw new RuntimeError.DeveloperError("cartesian is required"); } //>>includeEnd('debug'); const oneOverEarthSemimajorAxis = this._oneOverSemimajorAxis; const longitude = cartesian.x * oneOverEarthSemimajorAxis; const latitude = cartesian.y * oneOverEarthSemimajorAxis; const height = cartesian.z; if (!defaultValue.defined(result)) { return new Matrix2.Cartographic(longitude, latitude, height); } result.longitude = longitude; result.latitude = latitude; result.height = height; return result; }; /** * This enumerated type is used in determining where, relative to the frustum, an * object is located. The object can either be fully contained within the frustum (INSIDE), * partially inside the frustum and partially outside (INTERSECTING), or somewhere entirely * outside of the frustum's 6 planes (OUTSIDE). * * @enum {Number} */ const Intersect = { /** * Represents that an object is not contained within the frustum. * * @type {Number} * @constant */ OUTSIDE: -1, /** * Represents that an object intersects one of the frustum's planes. * * @type {Number} * @constant */ INTERSECTING: 0, /** * Represents that an object is fully within the frustum. * * @type {Number} * @constant */ INSIDE: 1, }; var Intersect$1 = Object.freeze(Intersect); /** * Represents the closed interval [start, stop]. * @alias Interval * @constructor * * @param {Number} [start=0.0] The beginning of the interval. * @param {Number} [stop=0.0] The end of the interval. */ function Interval(start, stop) { /** * The beginning of the interval. * @type {Number} * @default 0.0 */ this.start = defaultValue.defaultValue(start, 0.0); /** * The end of the interval. * @type {Number} * @default 0.0 */ this.stop = defaultValue.defaultValue(stop, 0.0); } /** * A bounding sphere with a center and a radius. * @alias BoundingSphere * @constructor * * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the bounding sphere. * @param {Number} [radius=0.0] The radius of the bounding sphere. * * @see AxisAlignedBoundingBox * @see BoundingRectangle * @see Packable */ function BoundingSphere(center, radius) { /** * The center point of the sphere. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.center = Matrix2.Cartesian3.clone(defaultValue.defaultValue(center, Matrix2.Cartesian3.ZERO)); /** * The radius of the sphere. * @type {Number} * @default 0.0 */ this.radius = defaultValue.defaultValue(radius, 0.0); } const fromPointsXMin = new Matrix2.Cartesian3(); const fromPointsYMin = new Matrix2.Cartesian3(); const fromPointsZMin = new Matrix2.Cartesian3(); const fromPointsXMax = new Matrix2.Cartesian3(); const fromPointsYMax = new Matrix2.Cartesian3(); const fromPointsZMax = new Matrix2.Cartesian3(); const fromPointsCurrentPos = new Matrix2.Cartesian3(); const fromPointsScratch = new Matrix2.Cartesian3(); const fromPointsRitterCenter = new Matrix2.Cartesian3(); const fromPointsMinBoxPt = new Matrix2.Cartesian3(); const fromPointsMaxBoxPt = new Matrix2.Cartesian3(); const fromPointsNaiveCenterScratch = new Matrix2.Cartesian3(); const volumeConstant = (4.0 / 3.0) * ComponentDatatype.CesiumMath.PI; /** * Computes a tight-fitting bounding sphere enclosing a list of 3D Cartesian points. * The bounding sphere is computed by running two algorithms, a naive algorithm and * Ritter's algorithm. The smaller of the two spheres is used to ensure a tight fit. * * @param {Cartesian3[]} [positions] An array of points that the bounding sphere will enclose. Each point must have <code>x</code>, <code>y</code>, and <code>z</code> properties. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. * * @see {@link http://help.agi.com/AGIComponents/html/BlogBoundingSphere.htm|Bounding Sphere computation article} */ BoundingSphere.fromPoints = function (positions, result) { if (!defaultValue.defined(result)) { result = new BoundingSphere(); } if (!defaultValue.defined(positions) || positions.length === 0) { result.center = Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } const currentPos = Matrix2.Cartesian3.clone(positions[0], fromPointsCurrentPos); const xMin = Matrix2.Cartesian3.clone(currentPos, fromPointsXMin); const yMin = Matrix2.Cartesian3.clone(currentPos, fromPointsYMin); const zMin = Matrix2.Cartesian3.clone(currentPos, fromPointsZMin); const xMax = Matrix2.Cartesian3.clone(currentPos, fromPointsXMax); const yMax = Matrix2.Cartesian3.clone(currentPos, fromPointsYMax); const zMax = Matrix2.Cartesian3.clone(currentPos, fromPointsZMax); const numPositions = positions.length; let i; for (i = 1; i < numPositions; i++) { Matrix2.Cartesian3.clone(positions[i], currentPos); const x = currentPos.x; const y = currentPos.y; const z = currentPos.z; // Store points containing the the smallest and largest components if (x < xMin.x) { Matrix2.Cartesian3.clone(currentPos, xMin); } if (x > xMax.x) { Matrix2.Cartesian3.clone(currentPos, xMax); } if (y < yMin.y) { Matrix2.Cartesian3.clone(currentPos, yMin); } if (y > yMax.y) { Matrix2.Cartesian3.clone(currentPos, yMax); } if (z < zMin.z) { Matrix2.Cartesian3.clone(currentPos, zMin); } if (z > zMax.z) { Matrix2.Cartesian3.clone(currentPos, zMax); } } // Compute x-, y-, and z-spans (Squared distances b/n each component's min. and max.). const xSpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(xMax, xMin, fromPointsScratch) ); const ySpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(yMax, yMin, fromPointsScratch) ); const zSpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(zMax, zMin, fromPointsScratch) ); // Set the diameter endpoints to the largest span. let diameter1 = xMin; let diameter2 = xMax; let maxSpan = xSpan; if (ySpan > maxSpan) { maxSpan = ySpan; diameter1 = yMin; diameter2 = yMax; } if (zSpan > maxSpan) { maxSpan = zSpan; diameter1 = zMin; diameter2 = zMax; } // Calculate the center of the initial sphere found by Ritter's algorithm const ritterCenter = fromPointsRitterCenter; ritterCenter.x = (diameter1.x + diameter2.x) * 0.5; ritterCenter.y = (diameter1.y + diameter2.y) * 0.5; ritterCenter.z = (diameter1.z + diameter2.z) * 0.5; // Calculate the radius of the initial sphere found by Ritter's algorithm let radiusSquared = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(diameter2, ritterCenter, fromPointsScratch) ); let ritterRadius = Math.sqrt(radiusSquared); // Find the center of the sphere found using the Naive method. const minBoxPt = fromPointsMinBoxPt; minBoxPt.x = xMin.x; minBoxPt.y = yMin.y; minBoxPt.z = zMin.z; const maxBoxPt = fromPointsMaxBoxPt; maxBoxPt.x = xMax.x; maxBoxPt.y = yMax.y; maxBoxPt.z = zMax.z; const naiveCenter = Matrix2.Cartesian3.midpoint( minBoxPt, maxBoxPt, fromPointsNaiveCenterScratch ); // Begin 2nd pass to find naive radius and modify the ritter sphere. let naiveRadius = 0; for (i = 0; i < numPositions; i++) { Matrix2.Cartesian3.clone(positions[i], currentPos); // Find the furthest point from the naive center to calculate the naive radius. const r = Matrix2.Cartesian3.magnitude( Matrix2.Cartesian3.subtract(currentPos, naiveCenter, fromPointsScratch) ); if (r > naiveRadius) { naiveRadius = r; } // Make adjustments to the Ritter Sphere to include all points. const oldCenterToPointSquared = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(currentPos, ritterCenter, fromPointsScratch) ); if (oldCenterToPointSquared > radiusSquared) { const oldCenterToPoint = Math.sqrt(oldCenterToPointSquared); // Calculate new radius to include the point that lies outside ritterRadius = (ritterRadius + oldCenterToPoint) * 0.5; radiusSquared = ritterRadius * ritterRadius; // Calculate center of new Ritter sphere const oldToNew = oldCenterToPoint - ritterRadius; ritterCenter.x = (ritterRadius * ritterCenter.x + oldToNew * currentPos.x) / oldCenterToPoint; ritterCenter.y = (ritterRadius * ritterCenter.y + oldToNew * currentPos.y) / oldCenterToPoint; ritterCenter.z = (ritterRadius * ritterCenter.z + oldToNew * currentPos.z) / oldCenterToPoint; } } if (ritterRadius < naiveRadius) { Matrix2.Cartesian3.clone(ritterCenter, result.center); result.radius = ritterRadius; } else { Matrix2.Cartesian3.clone(naiveCenter, result.center); result.radius = naiveRadius; } return result; }; const defaultProjection = new GeographicProjection(); const fromRectangle2DLowerLeft = new Matrix2.Cartesian3(); const fromRectangle2DUpperRight = new Matrix2.Cartesian3(); const fromRectangle2DSouthwest = new Matrix2.Cartographic(); const fromRectangle2DNortheast = new Matrix2.Cartographic(); /** * Computes a bounding sphere from a rectangle projected in 2D. * * @param {Rectangle} [rectangle] The rectangle around which to create a bounding sphere. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle into 2D. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromRectangle2D = function (rectangle, projection, result) { return BoundingSphere.fromRectangleWithHeights2D( rectangle, projection, 0.0, 0.0, result ); }; /** * Computes a bounding sphere from a rectangle projected in 2D. The bounding sphere accounts for the * object's minimum and maximum heights over the rectangle. * * @param {Rectangle} [rectangle] The rectangle around which to create a bounding sphere. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle into 2D. * @param {Number} [minimumHeight=0.0] The minimum height over the rectangle. * @param {Number} [maximumHeight=0.0] The maximum height over the rectangle. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromRectangleWithHeights2D = function ( rectangle, projection, minimumHeight, maximumHeight, result ) { if (!defaultValue.defined(result)) { result = new BoundingSphere(); } if (!defaultValue.defined(rectangle)) { result.center = Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } projection = defaultValue.defaultValue(projection, defaultProjection); Matrix2.Rectangle.southwest(rectangle, fromRectangle2DSouthwest); fromRectangle2DSouthwest.height = minimumHeight; Matrix2.Rectangle.northeast(rectangle, fromRectangle2DNortheast); fromRectangle2DNortheast.height = maximumHeight; const lowerLeft = projection.project( fromRectangle2DSouthwest, fromRectangle2DLowerLeft ); const upperRight = projection.project( fromRectangle2DNortheast, fromRectangle2DUpperRight ); const width = upperRight.x - lowerLeft.x; const height = upperRight.y - lowerLeft.y; const elevation = upperRight.z - lowerLeft.z; result.radius = Math.sqrt(width * width + height * height + elevation * elevation) * 0.5; const center = result.center; center.x = lowerLeft.x + width * 0.5; center.y = lowerLeft.y + height * 0.5; center.z = lowerLeft.z + elevation * 0.5; return result; }; const fromRectangle3DScratch = []; /** * Computes a bounding sphere from a rectangle in 3D. The bounding sphere is created using a subsample of points * on the ellipsoid and contained in the rectangle. It may not be accurate for all rectangles on all types of ellipsoids. * * @param {Rectangle} [rectangle] The valid rectangle used to create a bounding sphere. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the rectangle. * @param {Number} [surfaceHeight=0.0] The height above the surface of the ellipsoid. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromRectangle3D = function ( rectangle, ellipsoid, surfaceHeight, result ) { ellipsoid = defaultValue.defaultValue(ellipsoid, Matrix2.Ellipsoid.WGS84); surfaceHeight = defaultValue.defaultValue(surfaceHeight, 0.0); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } if (!defaultValue.defined(rectangle)) { result.center = Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } const positions = Matrix2.Rectangle.subsample( rectangle, ellipsoid, surfaceHeight, fromRectangle3DScratch ); return BoundingSphere.fromPoints(positions, result); }; /** * Computes a tight-fitting bounding sphere enclosing a list of 3D points, where the points are * stored in a flat array in X, Y, Z, order. The bounding sphere is computed by running two * algorithms, a naive algorithm and Ritter's algorithm. The smaller of the two spheres is used to * ensure a tight fit. * * @param {Number[]} [positions] An array of points that the bounding sphere will enclose. Each point * is formed from three elements in the array in the order X, Y, Z. * @param {Cartesian3} [center=Cartesian3.ZERO] The position to which the positions are relative, which need not be the * origin of the coordinate system. This is useful when the positions are to be used for * relative-to-center (RTC) rendering. * @param {Number} [stride=3] The number of array elements per vertex. It must be at least 3, but it may * be higher. Regardless of the value of this parameter, the X coordinate of the first position * is at array index 0, the Y coordinate is at array index 1, and the Z coordinate is at array index * 2. When stride is 3, the X coordinate of the next position then begins at array index 3. If * the stride is 5, however, two array elements are skipped and the next position begins at array * index 5. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. * * @example * // Compute the bounding sphere from 3 positions, each specified relative to a center. * // In addition to the X, Y, and Z coordinates, the points array contains two additional * // elements per point which are ignored for the purpose of computing the bounding sphere. * const center = new Cesium.Cartesian3(1.0, 2.0, 3.0); * const points = [1.0, 2.0, 3.0, 0.1, 0.2, * 4.0, 5.0, 6.0, 0.1, 0.2, * 7.0, 8.0, 9.0, 0.1, 0.2]; * const sphere = Cesium.BoundingSphere.fromVertices(points, center, 5); * * @see {@link http://blogs.agi.com/insight3d/index.php/2008/02/04/a-bounding/|Bounding Sphere computation article} */ BoundingSphere.fromVertices = function (positions, center, stride, result) { if (!defaultValue.defined(result)) { result = new BoundingSphere(); } if (!defaultValue.defined(positions) || positions.length === 0) { result.center = Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } center = defaultValue.defaultValue(center, Matrix2.Cartesian3.ZERO); stride = defaultValue.defaultValue(stride, 3); //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.number.greaterThanOrEquals("stride", stride, 3); //>>includeEnd('debug'); const currentPos = fromPointsCurrentPos; currentPos.x = positions[0] + center.x; currentPos.y = positions[1] + center.y; currentPos.z = positions[2] + center.z; const xMin = Matrix2.Cartesian3.clone(currentPos, fromPointsXMin); const yMin = Matrix2.Cartesian3.clone(currentPos, fromPointsYMin); const zMin = Matrix2.Cartesian3.clone(currentPos, fromPointsZMin); const xMax = Matrix2.Cartesian3.clone(currentPos, fromPointsXMax); const yMax = Matrix2.Cartesian3.clone(currentPos, fromPointsYMax); const zMax = Matrix2.Cartesian3.clone(currentPos, fromPointsZMax); const numElements = positions.length; let i; for (i = 0; i < numElements; i += stride) { const x = positions[i] + center.x; const y = positions[i + 1] + center.y; const z = positions[i + 2] + center.z; currentPos.x = x; currentPos.y = y; currentPos.z = z; // Store points containing the the smallest and largest components if (x < xMin.x) { Matrix2.Cartesian3.clone(currentPos, xMin); } if (x > xMax.x) { Matrix2.Cartesian3.clone(currentPos, xMax); } if (y < yMin.y) { Matrix2.Cartesian3.clone(currentPos, yMin); } if (y > yMax.y) { Matrix2.Cartesian3.clone(currentPos, yMax); } if (z < zMin.z) { Matrix2.Cartesian3.clone(currentPos, zMin); } if (z > zMax.z) { Matrix2.Cartesian3.clone(currentPos, zMax); } } // Compute x-, y-, and z-spans (Squared distances b/n each component's min. and max.). const xSpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(xMax, xMin, fromPointsScratch) ); const ySpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(yMax, yMin, fromPointsScratch) ); const zSpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(zMax, zMin, fromPointsScratch) ); // Set the diameter endpoints to the largest span. let diameter1 = xMin; let diameter2 = xMax; let maxSpan = xSpan; if (ySpan > maxSpan) { maxSpan = ySpan; diameter1 = yMin; diameter2 = yMax; } if (zSpan > maxSpan) { maxSpan = zSpan; diameter1 = zMin; diameter2 = zMax; } // Calculate the center of the initial sphere found by Ritter's algorithm const ritterCenter = fromPointsRitterCenter; ritterCenter.x = (diameter1.x + diameter2.x) * 0.5; ritterCenter.y = (diameter1.y + diameter2.y) * 0.5; ritterCenter.z = (diameter1.z + diameter2.z) * 0.5; // Calculate the radius of the initial sphere found by Ritter's algorithm let radiusSquared = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(diameter2, ritterCenter, fromPointsScratch) ); let ritterRadius = Math.sqrt(radiusSquared); // Find the center of the sphere found using the Naive method. const minBoxPt = fromPointsMinBoxPt; minBoxPt.x = xMin.x; minBoxPt.y = yMin.y; minBoxPt.z = zMin.z; const maxBoxPt = fromPointsMaxBoxPt; maxBoxPt.x = xMax.x; maxBoxPt.y = yMax.y; maxBoxPt.z = zMax.z; const naiveCenter = Matrix2.Cartesian3.midpoint( minBoxPt, maxBoxPt, fromPointsNaiveCenterScratch ); // Begin 2nd pass to find naive radius and modify the ritter sphere. let naiveRadius = 0; for (i = 0; i < numElements; i += stride) { currentPos.x = positions[i] + center.x; currentPos.y = positions[i + 1] + center.y; currentPos.z = positions[i + 2] + center.z; // Find the furthest point from the naive center to calculate the naive radius. const r = Matrix2.Cartesian3.magnitude( Matrix2.Cartesian3.subtract(currentPos, naiveCenter, fromPointsScratch) ); if (r > naiveRadius) { naiveRadius = r; } // Make adjustments to the Ritter Sphere to include all points. const oldCenterToPointSquared = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(currentPos, ritterCenter, fromPointsScratch) ); if (oldCenterToPointSquared > radiusSquared) { const oldCenterToPoint = Math.sqrt(oldCenterToPointSquared); // Calculate new radius to include the point that lies outside ritterRadius = (ritterRadius + oldCenterToPoint) * 0.5; radiusSquared = ritterRadius * ritterRadius; // Calculate center of new Ritter sphere const oldToNew = oldCenterToPoint - ritterRadius; ritterCenter.x = (ritterRadius * ritterCenter.x + oldToNew * currentPos.x) / oldCenterToPoint; ritterCenter.y = (ritterRadius * ritterCenter.y + oldToNew * currentPos.y) / oldCenterToPoint; ritterCenter.z = (ritterRadius * ritterCenter.z + oldToNew * currentPos.z) / oldCenterToPoint; } } if (ritterRadius < naiveRadius) { Matrix2.Cartesian3.clone(ritterCenter, result.center); result.radius = ritterRadius; } else { Matrix2.Cartesian3.clone(naiveCenter, result.center); result.radius = naiveRadius; } return result; }; /** * Computes a tight-fitting bounding sphere enclosing a list of EncodedCartesian3s, where the points are * stored in parallel flat arrays in X, Y, Z, order. The bounding sphere is computed by running two * algorithms, a naive algorithm and Ritter's algorithm. The smaller of the two spheres is used to * ensure a tight fit. * * @param {Number[]} [positionsHigh] An array of high bits of the encoded cartesians that the bounding sphere will enclose. Each point * is formed from three elements in the array in the order X, Y, Z. * @param {Number[]} [positionsLow] An array of low bits of the encoded cartesians that the bounding sphere will enclose. Each point * is formed from three elements in the array in the order X, Y, Z. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. * * @see {@link http://blogs.agi.com/insight3d/index.php/2008/02/04/a-bounding/|Bounding Sphere computation article} */ BoundingSphere.fromEncodedCartesianVertices = function ( positionsHigh, positionsLow, result ) { if (!defaultValue.defined(result)) { result = new BoundingSphere(); } if ( !defaultValue.defined(positionsHigh) || !defaultValue.defined(positionsLow) || positionsHigh.length !== positionsLow.length || positionsHigh.length === 0 ) { result.center = Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } const currentPos = fromPointsCurrentPos; currentPos.x = positionsHigh[0] + positionsLow[0]; currentPos.y = positionsHigh[1] + positionsLow[1]; currentPos.z = positionsHigh[2] + positionsLow[2]; const xMin = Matrix2.Cartesian3.clone(currentPos, fromPointsXMin); const yMin = Matrix2.Cartesian3.clone(currentPos, fromPointsYMin); const zMin = Matrix2.Cartesian3.clone(currentPos, fromPointsZMin); const xMax = Matrix2.Cartesian3.clone(currentPos, fromPointsXMax); const yMax = Matrix2.Cartesian3.clone(currentPos, fromPointsYMax); const zMax = Matrix2.Cartesian3.clone(currentPos, fromPointsZMax); const numElements = positionsHigh.length; let i; for (i = 0; i < numElements; i += 3) { const x = positionsHigh[i] + positionsLow[i]; const y = positionsHigh[i + 1] + positionsLow[i + 1]; const z = positionsHigh[i + 2] + positionsLow[i + 2]; currentPos.x = x; currentPos.y = y; currentPos.z = z; // Store points containing the the smallest and largest components if (x < xMin.x) { Matrix2.Cartesian3.clone(currentPos, xMin); } if (x > xMax.x) { Matrix2.Cartesian3.clone(currentPos, xMax); } if (y < yMin.y) { Matrix2.Cartesian3.clone(currentPos, yMin); } if (y > yMax.y) { Matrix2.Cartesian3.clone(currentPos, yMax); } if (z < zMin.z) { Matrix2.Cartesian3.clone(currentPos, zMin); } if (z > zMax.z) { Matrix2.Cartesian3.clone(currentPos, zMax); } } // Compute x-, y-, and z-spans (Squared distances b/n each component's min. and max.). const xSpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(xMax, xMin, fromPointsScratch) ); const ySpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(yMax, yMin, fromPointsScratch) ); const zSpan = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(zMax, zMin, fromPointsScratch) ); // Set the diameter endpoints to the largest span. let diameter1 = xMin; let diameter2 = xMax; let maxSpan = xSpan; if (ySpan > maxSpan) { maxSpan = ySpan; diameter1 = yMin; diameter2 = yMax; } if (zSpan > maxSpan) { maxSpan = zSpan; diameter1 = zMin; diameter2 = zMax; } // Calculate the center of the initial sphere found by Ritter's algorithm const ritterCenter = fromPointsRitterCenter; ritterCenter.x = (diameter1.x + diameter2.x) * 0.5; ritterCenter.y = (diameter1.y + diameter2.y) * 0.5; ritterCenter.z = (diameter1.z + diameter2.z) * 0.5; // Calculate the radius of the initial sphere found by Ritter's algorithm let radiusSquared = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(diameter2, ritterCenter, fromPointsScratch) ); let ritterRadius = Math.sqrt(radiusSquared); // Find the center of the sphere found using the Naive method. const minBoxPt = fromPointsMinBoxPt; minBoxPt.x = xMin.x; minBoxPt.y = yMin.y; minBoxPt.z = zMin.z; const maxBoxPt = fromPointsMaxBoxPt; maxBoxPt.x = xMax.x; maxBoxPt.y = yMax.y; maxBoxPt.z = zMax.z; const naiveCenter = Matrix2.Cartesian3.midpoint( minBoxPt, maxBoxPt, fromPointsNaiveCenterScratch ); // Begin 2nd pass to find naive radius and modify the ritter sphere. let naiveRadius = 0; for (i = 0; i < numElements; i += 3) { currentPos.x = positionsHigh[i] + positionsLow[i]; currentPos.y = positionsHigh[i + 1] + positionsLow[i + 1]; currentPos.z = positionsHigh[i + 2] + positionsLow[i + 2]; // Find the furthest point from the naive center to calculate the naive radius. const r = Matrix2.Cartesian3.magnitude( Matrix2.Cartesian3.subtract(currentPos, naiveCenter, fromPointsScratch) ); if (r > naiveRadius) { naiveRadius = r; } // Make adjustments to the Ritter Sphere to include all points. const oldCenterToPointSquared = Matrix2.Cartesian3.magnitudeSquared( Matrix2.Cartesian3.subtract(currentPos, ritterCenter, fromPointsScratch) ); if (oldCenterToPointSquared > radiusSquared) { const oldCenterToPoint = Math.sqrt(oldCenterToPointSquared); // Calculate new radius to include the point that lies outside ritterRadius = (ritterRadius + oldCenterToPoint) * 0.5; radiusSquared = ritterRadius * ritterRadius; // Calculate center of new Ritter sphere const oldToNew = oldCenterToPoint - ritterRadius; ritterCenter.x = (ritterRadius * ritterCenter.x + oldToNew * currentPos.x) / oldCenterToPoint; ritterCenter.y = (ritterRadius * ritterCenter.y + oldToNew * currentPos.y) / oldCenterToPoint; ritterCenter.z = (ritterRadius * ritterCenter.z + oldToNew * currentPos.z) / oldCenterToPoint; } } if (ritterRadius < naiveRadius) { Matrix2.Cartesian3.clone(ritterCenter, result.center); result.radius = ritterRadius; } else { Matrix2.Cartesian3.clone(naiveCenter, result.center); result.radius = naiveRadius; } return result; }; /** * Computes a bounding sphere from the corner points of an axis-aligned bounding box. The sphere * tightly and fully encompasses the box. * * @param {Cartesian3} [corner] The minimum height over the rectangle. * @param {Cartesian3} [oppositeCorner] The maximum height over the rectangle. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. * * @example * // Create a bounding sphere around the unit cube * const sphere = Cesium.BoundingSphere.fromCornerPoints(new Cesium.Cartesian3(-0.5, -0.5, -0.5), new Cesium.Cartesian3(0.5, 0.5, 0.5)); */ BoundingSphere.fromCornerPoints = function (corner, oppositeCorner, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("corner", corner); RuntimeError.Check.typeOf.object("oppositeCorner", oppositeCorner); //>>includeEnd('debug'); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } const center = Matrix2.Cartesian3.midpoint(corner, oppositeCorner, result.center); result.radius = Matrix2.Cartesian3.distance(center, oppositeCorner); return result; }; /** * Creates a bounding sphere encompassing an ellipsoid. * * @param {Ellipsoid} ellipsoid The ellipsoid around which to create a bounding sphere. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. * * @example * const boundingSphere = Cesium.BoundingSphere.fromEllipsoid(ellipsoid); */ BoundingSphere.fromEllipsoid = function (ellipsoid, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("ellipsoid", ellipsoid); //>>includeEnd('debug'); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = ellipsoid.maximumRadius; return result; }; const fromBoundingSpheresScratch = new Matrix2.Cartesian3(); /** * Computes a tight-fitting bounding sphere enclosing the provided array of bounding spheres. * * @param {BoundingSphere[]} [boundingSpheres] The array of bounding spheres. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromBoundingSpheres = function (boundingSpheres, result) { if (!defaultValue.defined(result)) { result = new BoundingSphere(); } if (!defaultValue.defined(boundingSpheres) || boundingSpheres.length === 0) { result.center = Matrix2.Cartesian3.clone(Matrix2.Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } const length = boundingSpheres.length; if (length === 1) { return BoundingSphere.clone(boundingSpheres[0], result); } if (length === 2) { return BoundingSphere.union(boundingSpheres[0], boundingSpheres[1], result); } const positions = []; let i; for (i = 0; i < length; i++) { positions.push(boundingSpheres[i].center); } result = BoundingSphere.fromPoints(positions, result); const center = result.center; let radius = result.radius; for (i = 0; i < length; i++) { const tmp = boundingSpheres[i]; radius = Math.max( radius, Matrix2.Cartesian3.distance(center, tmp.center, fromBoundingSpheresScratch) + tmp.radius ); } result.radius = radius; return result; }; const fromOrientedBoundingBoxScratchU = new Matrix2.Cartesian3(); const fromOrientedBoundingBoxScratchV = new Matrix2.Cartesian3(); const fromOrientedBoundingBoxScratchW = new Matrix2.Cartesian3(); /** * Computes a tight-fitting bounding sphere enclosing the provided oriented bounding box. * * @param {OrientedBoundingBox} orientedBoundingBox The oriented bounding box. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromOrientedBoundingBox = function ( orientedBoundingBox, result ) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.defined("orientedBoundingBox", orientedBoundingBox); //>>includeEnd('debug'); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } const halfAxes = orientedBoundingBox.halfAxes; const u = Matrix2.Matrix3.getColumn(halfAxes, 0, fromOrientedBoundingBoxScratchU); const v = Matrix2.Matrix3.getColumn(halfAxes, 1, fromOrientedBoundingBoxScratchV); const w = Matrix2.Matrix3.getColumn(halfAxes, 2, fromOrientedBoundingBoxScratchW); Matrix2.Cartesian3.add(u, v, u); Matrix2.Cartesian3.add(u, w, u); result.center = Matrix2.Cartesian3.clone(orientedBoundingBox.center, result.center); result.radius = Matrix2.Cartesian3.magnitude(u); return result; }; const scratchFromTransformationCenter = new Matrix2.Cartesian3(); const scratchFromTransformationScale = new Matrix2.Cartesian3(); /** * Computes a tight-fitting bounding sphere enclosing the provided affine transformation. * * @param {Matrix4} transformation The affine transformation. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.fromTransformation = function (transformation, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("transformation", transformation); //>>includeEnd('debug'); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } const center = Matrix2.Matrix4.getTranslation( transformation, scratchFromTransformationCenter ); const scale = Matrix2.Matrix4.getScale( transformation, scratchFromTransformationScale ); const radius = 0.5 * Matrix2.Cartesian3.magnitude(scale); result.center = Matrix2.Cartesian3.clone(center, result.center); result.radius = radius; return result; }; /** * Duplicates a BoundingSphere instance. * * @param {BoundingSphere} sphere The bounding sphere to duplicate. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. (Returns undefined if sphere is undefined) */ BoundingSphere.clone = function (sphere, result) { if (!defaultValue.defined(sphere)) { return undefined; } if (!defaultValue.defined(result)) { return new BoundingSphere(sphere.center, sphere.radius); } result.center = Matrix2.Cartesian3.clone(sphere.center, result.center); result.radius = sphere.radius; return result; }; /** * The number of elements used to pack the object into an array. * @type {Number} */ BoundingSphere.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {BoundingSphere} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ BoundingSphere.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("value", value); RuntimeError.Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue.defaultValue(startingIndex, 0); const center = value.center; array[startingIndex++] = center.x; array[startingIndex++] = center.y; array[startingIndex++] = center.z; array[startingIndex] = value.radius; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {BoundingSphere} [result] The object into which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. */ BoundingSphere.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue.defaultValue(startingIndex, 0); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } const center = result.center; center.x = array[startingIndex++]; center.y = array[startingIndex++]; center.z = array[startingIndex++]; result.radius = array[startingIndex]; return result; }; const unionScratch = new Matrix2.Cartesian3(); const unionScratchCenter = new Matrix2.Cartesian3(); /** * Computes a bounding sphere that contains both the left and right bounding spheres. * * @param {BoundingSphere} left A sphere to enclose in a bounding sphere. * @param {BoundingSphere} right A sphere to enclose in a bounding sphere. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.union = function (left, right, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("left", left); RuntimeError.Check.typeOf.object("right", right); //>>includeEnd('debug'); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } const leftCenter = left.center; const leftRadius = left.radius; const rightCenter = right.center; const rightRadius = right.radius; const toRightCenter = Matrix2.Cartesian3.subtract( rightCenter, leftCenter, unionScratch ); const centerSeparation = Matrix2.Cartesian3.magnitude(toRightCenter); if (leftRadius >= centerSeparation + rightRadius) { // Left sphere wins. left.clone(result); return result; } if (rightRadius >= centerSeparation + leftRadius) { // Right sphere wins. right.clone(result); return result; } // There are two tangent points, one on far side of each sphere. const halfDistanceBetweenTangentPoints = (leftRadius + centerSeparation + rightRadius) * 0.5; // Compute the center point halfway between the two tangent points. const center = Matrix2.Cartesian3.multiplyByScalar( toRightCenter, (-leftRadius + halfDistanceBetweenTangentPoints) / centerSeparation, unionScratchCenter ); Matrix2.Cartesian3.add(center, leftCenter, center); Matrix2.Cartesian3.clone(center, result.center); result.radius = halfDistanceBetweenTangentPoints; return result; }; const expandScratch = new Matrix2.Cartesian3(); /** * Computes a bounding sphere by enlarging the provided sphere to contain the provided point. * * @param {BoundingSphere} sphere A sphere to expand. * @param {Cartesian3} point A point to enclose in a bounding sphere. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.expand = function (sphere, point, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("sphere", sphere); RuntimeError.Check.typeOf.object("point", point); //>>includeEnd('debug'); result = BoundingSphere.clone(sphere, result); const radius = Matrix2.Cartesian3.magnitude( Matrix2.Cartesian3.subtract(point, result.center, expandScratch) ); if (radius > result.radius) { result.radius = radius; } return result; }; /** * Determines which side of a plane a sphere is located. * * @param {BoundingSphere} sphere The bounding sphere to test. * @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.intersectPlane = function (sphere, plane) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("sphere", sphere); RuntimeError.Check.typeOf.object("plane", plane); //>>includeEnd('debug'); const center = sphere.center; const radius = sphere.radius; const normal = plane.normal; const distanceToPlane = Matrix2.Cartesian3.dot(normal, center) + plane.distance; if (distanceToPlane < -radius) { // The center point is negative side of the plane normal return Intersect$1.OUTSIDE; } else if (distanceToPlane < radius) { // The center point is positive side of the plane, but radius extends beyond it; partial overlap return Intersect$1.INTERSECTING; } return Intersect$1.INSIDE; }; /** * Applies a 4x4 affine transformation matrix to a bounding sphere. * * @param {BoundingSphere} sphere The bounding sphere to apply the transformation to. * @param {Matrix4} transform The transformation matrix to apply to the bounding sphere. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. */ BoundingSphere.transform = function (sphere, transform, result) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("sphere", sphere); RuntimeError.Check.typeOf.object("transform", transform); //>>includeEnd('debug'); if (!defaultValue.defined(result)) { result = new BoundingSphere(); } result.center = Matrix2.Matrix4.multiplyByPoint( transform, sphere.center, result.center ); result.radius = Matrix2.Matrix4.getMaximumScale(transform) * sphere.radius; return result; }; const distanceSquaredToScratch = new Matrix2.Cartesian3(); /** * Computes the estimated distance squared from the closest point on a bounding sphere to a point. * * @param {BoundingSphere} sphere The sphere. * @param {Cartesian3} cartesian The point * @returns {Number} The distance squared from the bounding sphere to the point. Returns 0 if the point is inside the sphere. * * @example * // Sort bounding spheres from back to front * spheres.sort(function(a, b) { * return Cesium.BoundingSphere.distanceSquaredTo(b, camera.positionWC) - Cesium.BoundingSphere.distanceSquaredTo(a, camera.positionWC); * }); */ BoundingSphere.distanceSquaredTo = function (sphere, cartesian) { //>>includeStart('debug', pragmas.debug); RuntimeError.Check.typeOf.object("sphere", sphere); RuntimeError.Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); const diff = Matrix2.Cartesian3.subtract( sphere.center, cartesian, distanceSquaredToScratch ); const distance = Matrix2.Cartesian3.magnitude(diff) - sphere.radius; if (distance <= 0.0) { return 0.0; } return distance * distance; }; /** * Applies a 4x4 affine transformation matrix to a bounding sphere where there is no scale * The transformation matrix is not verified to have a uniform scale of 1. * This method is faster than computing the general bounding sphere transform using {@link BoundingSphere.transform}. * * @param {BoundingSphere} sphere The bounding sphere to apply the transformation to. * @param {Matrix4} transform The transformation matrix to apply to the bounding sphere. * @param {BoundingSphere} [result] The object onto which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. * * @example * const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid); * const boundingSphere = new Cesium.BoundingSphere(); * const newBoundingSphere = Cesium.BoundingSphere.transformWithoutScale(boundingSphere, modelMatrix); */ BoundingSphere.transformWithoutScale = function (sphere, transform, result) { //>>includeStart('debug', pragmas.debug); RuntimeError