OrientedBoundingBox.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  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 fromTangentPlaneExtents(tangentPlane, 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, tangentPlane.xAxis, halfAxes);
  221. Matrix3.setColumn(halfAxes, 1, tangentPlane.yAxis, halfAxes);
  222. Matrix3.setColumn(halfAxes, 2, tangentPlane.zAxis, 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(tangentPlane.origin, centerOffset, center);
  234. Matrix3.multiplyByScale(halfAxes, scale, halfAxes);
  235. return result;
  236. }
  237. var scratchRectangleCenterCartographic = new Cartographic();
  238. var scratchRectangleCenter = new Cartesian3();
  239. var perimeterCartographicScratch = [new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic()];
  240. var perimeterCartesianScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
  241. var perimeterProjectedScratch = [new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2()];
  242. /**
  243. * Computes an OrientedBoundingBox that bounds a {@link Rectangle} on the surface of an {@link Ellipsoid}.
  244. * There are no guarantees about the orientation of the bounding box.
  245. *
  246. * @param {Rectangle} rectangle The cartographic rectangle on the surface of the ellipsoid.
  247. * @param {Number} [minimumHeight=0.0] The minimum height (elevation) within the tile.
  248. * @param {Number} [maximumHeight=0.0] The maximum height (elevation) within the tile.
  249. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle is defined.
  250. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  251. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided.
  252. *
  253. * @exception {DeveloperError} rectangle.width must be between 0 and pi.
  254. * @exception {DeveloperError} rectangle.height must be between 0 and pi.
  255. * @exception {DeveloperError} ellipsoid must be an ellipsoid of revolution (<code>radii.x == radii.y</code>)
  256. */
  257. OrientedBoundingBox.fromRectangle = function(rectangle, minimumHeight, maximumHeight, ellipsoid, result) {
  258. //>>includeStart('debug', pragmas.debug);
  259. if (!defined(rectangle)) {
  260. throw new DeveloperError('rectangle is required');
  261. }
  262. if (rectangle.width < 0.0 || rectangle.width > CesiumMath.TWO_PI) {
  263. throw new DeveloperError('Rectangle width must be between 0 and 2*pi');
  264. }
  265. if (rectangle.height < 0.0 || rectangle.height > CesiumMath.PI) {
  266. throw new DeveloperError('Rectangle height must be between 0 and pi');
  267. }
  268. if (defined(ellipsoid) && !CesiumMath.equalsEpsilon(ellipsoid.radii.x, ellipsoid.radii.y, CesiumMath.EPSILON15)) {
  269. throw new DeveloperError('Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)');
  270. }
  271. //>>includeEnd('debug');
  272. minimumHeight = defaultValue(minimumHeight, 0.0);
  273. maximumHeight = defaultValue(maximumHeight, 0.0);
  274. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  275. // The bounding box will be aligned with the tangent plane at the center of the rectangle.
  276. var tangentPointCartographic = Rectangle.center(rectangle, scratchRectangleCenterCartographic);
  277. var tangentPoint = ellipsoid.cartographicToCartesian(tangentPointCartographic, scratchRectangleCenter);
  278. var tangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid);
  279. var plane = tangentPlane.plane;
  280. // Corner arrangement:
  281. // N/+y
  282. // [0] [1] [2]
  283. // W/-x [7] [3] E/+x
  284. // [6] [5] [4]
  285. // S/-y
  286. // "C" refers to the central lat/long, which by default aligns with the tangent point (above).
  287. // If the rectangle spans the equator, CW and CE are instead aligned with the equator.
  288. var perimeterNW = perimeterCartographicScratch[0];
  289. var perimeterNC = perimeterCartographicScratch[1];
  290. var perimeterNE = perimeterCartographicScratch[2];
  291. var perimeterCE = perimeterCartographicScratch[3];
  292. var perimeterSE = perimeterCartographicScratch[4];
  293. var perimeterSC = perimeterCartographicScratch[5];
  294. var perimeterSW = perimeterCartographicScratch[6];
  295. var perimeterCW = perimeterCartographicScratch[7];
  296. var lonCenter = tangentPointCartographic.longitude;
  297. var latCenter = (rectangle.south < 0.0 && rectangle.north > 0.0) ? 0.0 : tangentPointCartographic.latitude;
  298. perimeterSW.latitude = perimeterSC.latitude = perimeterSE.latitude = rectangle.south;
  299. perimeterCW.latitude = perimeterCE.latitude = latCenter;
  300. perimeterNW.latitude = perimeterNC.latitude = perimeterNE.latitude = rectangle.north;
  301. perimeterSW.longitude = perimeterCW.longitude = perimeterNW.longitude = rectangle.west;
  302. perimeterSC.longitude = perimeterNC.longitude = lonCenter;
  303. perimeterSE.longitude = perimeterCE.longitude = perimeterNE.longitude = rectangle.east;
  304. // Compute XY extents using the rectangle at maximum height
  305. perimeterNE.height = perimeterNC.height = perimeterNW.height = perimeterCW.height = perimeterSW.height = perimeterSC.height = perimeterSE.height = perimeterCE.height = maximumHeight;
  306. ellipsoid.cartographicArrayToCartesianArray(perimeterCartographicScratch, perimeterCartesianScratch);
  307. tangentPlane.projectPointsToNearestOnPlane(perimeterCartesianScratch, perimeterProjectedScratch);
  308. // See the `perimeterXX` definitions above for what these are
  309. var minX = Math.min(perimeterProjectedScratch[6].x, perimeterProjectedScratch[7].x, perimeterProjectedScratch[0].x);
  310. var maxX = Math.max(perimeterProjectedScratch[2].x, perimeterProjectedScratch[3].x, perimeterProjectedScratch[4].x);
  311. var minY = Math.min(perimeterProjectedScratch[4].y, perimeterProjectedScratch[5].y, perimeterProjectedScratch[6].y);
  312. var maxY = Math.max(perimeterProjectedScratch[0].y, perimeterProjectedScratch[1].y, perimeterProjectedScratch[2].y);
  313. // Compute minimum Z using the rectangle at minimum height
  314. perimeterNE.height = perimeterNW.height = perimeterSE.height = perimeterSW.height = minimumHeight;
  315. ellipsoid.cartographicArrayToCartesianArray(perimeterCartographicScratch, perimeterCartesianScratch);
  316. var minZ = Math.min(Plane.getPointDistance(plane, perimeterCartesianScratch[0]),
  317. Plane.getPointDistance(plane, perimeterCartesianScratch[2]),
  318. Plane.getPointDistance(plane, perimeterCartesianScratch[4]),
  319. Plane.getPointDistance(plane, perimeterCartesianScratch[6]));
  320. var maxZ = maximumHeight; // Since the tangent plane touches the surface at height = 0, this is okay
  321. return fromTangentPlaneExtents(tangentPlane, minX, maxX, minY, maxY, minZ, maxZ, result);
  322. };
  323. /**
  324. * Duplicates a OrientedBoundingBox instance.
  325. *
  326. * @param {OrientedBoundingBox} box The bounding box to duplicate.
  327. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  328. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided. (Returns undefined if box is undefined)
  329. */
  330. OrientedBoundingBox.clone = function(box, result) {
  331. if (!defined(box)) {
  332. return undefined;
  333. }
  334. if (!defined(result)) {
  335. return new OrientedBoundingBox(box.center, box.halfAxes);
  336. }
  337. Cartesian3.clone(box.center, result.center);
  338. Matrix3.clone(box.halfAxes, result.halfAxes);
  339. return result;
  340. };
  341. /**
  342. * Determines which side of a plane the oriented bounding box is located.
  343. *
  344. * @param {OrientedBoundingBox} box The oriented bounding box to test.
  345. * @param {Plane} plane The plane to test against.
  346. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane
  347. * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is
  348. * on the opposite side, and {@link Intersect.INTERSECTING} if the box
  349. * intersects the plane.
  350. */
  351. OrientedBoundingBox.intersectPlane = function(box, plane) {
  352. //>>includeStart('debug', pragmas.debug);
  353. if (!defined(box)) {
  354. throw new DeveloperError('box is required.');
  355. }
  356. if (!defined(plane)) {
  357. throw new DeveloperError('plane is required.');
  358. }
  359. //>>includeEnd('debug');
  360. var center = box.center;
  361. var normal = plane.normal;
  362. var halfAxes = box.halfAxes;
  363. var normalX = normal.x, normalY = normal.y, normalZ = normal.z;
  364. // plane is used as if it is its normal; the first three components are assumed to be normalized
  365. var radEffective = Math.abs(normalX * halfAxes[Matrix3.COLUMN0ROW0] + normalY * halfAxes[Matrix3.COLUMN0ROW1] + normalZ * halfAxes[Matrix3.COLUMN0ROW2]) +
  366. Math.abs(normalX * halfAxes[Matrix3.COLUMN1ROW0] + normalY * halfAxes[Matrix3.COLUMN1ROW1] + normalZ * halfAxes[Matrix3.COLUMN1ROW2]) +
  367. Math.abs(normalX * halfAxes[Matrix3.COLUMN2ROW0] + normalY * halfAxes[Matrix3.COLUMN2ROW1] + normalZ * halfAxes[Matrix3.COLUMN2ROW2]);
  368. var distanceToPlane = Cartesian3.dot(normal, center) + plane.distance;
  369. if (distanceToPlane <= -radEffective) {
  370. // The entire box is on the negative side of the plane normal
  371. return Intersect.OUTSIDE;
  372. } else if (distanceToPlane >= radEffective) {
  373. // The entire box is on the positive side of the plane normal
  374. return Intersect.INSIDE;
  375. }
  376. return Intersect.INTERSECTING;
  377. };
  378. var scratchCartesianU = new Cartesian3();
  379. var scratchCartesianV = new Cartesian3();
  380. var scratchCartesianW = new Cartesian3();
  381. var scratchPPrime = new Cartesian3();
  382. /**
  383. * Computes the estimated distance squared from the closest point on a bounding box to a point.
  384. *
  385. * @param {OrientedBoundingBox} box The box.
  386. * @param {Cartesian3} cartesian The point
  387. * @returns {Number} The estimated distance squared from the bounding sphere to the point.
  388. *
  389. * @example
  390. * // Sort bounding boxes from back to front
  391. * boxes.sort(function(a, b) {
  392. * return Cesium.OrientedBoundingBox.distanceSquaredTo(b, camera.positionWC) - Cesium.OrientedBoundingBox.distanceSquaredTo(a, camera.positionWC);
  393. * });
  394. */
  395. OrientedBoundingBox.distanceSquaredTo = function(box, cartesian) {
  396. // See Geometric Tools for Computer Graphics 10.4.2
  397. //>>includeStart('debug', pragmas.debug);
  398. if (!defined(box)) {
  399. throw new DeveloperError('box is required.');
  400. }
  401. if (!defined(cartesian)) {
  402. throw new DeveloperError('cartesian is required.');
  403. }
  404. //>>includeEnd('debug');
  405. var offset = Cartesian3.subtract(cartesian, box.center, scratchOffset);
  406. var halfAxes = box.halfAxes;
  407. var u = Matrix3.getColumn(halfAxes, 0, scratchCartesianU);
  408. var v = Matrix3.getColumn(halfAxes, 1, scratchCartesianV);
  409. var w = Matrix3.getColumn(halfAxes, 2, scratchCartesianW);
  410. var uHalf = Cartesian3.magnitude(u);
  411. var vHalf = Cartesian3.magnitude(v);
  412. var wHalf = Cartesian3.magnitude(w);
  413. Cartesian3.normalize(u, u);
  414. Cartesian3.normalize(v, v);
  415. Cartesian3.normalize(w, w);
  416. var pPrime = scratchPPrime;
  417. pPrime.x = Cartesian3.dot(offset, u);
  418. pPrime.y = Cartesian3.dot(offset, v);
  419. pPrime.z = Cartesian3.dot(offset, w);
  420. var distanceSquared = 0.0;
  421. var d;
  422. if (pPrime.x < -uHalf) {
  423. d = pPrime.x + uHalf;
  424. distanceSquared += d * d;
  425. } else if (pPrime.x > uHalf) {
  426. d = pPrime.x - uHalf;
  427. distanceSquared += d * d;
  428. }
  429. if (pPrime.y < -vHalf) {
  430. d = pPrime.y + vHalf;
  431. distanceSquared += d * d;
  432. } else if (pPrime.y > vHalf) {
  433. d = pPrime.y - vHalf;
  434. distanceSquared += d * d;
  435. }
  436. if (pPrime.z < -wHalf) {
  437. d = pPrime.z + wHalf;
  438. distanceSquared += d * d;
  439. } else if (pPrime.z > wHalf) {
  440. d = pPrime.z - wHalf;
  441. distanceSquared += d * d;
  442. }
  443. return distanceSquared;
  444. };
  445. var scratchCorner = new Cartesian3();
  446. var scratchToCenter = new Cartesian3();
  447. /**
  448. * The distances calculated by the vector from the center of the bounding box to position projected onto direction.
  449. * <br>
  450. * If you imagine the infinite number of planes with normal direction, this computes the smallest distance to the
  451. * closest and farthest planes from position that intersect the bounding box.
  452. *
  453. * @param {OrientedBoundingBox} box The bounding box to calculate the distance to.
  454. * @param {Cartesian3} position The position to calculate the distance from.
  455. * @param {Cartesian3} direction The direction from position.
  456. * @param {Interval} [result] A Interval to store the nearest and farthest distances.
  457. * @returns {Interval} The nearest and farthest distances on the bounding box from position in direction.
  458. */
  459. OrientedBoundingBox.computePlaneDistances = function(box, position, direction, result) {
  460. //>>includeStart('debug', pragmas.debug);
  461. if (!defined(box)) {
  462. throw new DeveloperError('box is required.');
  463. }
  464. if (!defined(position)) {
  465. throw new DeveloperError('position is required.');
  466. }
  467. if (!defined(direction)) {
  468. throw new DeveloperError('direction is required.');
  469. }
  470. //>>includeEnd('debug');
  471. if (!defined(result)) {
  472. result = new Interval();
  473. }
  474. var minDist = Number.POSITIVE_INFINITY;
  475. var maxDist = Number.NEGATIVE_INFINITY;
  476. var center = box.center;
  477. var halfAxes = box.halfAxes;
  478. var u = Matrix3.getColumn(halfAxes, 0, scratchCartesianU);
  479. var v = Matrix3.getColumn(halfAxes, 1, scratchCartesianV);
  480. var w = Matrix3.getColumn(halfAxes, 2, scratchCartesianW);
  481. // project first corner
  482. var corner = Cartesian3.add(u, v, scratchCorner);
  483. Cartesian3.add(corner, w, corner);
  484. Cartesian3.add(corner, center, corner);
  485. var toCenter = Cartesian3.subtract(corner, position, scratchToCenter);
  486. var mag = Cartesian3.dot(direction, toCenter);
  487. minDist = Math.min(mag, minDist);
  488. maxDist = Math.max(mag, maxDist);
  489. // project second corner
  490. Cartesian3.add(center, u, corner);
  491. Cartesian3.add(corner, v, corner);
  492. Cartesian3.subtract(corner, w, corner);
  493. Cartesian3.subtract(corner, position, toCenter);
  494. mag = Cartesian3.dot(direction, toCenter);
  495. minDist = Math.min(mag, minDist);
  496. maxDist = Math.max(mag, maxDist);
  497. // project third corner
  498. Cartesian3.add(center, u, corner);
  499. Cartesian3.subtract(corner, v, corner);
  500. Cartesian3.add(corner, w, corner);
  501. Cartesian3.subtract(corner, position, toCenter);
  502. mag = Cartesian3.dot(direction, toCenter);
  503. minDist = Math.min(mag, minDist);
  504. maxDist = Math.max(mag, maxDist);
  505. // project fourth corner
  506. Cartesian3.add(center, u, corner);
  507. Cartesian3.subtract(corner, v, corner);
  508. Cartesian3.subtract(corner, w, corner);
  509. Cartesian3.subtract(corner, position, toCenter);
  510. mag = Cartesian3.dot(direction, toCenter);
  511. minDist = Math.min(mag, minDist);
  512. maxDist = Math.max(mag, maxDist);
  513. // project fifth corner
  514. Cartesian3.subtract(center, u, corner);
  515. Cartesian3.add(corner, v, corner);
  516. Cartesian3.add(corner, w, corner);
  517. Cartesian3.subtract(corner, position, toCenter);
  518. mag = Cartesian3.dot(direction, toCenter);
  519. minDist = Math.min(mag, minDist);
  520. maxDist = Math.max(mag, maxDist);
  521. // project sixth corner
  522. Cartesian3.subtract(center, u, corner);
  523. Cartesian3.add(corner, v, corner);
  524. Cartesian3.subtract(corner, w, corner);
  525. Cartesian3.subtract(corner, position, toCenter);
  526. mag = Cartesian3.dot(direction, toCenter);
  527. minDist = Math.min(mag, minDist);
  528. maxDist = Math.max(mag, maxDist);
  529. // project seventh corner
  530. Cartesian3.subtract(center, u, corner);
  531. Cartesian3.subtract(corner, v, corner);
  532. Cartesian3.add(corner, w, corner);
  533. Cartesian3.subtract(corner, position, toCenter);
  534. mag = Cartesian3.dot(direction, toCenter);
  535. minDist = Math.min(mag, minDist);
  536. maxDist = Math.max(mag, maxDist);
  537. // project eighth corner
  538. Cartesian3.subtract(center, u, corner);
  539. Cartesian3.subtract(corner, v, corner);
  540. Cartesian3.subtract(corner, w, corner);
  541. Cartesian3.subtract(corner, position, toCenter);
  542. mag = Cartesian3.dot(direction, toCenter);
  543. minDist = Math.min(mag, minDist);
  544. maxDist = Math.max(mag, maxDist);
  545. result.start = minDist;
  546. result.stop = maxDist;
  547. return result;
  548. };
  549. var scratchBoundingSphere = new BoundingSphere();
  550. /**
  551. * Determines whether or not a bounding box is hidden from view by the occluder.
  552. *
  553. * @param {OrientedBoundingBox} box The bounding box surrounding the occludee object.
  554. * @param {Occluder} occluder The occluder.
  555. * @returns {Boolean} <code>true</code> if the box is not visible; otherwise <code>false</code>.
  556. */
  557. OrientedBoundingBox.isOccluded = function(box, occluder) {
  558. //>>includeStart('debug', pragmas.debug);
  559. if (!defined(box)) {
  560. throw new DeveloperError('box is required.');
  561. }
  562. if (!defined(occluder)) {
  563. throw new DeveloperError('occluder is required.');
  564. }
  565. //>>includeEnd('debug');
  566. var sphere = BoundingSphere.fromOrientedBoundingBox(box, scratchBoundingSphere);
  567. return !occluder.isBoundingSphereVisible(sphere);
  568. };
  569. /**
  570. * Determines which side of a plane the oriented bounding box is located.
  571. *
  572. * @param {Plane} plane The plane to test against.
  573. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane
  574. * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is
  575. * on the opposite side, and {@link Intersect.INTERSECTING} if the box
  576. * intersects the plane.
  577. */
  578. OrientedBoundingBox.prototype.intersectPlane = function(plane) {
  579. return OrientedBoundingBox.intersectPlane(this, plane);
  580. };
  581. /**
  582. * Computes the estimated distance squared from the closest point on a bounding box to a point.
  583. *
  584. * @param {Cartesian3} cartesian The point
  585. * @returns {Number} The estimated distance squared from the bounding sphere to the point.
  586. *
  587. * @example
  588. * // Sort bounding boxes from back to front
  589. * boxes.sort(function(a, b) {
  590. * return b.distanceSquaredTo(camera.positionWC) - a.distanceSquaredTo(camera.positionWC);
  591. * });
  592. */
  593. OrientedBoundingBox.prototype.distanceSquaredTo = function(cartesian) {
  594. return OrientedBoundingBox.distanceSquaredTo(this, cartesian);
  595. };
  596. /**
  597. * The distances calculated by the vector from the center of the bounding box to position projected onto direction.
  598. * <br>
  599. * If you imagine the infinite number of planes with normal direction, this computes the smallest distance to the
  600. * closest and farthest planes from position that intersect the bounding box.
  601. *
  602. * @param {Cartesian3} position The position to calculate the distance from.
  603. * @param {Cartesian3} direction The direction from position.
  604. * @param {Interval} [result] A Interval to store the nearest and farthest distances.
  605. * @returns {Interval} The nearest and farthest distances on the bounding box from position in direction.
  606. */
  607. OrientedBoundingBox.prototype.computePlaneDistances = function(position, direction, result) {
  608. return OrientedBoundingBox.computePlaneDistances(this, position, direction, result);
  609. };
  610. /**
  611. * Determines whether or not a bounding box is hidden from view by the occluder.
  612. *
  613. * @param {Occluder} occluder The occluder.
  614. * @returns {Boolean} <code>true</code> if the sphere is not visible; otherwise <code>false</code>.
  615. */
  616. OrientedBoundingBox.prototype.isOccluded = function(occluder) {
  617. return OrientedBoundingBox.isOccluded(this, occluder);
  618. };
  619. /**
  620. * Compares the provided OrientedBoundingBox componentwise and returns
  621. * <code>true</code> if they are equal, <code>false</code> otherwise.
  622. *
  623. * @param {OrientedBoundingBox} left The first OrientedBoundingBox.
  624. * @param {OrientedBoundingBox} right The second OrientedBoundingBox.
  625. * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  626. */
  627. OrientedBoundingBox.equals = function(left, right) {
  628. return (left === right) ||
  629. ((defined(left)) &&
  630. (defined(right)) &&
  631. Cartesian3.equals(left.center, right.center) &&
  632. Matrix3.equals(left.halfAxes, right.halfAxes));
  633. };
  634. /**
  635. * Duplicates this OrientedBoundingBox instance.
  636. *
  637. * @param {OrientedBoundingBox} [result] The object onto which to store the result.
  638. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided.
  639. */
  640. OrientedBoundingBox.prototype.clone = function(result) {
  641. return OrientedBoundingBox.clone(this, result);
  642. };
  643. /**
  644. * Compares this OrientedBoundingBox against the provided OrientedBoundingBox componentwise and returns
  645. * <code>true</code> if they are equal, <code>false</code> otherwise.
  646. *
  647. * @param {OrientedBoundingBox} [right] The right hand side OrientedBoundingBox.
  648. * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
  649. */
  650. OrientedBoundingBox.prototype.equals = function(right) {
  651. return OrientedBoundingBox.equals(this, right);
  652. };
  653. export default OrientedBoundingBox;