您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

OrientedBoundingBox.js 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. import BoundingSphere from './BoundingSphere.js';
  2. import Cartesian2 from './Cartesian2.js';
  3. import Cartesian3 from './Cartesian3.js';
  4. import Cartographic from './Cartographic.js';
  5. import Check from './Check.js';
  6. import defaultValue from './defaultValue.js';
  7. import defined from './defined.js';
  8. import DeveloperError from './DeveloperError.js';
  9. import Ellipsoid from './Ellipsoid.js';
  10. import EllipsoidTangentPlane from './EllipsoidTangentPlane.js';
  11. import Intersect from './Intersect.js';
  12. import Interval from './Interval.js';
  13. import CesiumMath from './Math.js';
  14. import Matrix3 from './Matrix3.js';
  15. import Plane from './Plane.js';
  16. import Rectangle from './Rectangle.js';
  17. /**
  18. * Creates an instance of an OrientedBoundingBox.
  19. * 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.
  20. * @alias OrientedBoundingBox
  21. * @constructor
  22. *
  23. * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box.
  24. * @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box.
  25. * Equivalently, the transformation matrix, to rotate and scale a 0x0x0
  26. * cube centered at the origin.
  27. *
  28. *
  29. * @example
  30. * // Create an OrientedBoundingBox using a transformation matrix, a position where the box will be translated, and a scale.
  31. * var center = new Cesium.Cartesian3(1.0, 0.0, 0.0);
  32. * var halfAxes = Cesium.Matrix3.fromScale(new Cesium.Cartesian3(1.0, 3.0, 2.0), new Cesium.Matrix3());
  33. *
  34. * var obb = new Cesium.OrientedBoundingBox(center, halfAxes);
  35. *
  36. * @see BoundingSphere
  37. * @see BoundingRectangle
  38. */
  39. function OrientedBoundingBox(center, halfAxes) {
  40. /**
  41. * The center of the box.
  42. * @type {Cartesian3}
  43. * @default {@link Cartesian3.ZERO}
  44. */
  45. this.center = Cartesian3.clone(defaultValue(center, Cartesian3.ZERO));
  46. /**
  47. * The transformation matrix, to rotate the box to the right position.
  48. * @type {Matrix3}
  49. * @default {@link Matrix3.ZERO}
  50. */
  51. this.halfAxes = Matrix3.clone(defaultValue(halfAxes, Matrix3.ZERO));
  52. }
  53. /**
  54. * The number of elements used to pack the object into an array.
  55. * @type {Number}
  56. */
  57. OrientedBoundingBox.packedLength = Cartesian3.packedLength + Matrix3.packedLength;
  58. /**
  59. * Stores the provided instance into the provided array.
  60. *
  61. * @param {OrientedBoundingBox} value The value to pack.
  62. * @param {Number[]} array The array to pack into.
  63. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  64. *
  65. * @returns {Number[]} The array that was packed into
  66. */
  67. OrientedBoundingBox.pack = function(value, array, startingIndex) {
  68. //>>includeStart('debug', pragmas.debug);
  69. Check.typeOf.object('value', value);
  70. Check.defined('array', array);
  71. //>>includeEnd('debug');
  72. startingIndex = defaultValue(startingIndex, 0);
  73. Cartesian3.pack(value.center, array, startingIndex);
  74. Matrix3.pack(value.halfAxes, array, startingIndex + Cartesian3.packedLength);
  75. return array;
  76. };
  77. /**
  78. * Retrieves an instance from a packed array.
  79. *
  80. * @param {Number[]} array The packed array.
  81. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  82. * @param {OrientedBoundingBox} [result] The object into which to store the result.
  83. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided.
  84. */
  85. OrientedBoundingBox.unpack = function(array, startingIndex, result) {
  86. //>>includeStart('debug', pragmas.debug);
  87. Check.defined('array', array);
  88. //>>includeEnd('debug');
  89. startingIndex = defaultValue(startingIndex, 0);
  90. if (!defined(result)) {
  91. result = new OrientedBoundingBox();
  92. }
  93. Cartesian3.unpack(array, startingIndex, result.center);
  94. Matrix3.unpack(array, startingIndex + Cartesian3.packedLength, result.halfAxes);
  95. return result;
  96. };
  97. var scratchCartesian1 = new Cartesian3();
  98. var scratchCartesian2 = new Cartesian3();
  99. var scratchCartesian3 = new Cartesian3();
  100. var scratchCartesian4 = new Cartesian3();
  101. var scratchCartesian5 = new Cartesian3();
  102. var scratchCartesian6 = new Cartesian3();
  103. var scratchCovarianceResult = new Matrix3();
  104. var scratchEigenResult = {
  105. unitary : new Matrix3(),
  106. diagonal : new Matrix3()
  107. };
  108. /**
  109. * Computes an instance of an OrientedBoundingBox of the given positions.
  110. * This is an implementation of Stefan Gottschalk's Collision Queries using Oriented Bounding Boxes solution (PHD thesis).
  111. * Reference: http://gamma.cs.unc.edu/users/gottschalk/main.pdf
  112. *
  113. * @param {Cartesian3[]} [positions] List of {@link Cartesian3} points that the bounding box will enclose.
  114. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  115. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided.
  116. *
  117. * @example
  118. * // Compute an object oriented bounding box enclosing two points.
  119. * var box = Cesium.OrientedBoundingBox.fromPoints([new Cesium.Cartesian3(2, 0, 0), new Cesium.Cartesian3(-2, 0, 0)]);
  120. */
  121. OrientedBoundingBox.fromPoints = function(positions, result) {
  122. if (!defined(result)) {
  123. result = new OrientedBoundingBox();
  124. }
  125. if (!defined(positions) || positions.length === 0) {
  126. result.halfAxes = Matrix3.ZERO;
  127. result.center = Cartesian3.ZERO;
  128. return result;
  129. }
  130. var i;
  131. var length = positions.length;
  132. var meanPoint = Cartesian3.clone(positions[0], scratchCartesian1);
  133. for (i = 1; i < length; i++) {
  134. Cartesian3.add(meanPoint, positions[i], meanPoint);
  135. }
  136. var invLength = 1.0 / length;
  137. Cartesian3.multiplyByScalar(meanPoint, invLength, meanPoint);
  138. var exx = 0.0;
  139. var exy = 0.0;
  140. var exz = 0.0;
  141. var eyy = 0.0;
  142. var eyz = 0.0;
  143. var ezz = 0.0;
  144. var p;
  145. for (i = 0; i < length; i++) {
  146. p = Cartesian3.subtract(positions[i], meanPoint, scratchCartesian2);
  147. exx += p.x * p.x;
  148. exy += p.x * p.y;
  149. exz += p.x * p.z;
  150. eyy += p.y * p.y;
  151. eyz += p.y * p.z;
  152. ezz += p.z * p.z;
  153. }
  154. exx *= invLength;
  155. exy *= invLength;
  156. exz *= invLength;
  157. eyy *= invLength;
  158. eyz *= invLength;
  159. ezz *= invLength;
  160. var covarianceMatrix = scratchCovarianceResult;
  161. covarianceMatrix[0] = exx;
  162. covarianceMatrix[1] = exy;
  163. covarianceMatrix[2] = exz;
  164. covarianceMatrix[3] = exy;
  165. covarianceMatrix[4] = eyy;
  166. covarianceMatrix[5] = eyz;
  167. covarianceMatrix[6] = exz;
  168. covarianceMatrix[7] = eyz;
  169. covarianceMatrix[8] = ezz;
  170. var eigenDecomposition = Matrix3.computeEigenDecomposition(covarianceMatrix, scratchEigenResult);
  171. var rotation = Matrix3.clone(eigenDecomposition.unitary, result.halfAxes);
  172. var v1 = Matrix3.getColumn(rotation, 0, scratchCartesian4);
  173. var v2 = Matrix3.getColumn(rotation, 1, scratchCartesian5);
  174. var v3 = Matrix3.getColumn(rotation, 2, scratchCartesian6);
  175. var u1 = -Number.MAX_VALUE;
  176. var u2 = -Number.MAX_VALUE;
  177. var u3 = -Number.MAX_VALUE;
  178. var l1 = Number.MAX_VALUE;
  179. var l2 = Number.MAX_VALUE;
  180. var l3 = Number.MAX_VALUE;
  181. for (i = 0; i < length; i++) {
  182. p = positions[i];
  183. u1 = Math.max(Cartesian3.dot(v1, p), u1);
  184. u2 = Math.max(Cartesian3.dot(v2, p), u2);
  185. u3 = Math.max(Cartesian3.dot(v3, p), u3);
  186. l1 = Math.min(Cartesian3.dot(v1, p), l1);
  187. l2 = Math.min(Cartesian3.dot(v2, p), l2);
  188. l3 = Math.min(Cartesian3.dot(v3, p), l3);
  189. }
  190. v1 = Cartesian3.multiplyByScalar(v1, 0.5 * (l1 + u1), v1);
  191. v2 = Cartesian3.multiplyByScalar(v2, 0.5 * (l2 + u2), v2);
  192. v3 = Cartesian3.multiplyByScalar(v3, 0.5 * (l3 + u3), v3);
  193. var center = Cartesian3.add(v1, v2, result.center);
  194. Cartesian3.add(center, v3, center);
  195. var scale = scratchCartesian3;
  196. scale.x = u1 - l1;
  197. scale.y = u2 - l2;
  198. scale.z = u3 - l3;
  199. Cartesian3.multiplyByScalar(scale, 0.5, scale);
  200. Matrix3.multiplyByScale(result.halfAxes, scale, result.halfAxes);
  201. return result;
  202. };
  203. var scratchOffset = new Cartesian3();
  204. var scratchScale = new Cartesian3();
  205. function fromPlaneExtents(planeOrigin, planeXAxis, planeYAxis, planeZAxis, minimumX, maximumX, minimumY, maximumY, minimumZ, maximumZ, result) {
  206. //>>includeStart('debug', pragmas.debug);
  207. if (!defined(minimumX) ||
  208. !defined(maximumX) ||
  209. !defined(minimumY) ||
  210. !defined(maximumY) ||
  211. !defined(minimumZ) ||
  212. !defined(maximumZ)) {
  213. throw new DeveloperError('all extents (minimum/maximum X/Y/Z) are required.');
  214. }
  215. //>>includeEnd('debug');
  216. if (!defined(result)) {
  217. result = new OrientedBoundingBox();
  218. }
  219. var halfAxes = result.halfAxes;
  220. Matrix3.setColumn(halfAxes, 0, planeXAxis, halfAxes);
  221. Matrix3.setColumn(halfAxes, 1, planeYAxis, halfAxes);
  222. Matrix3.setColumn(halfAxes, 2, planeZAxis, halfAxes);
  223. var centerOffset = scratchOffset;
  224. centerOffset.x = (minimumX + maximumX) / 2.0;
  225. centerOffset.y = (minimumY + maximumY) / 2.0;
  226. centerOffset.z = (minimumZ + maximumZ) / 2.0;
  227. var scale = scratchScale;
  228. scale.x = (maximumX - minimumX) / 2.0;
  229. scale.y = (maximumY - minimumY) / 2.0;
  230. scale.z = (maximumZ - minimumZ) / 2.0;
  231. var center = result.center;
  232. centerOffset = Matrix3.multiplyByVector(halfAxes, centerOffset, centerOffset);
  233. Cartesian3.add(planeOrigin, centerOffset, center);
  234. Matrix3.multiplyByScale(halfAxes, scale, halfAxes);
  235. return result;
  236. }
  237. var scratchRectangleCenterCartographic = new Cartographic();
  238. var scratchRectangleCenter = new Cartesian3();
  239. var scratchPerimeterCartographicNC = new Cartographic();
  240. var scratchPerimeterCartographicNW = new Cartographic();
  241. var scratchPerimeterCartographicCW = new Cartographic();
  242. var scratchPerimeterCartographicSW = new Cartographic();
  243. var scratchPerimeterCartographicSC = new Cartographic();
  244. var scratchPerimeterCartesianNC = new Cartesian3();
  245. var scratchPerimeterCartesianNW = new Cartesian3();
  246. var scratchPerimeterCartesianCW = new Cartesian3();
  247. var scratchPerimeterCartesianSW = new Cartesian3();
  248. var scratchPerimeterCartesianSC = new Cartesian3();
  249. var scratchPerimeterProjectedNC = new Cartesian2();
  250. var scratchPerimeterProjectedNW = new Cartesian2();
  251. var scratchPerimeterProjectedCW = new Cartesian2();
  252. var scratchPerimeterProjectedSW = new Cartesian2();
  253. var scratchPerimeterProjectedSC = new Cartesian2();
  254. var scratchPlaneOrigin = new Cartesian3();
  255. var scratchPlaneNormal = new Cartesian3();
  256. var scratchPlaneXAxis = new Cartesian3();
  257. var scratchHorizonCartesian = new Cartesian3();
  258. var scratchHorizonProjected = new Cartesian2();
  259. var scratchMaxY = new Cartesian3();
  260. var scratchMinY = new Cartesian3();
  261. var scratchZ = new Cartesian3();
  262. var scratchPlane = new Plane(Cartesian3.UNIT_X, 0.0);
  263. /**
  264. * Computes an OrientedBoundingBox that bounds a {@link Rectangle} on the surface of an {@link Ellipsoid}.
  265. * There are no guarantees about the orientation of the bounding box.
  266. *
  267. * @param {Rectangle} rectangle The cartographic rectangle on the surface of the ellipsoid.
  268. * @param {Number} [minimumHeight=0.0] The minimum height (elevation) within the tile.
  269. * @param {Number} [maximumHeight=0.0] The maximum height (elevation) within the tile.
  270. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle is defined.
  271. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  272. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided.
  273. *
  274. * @exception {DeveloperError} rectangle.width must be between 0 and pi.
  275. * @exception {DeveloperError} rectangle.height must be between 0 and pi.
  276. * @exception {DeveloperError} ellipsoid must be an ellipsoid of revolution (<code>radii.x == radii.y</code>)
  277. */
  278. OrientedBoundingBox.fromRectangle = function(rectangle, minimumHeight, maximumHeight, ellipsoid, result) {
  279. //>>includeStart('debug', pragmas.debug);
  280. if (!defined(rectangle)) {
  281. throw new DeveloperError('rectangle is required');
  282. }
  283. if (rectangle.width < 0.0 || rectangle.width > CesiumMath.TWO_PI) {
  284. throw new DeveloperError('Rectangle width must be between 0 and 2*pi');
  285. }
  286. if (rectangle.height < 0.0 || rectangle.height > CesiumMath.PI) {
  287. throw new DeveloperError('Rectangle height must be between 0 and pi');
  288. }
  289. if (defined(ellipsoid) && !CesiumMath.equalsEpsilon(ellipsoid.radii.x, ellipsoid.radii.y, CesiumMath.EPSILON15)) {
  290. throw new DeveloperError('Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)');
  291. }
  292. //>>includeEnd('debug');
  293. minimumHeight = defaultValue(minimumHeight, 0.0);
  294. maximumHeight = defaultValue(maximumHeight, 0.0);
  295. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  296. var minX, maxX, minY, maxY, minZ, maxZ, plane;
  297. if (rectangle.width <= CesiumMath.PI) {
  298. // The bounding box will be aligned with the tangent plane at the center of the rectangle.
  299. var tangentPointCartographic = Rectangle.center(rectangle, scratchRectangleCenterCartographic);
  300. var tangentPoint = ellipsoid.cartographicToCartesian(tangentPointCartographic, scratchRectangleCenter);
  301. var tangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid);
  302. plane = tangentPlane.plane;
  303. // If the rectangle spans the equator, CW is instead aligned with the equator (because it sticks out the farthest at the equator).
  304. var lonCenter = tangentPointCartographic.longitude;
  305. var latCenter = (rectangle.south < 0.0 && rectangle.north > 0.0) ? 0.0 : tangentPointCartographic.latitude;
  306. // Compute XY extents using the rectangle at maximum height
  307. var perimeterCartographicNC = Cartographic.fromRadians(lonCenter, rectangle.north, maximumHeight, scratchPerimeterCartographicNC);
  308. var perimeterCartographicNW = Cartographic.fromRadians(rectangle.west, rectangle.north, maximumHeight, scratchPerimeterCartographicNW);
  309. var perimeterCartographicCW = Cartographic.fromRadians(rectangle.west, latCenter, maximumHeight, scratchPerimeterCartographicCW);
  310. var perimeterCartographicSW = Cartographic.fromRadians(rectangle.west, rectangle.south, maximumHeight, scratchPerimeterCartographicSW);
  311. var perimeterCartographicSC = Cartographic.fromRadians(lonCenter, rectangle.south, maximumHeight, scratchPerimeterCartographicSC);
  312. var perimeterCartesianNC = ellipsoid.cartographicToCartesian(perimeterCartographicNC, scratchPerimeterCartesianNC);
  313. var perimeterCartesianNW = ellipsoid.cartographicToCartesian(perimeterCartographicNW, scratchPerimeterCartesianNW);
  314. var perimeterCartesianCW = ellipsoid.cartographicToCartesian(perimeterCartographicCW, scratchPerimeterCartesianCW);
  315. var perimeterCartesianSW = ellipsoid.cartographicToCartesian(perimeterCartographicSW, scratchPerimeterCartesianSW);
  316. var perimeterCartesianSC = ellipsoid.cartographicToCartesian(perimeterCartographicSC, scratchPerimeterCartesianSC);
  317. var perimeterProjectedNC = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianNC, scratchPerimeterProjectedNC);
  318. var perimeterProjectedNW = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianNW, scratchPerimeterProjectedNW);
  319. var perimeterProjectedCW = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianCW, scratchPerimeterProjectedCW);
  320. var perimeterProjectedSW = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianSW, scratchPerimeterProjectedSW);
  321. var perimeterProjectedSC = tangentPlane.projectPointToNearestOnPlane(perimeterCartesianSC, scratchPerimeterProjectedSC);
  322. minX = Math.min(perimeterProjectedNW.x, perimeterProjectedCW.x, perimeterProjectedSW.x);
  323. maxX = -minX; // symmetrical
  324. maxY = Math.max(perimeterProjectedNW.y, perimeterProjectedNC.y);
  325. minY = Math.min(perimeterProjectedSW.y, perimeterProjectedSC.y);
  326. // Compute minimum Z using the rectangle at minimum height, since it will be deeper than the maximum height
  327. perimeterCartographicNW.height = perimeterCartographicSW.height = minimumHeight;
  328. perimeterCartesianNW = ellipsoid.cartographicToCartesian(perimeterCartographicNW, scratchPerimeterCartesianNW);
  329. perimeterCartesianSW = ellipsoid.cartographicToCartesian(perimeterCartographicSW, scratchPerimeterCartesianSW);
  330. minZ = Math.min(Plane.getPointDistance(plane, perimeterCartesianNW), Plane.getPointDistance(plane, perimeterCartesianSW));
  331. maxZ = maximumHeight; // Since the tangent plane touches the surface at height = 0, this is okay
  332. return fromPlaneExtents(tangentPlane.origin, tangentPlane.xAxis, tangentPlane.yAxis, tangentPlane.zAxis, minX, maxX, minY, maxY, minZ, maxZ, result);
  333. }
  334. // Handle the case where rectangle width is greater than PI (wraps around more than half the ellipsoid).
  335. var fullyAboveEquator = rectangle.south > 0.0;
  336. var fullyBelowEquator = rectangle.north < 0.0;
  337. var latitudeNearestToEquator = fullyAboveEquator ? rectangle.south : (fullyBelowEquator ? rectangle.north : 0.0);
  338. var centerLongitude = Rectangle.center(rectangle, scratchRectangleCenterCartographic).longitude;
  339. // Plane is located at the rectangle's center longitude and the rectangle's latitude that is closest to the equator. It rotates around the Z axis.
  340. // This results in a better fit than the obb approach for smaller rectangles, which orients with the rectangle's center normal.
  341. var planeOrigin = Cartesian3.fromRadians(centerLongitude, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchPlaneOrigin);
  342. planeOrigin.z = 0.0; // center the plane on the equator to simpify plane normal calculation
  343. var isPole = Math.abs(planeOrigin.x) < CesiumMath.EPSILON10 && Math.abs(planeOrigin.y) < CesiumMath.EPSILON10;
  344. var planeNormal = !isPole ? Cartesian3.normalize(planeOrigin, scratchPlaneNormal) : Cartesian3.UNIT_X;
  345. var planeYAxis = Cartesian3.UNIT_Z;
  346. var planeXAxis = Cartesian3.cross(planeNormal, planeYAxis, scratchPlaneXAxis);
  347. plane = Plane.fromPointNormal(planeOrigin, planeNormal, scratchPlane);
  348. // Get the horizon point relative to the center. This will be the farthest extent in the plane's X dimension.
  349. var horizonCartesian = Cartesian3.fromRadians(centerLongitude + CesiumMath.PI_OVER_TWO, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchHorizonCartesian);
  350. maxX = Cartesian3.dot(Plane.projectPointOntoPlane(plane, horizonCartesian, scratchHorizonProjected), planeXAxis);
  351. minX = -maxX; // symmetrical
  352. // Get the min and max Y, using the height that will give the largest extent
  353. maxY = Cartesian3.fromRadians(0.0, rectangle.north, fullyBelowEquator ? minimumHeight : maximumHeight, ellipsoid, scratchMaxY).z;
  354. minY = Cartesian3.fromRadians(0.0, rectangle.south, fullyAboveEquator ? minimumHeight : maximumHeight, ellipsoid, scratchMinY).z;
  355. var farZ = Cartesian3.fromRadians(rectangle.east, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchZ);
  356. minZ = Plane.getPointDistance(plane, farZ);
  357. maxZ = 0.0; // plane origin starts at maxZ already
  358. // min and max are local to the plane axes
  359. return fromPlaneExtents(planeOrigin, planeXAxis, planeYAxis, planeNormal, minX, maxX, minY, maxY, minZ, maxZ, result);
  360. };
  361. /**
  362. * Duplicates a OrientedBoundingBox instance.
  363. *
  364. * @param {OrientedBoundingBox} box The bounding box to duplicate.
  365. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  366. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided. (Returns undefined if box is undefined)
  367. */
  368. OrientedBoundingBox.clone = function(box, result) {
  369. if (!defined(box)) {
  370. return undefined;
  371. }
  372. if (!defined(result)) {
  373. return new OrientedBoundingBox(box.center, box.halfAxes);
  374. }
  375. Cartesian3.clone(box.center, result.center);
  376. Matrix3.clone(box.halfAxes, result.halfAxes);
  377. return result;
  378. };
  379. /**
  380. * Determines which side of a plane the oriented bounding box is located.
  381. *
  382. * @param {OrientedBoundingBox} box The oriented bounding box to test.
  383. * @param {Plane} plane The plane to test against.
  384. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane
  385. * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is
  386. * on the opposite side, and {@link Intersect.INTERSECTING} if the box
  387. * intersects the plane.
  388. */
  389. OrientedBoundingBox.intersectPlane = function(box, plane) {
  390. //>>includeStart('debug', pragmas.debug);
  391. if (!defined(box)) {
  392. throw new DeveloperError('box is required.');
  393. }
  394. if (!defined(plane)) {
  395. throw new DeveloperError('plane is required.');
  396. }
  397. //>>includeEnd('debug');
  398. var center = box.center;
  399. var normal = plane.normal;
  400. var halfAxes = box.halfAxes;
  401. var normalX = normal.x, normalY = normal.y, normalZ = normal.z;
  402. // plane is used as if it is its normal; the first three components are assumed to be normalized
  403. var radEffective = Math.abs(normalX * halfAxes[Matrix3.COLUMN0ROW0] + normalY * halfAxes[Matrix3.COLUMN0ROW1] + normalZ * halfAxes[Matrix3.COLUMN0ROW2]) +
  404. Math.abs(normalX * halfAxes[Matrix3.COLUMN1ROW0] + normalY * halfAxes[Matrix3.COLUMN1ROW1] + normalZ * halfAxes[Matrix3.COLUMN1ROW2]) +
  405. Math.abs(normalX * halfAxes[Matrix3.COLUMN2ROW0] + normalY * halfAxes[Matrix3.COLUMN2ROW1] + normalZ * halfAxes[Matrix3.COLUMN2ROW2]);
  406. var distanceToPlane = Cartesian3.dot(normal, center) + plane.distance;
  407. if (distanceToPlane <= -radEffective) {
  408. // The entire box is on the negative side of the plane normal
  409. return Intersect.OUTSIDE;
  410. } else if (distanceToPlane >= radEffective) {
  411. // The entire box is on the positive side of the plane normal
  412. return Intersect.INSIDE;
  413. }
  414. return Intersect.INTERSECTING;
  415. };
  416. var scratchCartesianU = new Cartesian3();
  417. var scratchCartesianV = new Cartesian3();
  418. var scratchCartesianW = new Cartesian3();
  419. var scratchPPrime = new Cartesian3();
  420. /**
  421. * Computes the estimated distance squared from the closest point on a bounding box to a point.
  422. *
  423. * @param {OrientedBoundingBox} box The box.
  424. * @param {Cartesian3} cartesian The point
  425. * @returns {Number} The estimated distance squared from the bounding sphere to the point.
  426. *
  427. * @example
  428. * // Sort bounding boxes from back to front
  429. * boxes.sort(function(a, b) {
  430. * return Cesium.OrientedBoundingBox.distanceSquaredTo(b, camera.positionWC) - Cesium.OrientedBoundingBox.distanceSquaredTo(a, camera.positionWC);
  431. * });
  432. */
  433. OrientedBoundingBox.distanceSquaredTo = function(box, cartesian) {
  434. // See Geometric Tools for Computer Graphics 10.4.2
  435. //>>includeStart('debug', pragmas.debug);
  436. if (!defined(box)) {
  437. throw new DeveloperError('box is required.');
  438. }
  439. if (!defined(cartesian)) {
  440. throw new DeveloperError('cartesian is required.');
  441. }
  442. //>>includeEnd('debug');
  443. var offset = Cartesian3.subtract(cartesian, box.center, scratchOffset);
  444. var halfAxes = box.halfAxes;
  445. var u = Matrix3.getColumn(halfAxes, 0, scratchCartesianU);
  446. var v = Matrix3.getColumn(halfAxes, 1, scratchCartesianV);
  447. var w = Matrix3.getColumn(halfAxes, 2, scratchCartesianW);
  448. var uHalf = Cartesian3.magnitude(u);
  449. var vHalf = Cartesian3.magnitude(v);
  450. var wHalf = Cartesian3.magnitude(w);
  451. Cartesian3.normalize(u, u);
  452. Cartesian3.normalize(v, v);
  453. Cartesian3.normalize(w, w);
  454. var pPrime = scratchPPrime;
  455. pPrime.x = Cartesian3.dot(offset, u);
  456. pPrime.y = Cartesian3.dot(offset, v);
  457. pPrime.z = Cartesian3.dot(offset, w);
  458. var distanceSquared = 0.0;
  459. var d;
  460. if (pPrime.x < -uHalf) {
  461. d = pPrime.x + uHalf;
  462. distanceSquared += d * d;
  463. } else if (pPrime.x > uHalf) {
  464. d = pPrime.x - uHalf;
  465. distanceSquared += d * d;
  466. }
  467. if (pPrime.y < -vHalf) {
  468. d = pPrime.y + vHalf;
  469. distanceSquared += d * d;
  470. } else if (pPrime.y > vHalf) {
  471. d = pPrime.y - vHalf;
  472. distanceSquared += d * d;
  473. }
  474. if (pPrime.z < -wHalf) {
  475. d = pPrime.z + wHalf;
  476. distanceSquared += d * d;
  477. } else if (pPrime.z > wHalf) {
  478. d = pPrime.z - wHalf;
  479. distanceSquared += d * d;
  480. }
  481. return distanceSquared;
  482. };
  483. var scratchCorner = new Cartesian3();
  484. var scratchToCenter = new Cartesian3();
  485. /**
  486. * The distances calculated by the vector from the center of the bounding box to position projected onto direction.
  487. * <br>
  488. * If you imagine the infinite number of planes with normal direction, this computes the smallest distance to the
  489. * closest and farthest planes from position that intersect the bounding box.
  490. *
  491. * @param {OrientedBoundingBox} box The bounding box to calculate the distance to.
  492. * @param {Cartesian3} position The position to calculate the distance from.
  493. * @param {Cartesian3} direction The direction from position.
  494. * @param {Interval} [result] A Interval to store the nearest and farthest distances.
  495. * @returns {Interval} The nearest and farthest distances on the bounding box from position in direction.
  496. */
  497. OrientedBoundingBox.computePlaneDistances = function(box, position, direction, result) {
  498. //>>includeStart('debug', pragmas.debug);
  499. if (!defined(box)) {
  500. throw new DeveloperError('box is required.');
  501. }
  502. if (!defined(position)) {
  503. throw new DeveloperError('position is required.');
  504. }
  505. if (!defined(direction)) {
  506. throw new DeveloperError('direction is required.');
  507. }
  508. //>>includeEnd('debug');
  509. if (!defined(result)) {
  510. result = new Interval();
  511. }
  512. var minDist = Number.POSITIVE_INFINITY;
  513. var maxDist = Number.NEGATIVE_INFINITY;
  514. var center = box.center;
  515. var halfAxes = box.halfAxes;
  516. var u = Matrix3.getColumn(halfAxes, 0, scratchCartesianU);
  517. var v = Matrix3.getColumn(halfAxes, 1, scratchCartesianV);
  518. var w = Matrix3.getColumn(halfAxes, 2, scratchCartesianW);
  519. // project first corner
  520. var corner = Cartesian3.add(u, v, scratchCorner);
  521. Cartesian3.add(corner, w, corner);
  522. Cartesian3.add(corner, center, corner);
  523. var toCenter = Cartesian3.subtract(corner, position, scratchToCenter);
  524. var mag = Cartesian3.dot(direction, toCenter);
  525. minDist = Math.min(mag, minDist);
  526. maxDist = Math.max(mag, maxDist);
  527. // project second corner
  528. Cartesian3.add(center, u, corner);
  529. Cartesian3.add(corner, v, corner);
  530. Cartesian3.subtract(corner, w, corner);
  531. Cartesian3.subtract(corner, position, toCenter);
  532. mag = Cartesian3.dot(direction, toCenter);
  533. minDist = Math.min(mag, minDist);
  534. maxDist = Math.max(mag, maxDist);
  535. // project third corner
  536. Cartesian3.add(center, u, corner);
  537. Cartesian3.subtract(corner, v, corner);
  538. Cartesian3.add(corner, w, corner);
  539. Cartesian3.subtract(corner, position, toCenter);
  540. mag = Cartesian3.dot(direction, toCenter);
  541. minDist = Math.min(mag, minDist);
  542. maxDist = Math.max(mag, maxDist);
  543. // project fourth corner
  544. Cartesian3.add(center, u, corner);
  545. Cartesian3.subtract(corner, v, corner);
  546. Cartesian3.subtract(corner, w, corner);
  547. Cartesian3.subtract(corner, position, toCenter);
  548. mag = Cartesian3.dot(direction, toCenter);
  549. minDist = Math.min(mag, minDist);
  550. maxDist = Math.max(mag, maxDist);
  551. // project fifth corner
  552. Cartesian3.subtract(center, u, corner);
  553. Cartesian3.add(corner, v, corner);
  554. Cartesian3.add(corner, w, corner);
  555. Cartesian3.subtract(corner, position, toCenter);
  556. mag = Cartesian3.dot(direction, toCenter);
  557. minDist = Math.min(mag, minDist);
  558. maxDist = Math.max(mag, maxDist);
  559. // project sixth corner
  560. Cartesian3.subtract(center, u, corner);
  561. Cartesian3.add(corner, v, corner);
  562. Cartesian3.subtract(corner, w, corner);
  563. Cartesian3.subtract(corner, position, toCenter);
  564. mag = Cartesian3.dot(direction, toCenter);
  565. minDist = Math.min(mag, minDist);
  566. maxDist = Math.max(mag, maxDist);
  567. // project seventh corner
  568. Cartesian3.subtract(center, u, corner);
  569. Cartesian3.subtract(corner, v, corner);
  570. Cartesian3.add(corner, w, corner);
  571. Cartesian3.subtract(corner, position, toCenter);
  572. mag = Cartesian3.dot(direction, toCenter);
  573. minDist = Math.min(mag, minDist);
  574. maxDist = Math.max(mag, maxDist);
  575. // project eighth corner
  576. Cartesian3.subtract(center, u, corner);
  577. Cartesian3.subtract(corner, v, corner);
  578. Cartesian3.subtract(corner, w, corner);
  579. Cartesian3.subtract(corner, position, toCenter);
  580. mag = Cartesian3.dot(direction, toCenter);
  581. minDist = Math.min(mag, minDist);
  582. maxDist = Math.max(mag, maxDist);
  583. result.start = minDist;
  584. result.stop = maxDist;
  585. return result;
  586. };
  587. var scratchBoundingSphere = new BoundingSphere();
  588. /**
  589. * Determines whether or not a bounding box is hidden from view by the occluder.
  590. *
  591. * @param {OrientedBoundingBox} box The bounding box surrounding the occludee object.
  592. * @param {Occluder} occluder The occluder.
  593. * @returns {Boolean} <code>true</code> if the box is not visible; otherwise <code>false</code>.
  594. */
  595. OrientedBoundingBox.isOccluded = function(box, occluder) {
  596. //>>includeStart('debug', pragmas.debug);
  597. if (!defined(box)) {
  598. throw new DeveloperError('box is required.');
  599. }
  600. if (!defined(occluder)) {
  601. throw new DeveloperError('occluder is required.');
  602. }
  603. //>>includeEnd('debug');
  604. var sphere = BoundingSphere.fromOrientedBoundingBox(box, scratchBoundingSphere);
  605. return !occluder.isBoundingSphereVisible(sphere);
  606. };
  607. /**
  608. * Determines which side of a plane the oriented bounding box is located.
  609. *
  610. * @param {Plane} plane The plane to test against.
  611. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane
  612. * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is
  613. * on the opposite side, and {@link Intersect.INTERSECTING} if the box
  614. * intersects the plane.
  615. */
  616. OrientedBoundingBox.prototype.intersectPlane = function(plane) {
  617. return OrientedBoundingBox.intersectPlane(this, plane);
  618. };
  619. /**
  620. * Computes the estimated distance squared from the closest point on a bounding box to a point.
  621. *
  622. * @param {Cartesian3} cartesian The point
  623. * @returns {Number} The estimated distance squared from the bounding sphere to the point.
  624. *
  625. * @example
  626. * // Sort bounding boxes from back to front
  627. * boxes.sort(function(a, b) {
  628. * return b.distanceSquaredTo(camera.positionWC) - a.distanceSquaredTo(camera.positionWC);
  629. * });
  630. */
  631. OrientedBoundingBox.prototype.distanceSquaredTo = function(cartesian) {
  632. return OrientedBoundingBox.distanceSquaredTo(this, cartesian);
  633. };
  634. /**
  635. * The distances calculated by the vector from the center of the bounding box to position projected onto direction.
  636. * <br>
  637. * If you imagine the infinite number of planes with normal direction, this computes the smallest distance to the
  638. * closest and farthest planes from position that intersect the bounding box.
  639. *
  640. * @param {Cartesian3} position The position to calculate the distance from.
  641. * @param {Cartesian3} direction The direction from position.
  642. * @param {Interval} [result] A Interval to store the nearest and farthest distances.
  643. * @returns {Interval} The nearest and farthest distances on the bounding box from position in direction.
  644. */
  645. OrientedBoundingBox.prototype.computePlaneDistances = function(position, direction, result) {
  646. return OrientedBoundingBox.computePlaneDistances(this, position, direction, result);
  647. };
  648. /**
  649. * Determines whether or not a bounding box is hidden from view by the occluder.
  650. *
  651. * @param {Occluder} occluder The occluder.
  652. * @returns {Boolean} <code>true</code> if the sphere is not visible; otherwise <code>false</code>.
  653. */
  654. OrientedBoundingBox.prototype.isOccluded = function(occluder) {
  655. return OrientedBoundingBox.isOccluded(this, occluder);
  656. };
  657. /**
  658. * Compares the provided OrientedBoundingBox componentwise and returns
  659. * <code>true</code> if they are equal, <code>false</code> otherwise.
  660. *
  661. * @param {OrientedBoundingBox} left The first OrientedBoundingBox.
  662. * @param {OrientedBoundingBox} right The second OrientedBoundingBox.
  663. * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  664. */
  665. OrientedBoundingBox.equals = function(left, right) {
  666. return (left === right) ||
  667. ((defined(left)) &&
  668. (defined(right)) &&
  669. Cartesian3.equals(left.center, right.center) &&
  670. Matrix3.equals(left.halfAxes, right.halfAxes));
  671. };
  672. /**
  673. * Duplicates this OrientedBoundingBox instance.
  674. *
  675. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  676. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided.
  677. */
  678. OrientedBoundingBox.prototype.clone = function(result) {
  679. return OrientedBoundingBox.clone(this, result);
  680. };
  681. /**
  682. * Compares this OrientedBoundingBox against the provided OrientedBoundingBox componentwise and returns
  683. * <code>true</code> if they are equal, <code>false</code> otherwise.
  684. *
  685. * @param {OrientedBoundingBox} [right] The right hand side OrientedBoundingBox.
  686. * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
  687. */
  688. OrientedBoundingBox.prototype.equals = function(right) {
  689. return OrientedBoundingBox.equals(this, right);
  690. };
  691. export default OrientedBoundingBox;