Source: geom/Frustum.js

  1. /*
  2. * Copyright 2003-2006, 2009, 2017, United States Government, as represented by the Administrator of the
  3. * National Aeronautics and Space Administration. All rights reserved.
  4. *
  5. * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @exports Frustum
  19. */
  20. define([
  21. '../error/ArgumentError',
  22. '../geom/Matrix',
  23. '../geom/Plane',
  24. '../util/Logger'
  25. ],
  26. function (ArgumentError,
  27. Matrix,
  28. Plane,
  29. Logger) {
  30. "use strict";
  31. /**
  32. * Constructs a frustum.
  33. * @alias Frustum
  34. * @constructor
  35. * @classdesc Represents a six-sided view frustum in Cartesian coordinates.
  36. * @param {Plane} left The frustum's left plane.
  37. * @param {Plane} right The frustum's right plane.
  38. * @param {Plane} bottom The frustum's bottom plane.
  39. * @param {Plane} top The frustum's top plane.
  40. * @param {Plane} near The frustum's near plane.
  41. * @param {Plane} far The frustum's far plane.
  42. * @throws {ArgumentError} If any specified plane is null or undefined.
  43. */
  44. var Frustum = function (left, right, bottom, top, near, far) {
  45. if (!left || !right || !bottom || !top || !near || !far) {
  46. throw new ArgumentError(
  47. Logger.logMessage(Logger.LEVEL_SEVERE, "Frustum", "constructor", "missingPlane"));
  48. }
  49. // Internal. Intentionally not documented. See property accessors below for public interface.
  50. this._left = left;
  51. this._right = right;
  52. this._bottom = bottom;
  53. this._top = top;
  54. this._near = near;
  55. this._far = far;
  56. // Internal. Intentionally not documented.
  57. this._planes = [this._left, this._right, this._top, this._bottom, this._near, this._far];
  58. };
  59. // These accessors are defined in order to prevent changes that would make the properties inconsistent with the
  60. // planes array.
  61. Object.defineProperties(Frustum.prototype, {
  62. /**
  63. * This frustum's left plane.
  64. * @memberof Frustum.prototype
  65. * @type {Plane}
  66. * @readonly
  67. */
  68. left: {
  69. get: function() {
  70. return this._left;
  71. }
  72. },
  73. /**
  74. * This frustum's right plane.
  75. * @memberof Frustum.prototype
  76. * @type {Plane}
  77. * @readonly
  78. */
  79. right: {
  80. get: function() {
  81. return this._right;
  82. }
  83. },
  84. /**
  85. * This frustum's bottom plane.
  86. * @memberof Frustum.prototype
  87. * @type {Plane}
  88. * @readonly
  89. */
  90. bottom: {
  91. get: function() {
  92. return this._bottom;
  93. }
  94. },
  95. /**
  96. * This frustum's top plane.
  97. * @memberof Frustum.prototype
  98. * @type {Plane}
  99. * @readonly
  100. */
  101. top: {
  102. get: function() {
  103. return this._top;
  104. }
  105. },
  106. /**
  107. * This frustum's near plane.
  108. * @memberof Frustum.prototype
  109. * @type {Plane}
  110. * @readonly
  111. */
  112. near: {
  113. get: function() {
  114. return this._near;
  115. }
  116. },
  117. /**
  118. * This frustum's far plane.
  119. * @memberof Frustum.prototype
  120. * @type {Plane}
  121. * @readonly
  122. */
  123. far: {
  124. get: function() {
  125. return this._far;
  126. }
  127. }
  128. });
  129. /**
  130. * Transforms this frustum by a specified matrix.
  131. * @param {Matrix} matrix The matrix to apply to this frustum.
  132. * @returns {Frustum} This frustum set to its original value multiplied by the specified matrix.
  133. * @throws {ArgumentError} If the specified matrix is null or undefined.
  134. */
  135. Frustum.prototype.transformByMatrix = function (matrix) {
  136. if (!matrix) {
  137. throw new ArgumentError(
  138. Logger.logMessage(Logger.LEVEL_SEVERE, "Frustum", "transformByMatrix", "missingMatrix"));
  139. }
  140. this._left.transformByMatrix(matrix);
  141. this._right.transformByMatrix(matrix);
  142. this._bottom.transformByMatrix(matrix);
  143. this._top.transformByMatrix(matrix);
  144. this._near.transformByMatrix(matrix);
  145. this._far.transformByMatrix(matrix);
  146. return this;
  147. };
  148. /**
  149. * Normalizes the plane vectors of the planes composing this frustum.
  150. * @returns {Frustum} This frustum with its planes normalized.
  151. */
  152. Frustum.prototype.normalize = function () {
  153. this._left.normalize();
  154. this._right.normalize();
  155. this._bottom.normalize();
  156. this._top.normalize();
  157. this._near.normalize();
  158. this._far.normalize();
  159. return this;
  160. };
  161. /**
  162. * Returns a new frustum with each of its planes 1 meter from the center.
  163. * @returns {Frustum} The new frustum.
  164. */
  165. Frustum.unitFrustum = function () {
  166. return new Frustum(
  167. new Plane(1, 0, 0, 1), // left
  168. new Plane(-1, 0, 0, 1), // right
  169. new Plane(0, 1, 1, 1), // bottom
  170. new Plane(0, -1, 0, 1), // top
  171. new Plane(0, 0, -1, 1), // near
  172. new Plane(0, 0, 1, 1) // far
  173. );
  174. };
  175. /**
  176. * Extracts a frustum from a projection matrix.
  177. * <p>
  178. * This method assumes that the specified matrix represents a projection matrix. If it does not represent a projection matrix
  179. * the results are undefined.
  180. * <p>
  181. * A projection matrix's view frustum is a Cartesian volume that contains everything visible in a scene displayed
  182. * using that projection matrix.
  183. *
  184. * @param {Matrix} matrix The projection matrix to extract the frustum from.
  185. * @return {Frustum} A new frustum containing the projection matrix's view frustum, in eye coordinates.
  186. * @throws {ArgumentError} If the specified matrix is null or undefined.
  187. */
  188. Frustum.fromProjectionMatrix = function (matrix) {
  189. if (!matrix) {
  190. throw new ArgumentError(
  191. Logger.logMessage(Logger.LEVEL_SEVERE, "Frustum", "fromProjectionMatrix", "missingMatrix"));
  192. }
  193. var x, y, z, w, d, left, right, top, bottom, near, far;
  194. // Left Plane = row 4 + row 1:
  195. x = matrix[12] + matrix[0];
  196. y = matrix[13] + matrix[1];
  197. z = matrix[14] + matrix[2];
  198. w = matrix[15] + matrix[3];
  199. d = Math.sqrt(x * x + y * y + z * z); // for normalizing the coordinates
  200. left = new Plane(x / d, y / d, z / d, w / d);
  201. // Right Plane = row 4 - row 1:
  202. x = matrix[12] - matrix[0];
  203. y = matrix[13] - matrix[1];
  204. z = matrix[14] - matrix[2];
  205. w = matrix[15] - matrix[3];
  206. d = Math.sqrt(x * x + y * y + z * z); // for normalizing the coordinates
  207. right = new Plane(x / d, y / d, z / d, w / d);
  208. // Bottom Plane = row 4 + row 2:
  209. x = matrix[12] + matrix[4];
  210. y = matrix[13] + matrix[5];
  211. z = matrix[14] + matrix[6];
  212. w = matrix[15] + matrix[7];
  213. d = Math.sqrt(x * x + y * y + z * z); // for normalizing the coordinates
  214. bottom = new Plane(x / d, y / d, z / d, w / d);
  215. // Top Plane = row 4 - row 2:
  216. x = matrix[12] - matrix[4];
  217. y = matrix[13] - matrix[5];
  218. z = matrix[14] - matrix[6];
  219. w = matrix[15] - matrix[7];
  220. d = Math.sqrt(x * x + y * y + z * z); // for normalizing the coordinates
  221. top = new Plane(x / d, y / d, z / d, w / d);
  222. // Near Plane = row 4 + row 3:
  223. x = matrix[12] + matrix[8];
  224. y = matrix[13] + matrix[9];
  225. z = matrix[14] + matrix[10];
  226. w = matrix[15] + matrix[11];
  227. d = Math.sqrt(x * x + y * y + z * z); // for normalizing the coordinates
  228. near = new Plane(x / d, y / d, z / d, w / d);
  229. // Far Plane = row 4 - row 3:
  230. x = matrix[12] - matrix[8];
  231. y = matrix[13] - matrix[9];
  232. z = matrix[14] - matrix[10];
  233. w = matrix[15] - matrix[11];
  234. d = Math.sqrt(x * x + y * y + z * z); // for normalizing the coordinates
  235. far = new Plane(x / d, y / d, z / d, w / d);
  236. return new Frustum(left, right, bottom, top, near, far);
  237. };
  238. Frustum.prototype.containsPoint = function (point) {
  239. if (!point) {
  240. throw new ArgumentError(
  241. Logger.logMessage(Logger.LEVEL_SEVERE, "Frustum", "containsPoint", "missingPoint"));
  242. }
  243. // See if the point is entirely within the frustum. The dot product of the point with each plane's vector
  244. // provides a distance to each plane. If this distance is less than 0, the point is clipped by that plane and
  245. // neither intersects nor is contained by the space enclosed by this Frustum.
  246. if (this._far.dot(point) <= 0)
  247. return false;
  248. if (this._left.dot(point) <= 0)
  249. return false;
  250. if (this._right.dot(point) <= 0)
  251. return false;
  252. if (this._top.dot(point) <= 0)
  253. return false;
  254. if (this._bottom.dot(point) <= 0)
  255. return false;
  256. if (this._near.dot(point) <= 0)
  257. return false;
  258. return true;
  259. };
  260. /**
  261. * Determines whether a line segment intersects this frustum.
  262. *
  263. * @param {Vec3} pointA One end of the segment.
  264. * @param {Vec3} pointB The other end of the segment.
  265. *
  266. * @return {boolean} <code>true</code> if the segment intersects or is contained in this frustum,
  267. * otherwise <code>false</code>.
  268. *
  269. * @throws {ArgumentError} If either point is null or undefined.
  270. */
  271. Frustum.prototype.intersectsSegment = function (pointA, pointB) {
  272. if (!pointA || !pointB) {
  273. throw new ArgumentError(
  274. Logger.logMessage(Logger.LEVEL_SEVERE, "Frustum", "containsPoint", "missingPoint"));
  275. }
  276. // First do a trivial accept test.
  277. if (this.containsPoint(pointA) || this.containsPoint(pointB))
  278. return true;
  279. if (pointA.equals(pointB))
  280. return false;
  281. for (var i = 0, len = this._planes.length; i < len; i++) {
  282. // See if both points are behind the plane and therefore not in the frustum.
  283. if (this._planes[i].onSameSide(pointA, pointB) < 0)
  284. return false;
  285. // See if the segment intersects the plane.
  286. if (this._planes[i].clip(pointA, pointB) != null)
  287. return true;
  288. }
  289. return false; // segment does not intersect frustum
  290. };
  291. return Frustum;
  292. });