/** * Cesium - https://github.com/CesiumGS/cesium * * Copyright 2011-2020 Cesium Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Columbus View (Pat. Pend.) * * Portions licensed separately. * See https://github.com/CesiumGS/cesium/blob/master/LICENSE.md for full licensing details. */ define(['exports', './when-8d13db60', './Check-70bec281', './Cartographic-f2a06374', './Cartesian2-16a61632', './BoundingSphere-d018a565', './Cartesian4-5af5bb24', './Transforms-f77c92da', './IntersectionTests-813bb943', './Plane-aa6c3ce5'], function (exports, when, Check, Cartographic, Cartesian2, BoundingSphere, Cartesian4, Transforms, IntersectionTests, Plane) { 'use strict'; /** * Creates an instance of an AxisAlignedBoundingBox from the minimum and maximum points along the x, y, and z axes. * @alias AxisAlignedBoundingBox * @constructor * * @param {Cartesian3} [minimum=Cartesian3.ZERO] The minimum point along the x, y, and z axes. * @param {Cartesian3} [maximum=Cartesian3.ZERO] The maximum point along the x, y, and z axes. * @param {Cartesian3} [center] The center of the box; automatically computed if not supplied. * * @see BoundingSphere * @see BoundingRectangle */ function AxisAlignedBoundingBox(minimum, maximum, center) { /** * The minimum point defining the bounding box. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.minimum = Cartographic.Cartesian3.clone(when.defaultValue(minimum, Cartographic.Cartesian3.ZERO)); /** * The maximum point defining the bounding box. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.maximum = Cartographic.Cartesian3.clone(when.defaultValue(maximum, Cartographic.Cartesian3.ZERO)); //If center was not defined, compute it. if (!when.defined(center)) { center = Cartographic.Cartesian3.midpoint(this.minimum, this.maximum, new Cartographic.Cartesian3()); } else { center = Cartographic.Cartesian3.clone(center); } /** * The center point of the bounding box. * @type {Cartesian3} */ this.center = center; } /** * Computes an instance of an AxisAlignedBoundingBox. The box is determined by * finding the points spaced the farthest apart on the x, y, and z axes. * * @param {Cartesian3[]} positions List of points that the bounding box will enclose. Each point must have a x, y, and z properties. * @param {AxisAlignedBoundingBox} [result] The object onto which to store the result. * @returns {AxisAlignedBoundingBox} The modified result parameter or a new AxisAlignedBoundingBox instance if one was not provided. * * @example * // Compute an axis aligned bounding box enclosing two points. * var box = Cesium.AxisAlignedBoundingBox.fromPoints([new Cesium.Cartesian3(2, 0, 0), new Cesium.Cartesian3(-2, 0, 0)]); */ AxisAlignedBoundingBox.fromPoints = function(positions, result) { if (!when.defined(result)) { result = new AxisAlignedBoundingBox(); } if (!when.defined(positions) || positions.length === 0) { result.minimum = Cartographic.Cartesian3.clone(Cartographic.Cartesian3.ZERO, result.minimum); result.maximum = Cartographic.Cartesian3.clone(Cartographic.Cartesian3.ZERO, result.maximum); result.center = Cartographic.Cartesian3.clone(Cartographic.Cartesian3.ZERO, result.center); return result; } var minimumX = positions[0].x; var minimumY = positions[0].y; var minimumZ = positions[0].z; var maximumX = positions[0].x; var maximumY = positions[0].y; var maximumZ = positions[0].z; var length = positions.length; for ( var i = 1; i < length; i++) { var p = positions[i]; var x = p.x; var y = p.y; var z = p.z; minimumX = Math.min(x, minimumX); maximumX = Math.max(x, maximumX); minimumY = Math.min(y, minimumY); maximumY = Math.max(y, maximumY); minimumZ = Math.min(z, minimumZ); maximumZ = Math.max(z, maximumZ); } var minimum = result.minimum; minimum.x = minimumX; minimum.y = minimumY; minimum.z = minimumZ; var maximum = result.maximum; maximum.x = maximumX; maximum.y = maximumY; maximum.z = maximumZ; result.center = Cartographic.Cartesian3.midpoint(minimum, maximum, result.center); return result; }; /** * Duplicates a AxisAlignedBoundingBox instance. * * @param {AxisAlignedBoundingBox} box The bounding box to duplicate. * @param {AxisAlignedBoundingBox} [result] The object onto which to store the result. * @returns {AxisAlignedBoundingBox} The modified result parameter or a new AxisAlignedBoundingBox instance if none was provided. (Returns undefined if box is undefined) */ AxisAlignedBoundingBox.clone = function(box, result) { if (!when.defined(box)) { return undefined; } if (!when.defined(result)) { return new AxisAlignedBoundingBox(box.minimum, box.maximum, box.center); } result.minimum = Cartographic.Cartesian3.clone(box.minimum, result.minimum); result.maximum = Cartographic.Cartesian3.clone(box.maximum, result.maximum); result.center = Cartographic.Cartesian3.clone(box.center, result.center); return result; }; /** * Compares the provided AxisAlignedBoundingBox componentwise and returns * true if they are equal, false otherwise. * * @param {AxisAlignedBoundingBox} [left] The first AxisAlignedBoundingBox. * @param {AxisAlignedBoundingBox} [right] The second AxisAlignedBoundingBox. * @returns {Boolean} true if left and right are equal, false otherwise. */ AxisAlignedBoundingBox.equals = function(left, right) { return (left === right) || ((when.defined(left)) && (when.defined(right)) && Cartographic.Cartesian3.equals(left.center, right.center) && Cartographic.Cartesian3.equals(left.minimum, right.minimum) && Cartographic.Cartesian3.equals(left.maximum, right.maximum)); }; var intersectScratch = new Cartographic.Cartesian3(); /** * Determines which side of a plane a box is located. * * @param {AxisAlignedBoundingBox} box The 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. */ AxisAlignedBoundingBox.intersectPlane = function(box, plane) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('box', box); Check.Check.defined('plane', plane); //>>includeEnd('debug'); intersectScratch = Cartographic.Cartesian3.subtract(box.maximum, box.minimum, intersectScratch); var h = Cartographic.Cartesian3.multiplyByScalar(intersectScratch, 0.5, intersectScratch); //The positive half diagonal 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 = Cartographic.Cartesian3.dot(box.center, normal) + plane.distance; //signed distance from center if (s - e > 0) { return BoundingSphere.Intersect.INSIDE; } if (s + e < 0) { //Not in front because normals point inward return BoundingSphere.Intersect.OUTSIDE; } return BoundingSphere.Intersect.INTERSECTING; }; /** * Duplicates this AxisAlignedBoundingBox instance. * * @param {AxisAlignedBoundingBox} [result] The object onto which to store the result. * @returns {AxisAlignedBoundingBox} The modified result parameter or a new AxisAlignedBoundingBox instance if one was not provided. */ AxisAlignedBoundingBox.prototype.clone = function(result) { return AxisAlignedBoundingBox.clone(this, result); }; /** * 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); }; /** * Compares this AxisAlignedBoundingBox against the provided AxisAlignedBoundingBox componentwise and returns * true if they are equal, false otherwise. * * @param {AxisAlignedBoundingBox} [right] The right hand side AxisAlignedBoundingBox. * @returns {Boolean} true if they are equal, false otherwise. */ AxisAlignedBoundingBox.prototype.equals = function(right) { return AxisAlignedBoundingBox.equals(this, right); }; var scratchCart4 = new Cartesian4.Cartesian4(); /** * A plane tangent to the provided ellipsoid at the provided origin. * If origin is not on the surface of the ellipsoid, it's surface projection will be used. * If origin is at the center of the ellipsoid, an exception will be thrown. * @alias EllipsoidTangentPlane * @constructor * * @param {Cartesian3} origin The point on the surface of the ellipsoid where the tangent plane touches. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use. * * @exception {DeveloperError} origin must not be at the center of the ellipsoid. */ function EllipsoidTangentPlane(origin, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('origin', origin); //>>includeEnd('debug'); ellipsoid = when.defaultValue(ellipsoid, Cartesian2.Ellipsoid.WGS84); origin = ellipsoid.scaleToGeodeticSurface(origin); //>>includeStart('debug', pragmas.debug); if (!when.defined(origin)) { throw new Check.DeveloperError('origin must not be at the center of the ellipsoid.'); } //>>includeEnd('debug'); var eastNorthUp = Transforms.Transforms.eastNorthUpToFixedFrame(origin, ellipsoid); this._ellipsoid = ellipsoid; this._origin = origin; this._xAxis = Cartographic.Cartesian3.fromCartesian4(BoundingSphere.Matrix4.getColumn(eastNorthUp, 0, scratchCart4)); this._yAxis = Cartographic.Cartesian3.fromCartesian4(BoundingSphere.Matrix4.getColumn(eastNorthUp, 1, scratchCart4)); var normal = Cartographic.Cartesian3.fromCartesian4(BoundingSphere.Matrix4.getColumn(eastNorthUp, 2, scratchCart4)); this._plane = Plane.Plane.fromPointNormal(origin, normal); } Object.defineProperties(EllipsoidTangentPlane.prototype, { /** * Gets the ellipsoid. * @memberof EllipsoidTangentPlane.prototype * @type {Ellipsoid} */ ellipsoid : { get : function() { return this._ellipsoid; } }, /** * Gets the origin. * @memberof EllipsoidTangentPlane.prototype * @type {Cartesian3} */ origin : { 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; } } }); var tmp = new AxisAlignedBoundingBox(); /** * Creates a new instance from the provided ellipsoid and the center * point of the provided Cartesians. * * @param {Cartesian3} cartesians The list of positions surrounding the center point. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use. */ EllipsoidTangentPlane.fromPoints = function(cartesians, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('cartesians', cartesians); //>>includeEnd('debug'); var box = AxisAlignedBoundingBox.fromPoints(cartesians, tmp); return new EllipsoidTangentPlane(box.center, ellipsoid); }; var scratchProjectPointOntoPlaneRay = new IntersectionTests.Ray(); var scratchProjectPointOntoPlaneCartesian3 = new Cartographic.Cartesian3(); /** * 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. Undefined if there is no intersection point */ EllipsoidTangentPlane.prototype.projectPointOntoPlane = function(cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('cartesian', cartesian); //>>includeEnd('debug'); var ray = scratchProjectPointOntoPlaneRay; ray.origin = cartesian; Cartographic.Cartesian3.normalize(cartesian, ray.direction); var intersectionPoint = IntersectionTests.IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3); if (!when.defined(intersectionPoint)) { Cartographic.Cartesian3.negate(ray.direction, ray.direction); intersectionPoint = IntersectionTests.IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3); } if (when.defined(intersectionPoint)) { var v = Cartographic.Cartesian3.subtract(intersectionPoint, this._origin, intersectionPoint); var x = Cartographic.Cartesian3.dot(this._xAxis, v); var y = Cartographic.Cartesian3.dot(this._yAxis, v); if (!when.defined(result)) { return new Cartesian2.Cartesian2(x, y); } result.x = x; result.y = y; return result; } return undefined; }; /** * 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. * @returns {Cartesian2[]} The modified result parameter or a new array of Cartesian2 instances if none was provided. */ EllipsoidTangentPlane.prototype.projectPointsOntoPlane = function(cartesians, result) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('cartesians', cartesians); //>>includeEnd('debug'); if (!when.defined(result)) { result = []; } var count = 0; var length = cartesians.length; for ( var i = 0; i < length; i++) { var p = this.projectPointOntoPlane(cartesians[i], result[count]); if (when.defined(p)) { result[count] = p; count++; } } result.length = count; 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); Check.Check.defined('cartesian', cartesian); //>>includeEnd('debug'); if (!when.defined(result)) { result = new Cartesian2.Cartesian2(); } var ray = scratchProjectPointOntoPlaneRay; ray.origin = cartesian; Cartographic.Cartesian3.clone(this._plane.normal, ray.direction); var intersectionPoint = IntersectionTests.IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3); if (!when.defined(intersectionPoint)) { Cartographic.Cartesian3.negate(ray.direction, ray.direction); intersectionPoint = IntersectionTests.IntersectionTests.rayPlane(ray, this._plane, scratchProjectPointOntoPlaneCartesian3); } var v = Cartographic.Cartesian3.subtract(intersectionPoint, this._origin, intersectionPoint); var x = Cartographic.Cartesian3.dot(this._xAxis, v); var y = Cartographic.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); Check.Check.defined('cartesians', cartesians); //>>includeEnd('debug'); if (!when.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 Cartographic.Cartesian3(); /** * Computes the projection of the provided 2D position onto the 3D ellipsoid. * * @param {Cartesian2} cartesian The points to project. * @param {Cartesian3} [result] The Cartesian3 instance to store result. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. */ EllipsoidTangentPlane.prototype.projectPointOntoEllipsoid = function(cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('cartesian', cartesian); //>>includeEnd('debug'); if (!when.defined(result)) { result = new Cartographic.Cartesian3(); } var ellipsoid = this._ellipsoid; var origin = this._origin; var xAxis = this._xAxis; var yAxis = this._yAxis; var tmp = projectPointsOntoEllipsoidScratch; Cartographic.Cartesian3.multiplyByScalar(xAxis, cartesian.x, tmp); result = Cartographic.Cartesian3.add(origin, tmp, result); Cartographic.Cartesian3.multiplyByScalar(yAxis, cartesian.y, tmp); Cartographic.Cartesian3.add(result, tmp, result); ellipsoid.scaleToGeocentricSurface(result, result); return result; }; /** * Computes the projection of the provided 2D positions onto the 3D ellipsoid. * * @param {Cartesian2[]} cartesians The array of points to project. * @param {Cartesian3[]} [result] The array of Cartesian3 instances onto which to store results. * @returns {Cartesian3[]} The modified result parameter or a new array of Cartesian3 instances if none was provided. */ EllipsoidTangentPlane.prototype.projectPointsOntoEllipsoid = function(cartesians, result) { //>>includeStart('debug', pragmas.debug); Check.Check.defined('cartesians', cartesians); //>>includeEnd('debug'); var length = cartesians.length; if (!when.defined(result)) { result = new Array(length); } else { result.length = length; } for ( var i = 0; i < length; ++i) { result[i] = this.projectPointOntoEllipsoid(cartesians[i], result[i]); } return result; }; exports.AxisAlignedBoundingBox = AxisAlignedBoundingBox; exports.EllipsoidTangentPlane = EllipsoidTangentPlane; });