babylon.bounding2d.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. module BABYLON {
  2. /**
  3. * Stores 2D Bounding Information.
  4. * This class handles a circle area and a bounding rectangle one.
  5. */
  6. export class BoundingInfo2D {
  7. /**
  8. * The coordinate of the center of the bounding info
  9. */
  10. public center: Vector2;
  11. /**
  12. * The radius of the bounding circle, from the center of the bounded object
  13. */
  14. public radius: number;
  15. /**
  16. * The extent of the bounding rectangle, from the center of the bounded object.
  17. * This is an absolute value in both X and Y of the vector which describe the right/top corner of the rectangle, you can easily reconstruct the whole rectangle by negating X &| Y.
  18. */
  19. public extent: Vector2;
  20. constructor() {
  21. this.radius = 0;
  22. this.center = Vector2.Zero();
  23. this.extent = Vector2.Zero();
  24. this._worldAABBDirty = false;
  25. this._worldAABBDirtyObservable = null;
  26. this._worldAABB = Vector4.Zero();
  27. }
  28. /**
  29. * Create a BoundingInfo2D object from a given size
  30. * @param size the size that will be used to set the extend, radius will be computed from it.
  31. */
  32. public static CreateFromSize(size: Size): BoundingInfo2D {
  33. let r = new BoundingInfo2D();
  34. BoundingInfo2D.CreateFromSizeToRef(size, r);
  35. return r;
  36. }
  37. /**
  38. * Create a BoundingInfo2D object from a given radius
  39. * @param radius the radius to use, the extent will be computed from it.
  40. */
  41. public static CreateFromRadius(radius: number): BoundingInfo2D {
  42. let r = new BoundingInfo2D();
  43. BoundingInfo2D.CreateFromRadiusToRef(radius, r);
  44. return r;
  45. }
  46. /**
  47. * Create a BoundingInfo2D object from a list of points.
  48. * The resulted object will be the smallest bounding area that includes all the given points.
  49. * @param points an array of points to compute the bounding object from.
  50. */
  51. public static CreateFromPoints(points: Vector2[]): BoundingInfo2D {
  52. let r = new BoundingInfo2D();
  53. BoundingInfo2D.CreateFromPointsToRef(points, r);
  54. return r;
  55. }
  56. /**
  57. * Update a BoundingInfo2D object using the given Size as input
  58. * @param size the bounding data will be computed from this size.
  59. * @param b must be a valid/allocated object, it will contain the result of the operation
  60. */
  61. public static CreateFromSizeToRef(size: Size, b: BoundingInfo2D) {
  62. if (!size) {
  63. size = Size.Zero();
  64. }
  65. b.center.x = +size.width / 2;
  66. b.center.y = +size.height / 2;
  67. b.extent.x = b.center.x;
  68. b.extent.y = b.center.y;
  69. b.radius = b.extent.length();
  70. b._worldAABBDirty = true;
  71. }
  72. /**
  73. * Update a BoundingInfo2D object using the given radius as input
  74. * @param radius the bounding data will be computed from this radius
  75. * @param b must be a valid/allocated object, it will contain the result of the operation
  76. */
  77. public static CreateFromRadiusToRef(radius: number, b: BoundingInfo2D) {
  78. b.center.x = b.center.y = 0;
  79. let r = +radius;
  80. b.extent.x = r;
  81. b.extent.y = r;
  82. b.radius = r;
  83. b._worldAABBDirty = true;
  84. }
  85. /**
  86. * Update a BoundingInfo2D object using the given points array as input
  87. * @param points the point array to use to update the bounding data
  88. * @param b must be a valid/allocated object, it will contain the result of the operation
  89. */
  90. public static CreateFromPointsToRef(points: Vector2[], b: BoundingInfo2D) {
  91. let xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
  92. for (let p of points) {
  93. xmin = Math.min(p.x, xmin);
  94. xmax = Math.max(p.x, xmax);
  95. ymin = Math.min(p.y, ymin);
  96. ymax = Math.max(p.y, ymax);
  97. }
  98. BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, b);
  99. }
  100. /**
  101. * Update a BoundingInfo2D object using the given min/max values as input
  102. * @param xmin the smallest x coordinate
  103. * @param xmax the biggest x coordinate
  104. * @param ymin the smallest y coordinate
  105. * @param ymax the buggest y coordinate
  106. * @param b must be a valid/allocated object, it will contain the result of the operation
  107. */
  108. public static CreateFromMinMaxToRef(xmin: number, xmax: number, ymin: number, ymax: number, b: BoundingInfo2D) {
  109. let w = xmax - xmin;
  110. let h = ymax - ymin;
  111. b.center = new Vector2(xmin + w / 2, ymin + h / 2);
  112. b.extent = new Vector2(xmax - b.center.x, ymax - b.center.y);
  113. b.radius = b.extent.length();
  114. b._worldAABBDirty = true;
  115. }
  116. /**
  117. * Duplicate this instance and return a new one
  118. * @return the duplicated instance
  119. */
  120. public clone(): BoundingInfo2D {
  121. let r = new BoundingInfo2D();
  122. r.center = this.center.clone();
  123. r.radius = this.radius;
  124. r.extent = this.extent.clone();
  125. return r;
  126. }
  127. public clear() {
  128. this.center.copyFromFloats(0, 0);
  129. this.radius = 0;
  130. this.extent.copyFromFloats(0, 0);
  131. this._worldAABBDirty = true;
  132. }
  133. public copyFrom(src: BoundingInfo2D) {
  134. this.center.copyFrom(src.center);
  135. this.radius = src.radius;
  136. this.extent.copyFrom(src.extent);
  137. this._worldAABBDirty = true;
  138. }
  139. public equals(other: BoundingInfo2D): boolean {
  140. if (!other) {
  141. return false;
  142. }
  143. return other.center.equals(this.center) && other.extent.equals(this.extent);
  144. }
  145. /**
  146. * return the max extend of the bounding info
  147. */
  148. public max(): Vector2 {
  149. let r = Vector2.Zero();
  150. this.maxToRef(r);
  151. return r;
  152. }
  153. /**
  154. * return the min/max extend of the bounding info.
  155. * x, y, z, w are left, bottom, right and top
  156. */
  157. public minMax(): Vector4 {
  158. let r = Vector4.Zero();
  159. this.minMaxToRef(r);
  160. return r;
  161. }
  162. /**
  163. * Update a vector2 with the max extend of the bounding info
  164. * @param result must be a valid/allocated vector2 that will contain the result of the operation
  165. */
  166. public maxToRef(result: Vector2) {
  167. result.x = this.center.x + this.extent.x;
  168. result.y = this.center.y + this.extent.y;
  169. }
  170. /**
  171. * Update a vector4 with the min/max extend of the bounding info
  172. * x, y, z, w are left, bottom, right and top
  173. * @param result must be a valid/allocated vector4 that will contain the result of the operation
  174. */
  175. public minMaxToRef(result: Vector4) {
  176. result.x = this.center.x - this.extent.x;
  177. result.y = this.center.y - this.extent.y;
  178. result.z = this.center.x + this.extent.x;
  179. result.w = this.center.y + this.extent.y;
  180. }
  181. /**
  182. * Return the size of the boundingInfo rect surface
  183. */
  184. public size(): Size {
  185. let r = Size.Zero();
  186. this.sizeToRef(r);
  187. return r;
  188. }
  189. /**
  190. * Stores in the result object the size of the boundingInfo rect surface
  191. * @param result
  192. */
  193. public sizeToRef(result: Size) {
  194. result.width = this.extent.x * 2;
  195. result.height = this.extent.y * 2;
  196. }
  197. /**
  198. * Inflate the boundingInfo with the given vector
  199. * @param offset the extent will be incremented with offset and the radius will be computed again
  200. */
  201. public inflate(offset: Vector2) {
  202. this.extent.addInPlace(offset);
  203. this.radius = this.extent.length();
  204. }
  205. /**
  206. * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
  207. * @param matrix the transformation matrix to apply
  208. * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
  209. */
  210. public transform(matrix: Matrix): BoundingInfo2D {
  211. var r = new BoundingInfo2D();
  212. this.transformToRef(matrix, r);
  213. return r;
  214. }
  215. /**
  216. * Compute the union of this BoundingInfo2D with a given one, returns a new BoundingInfo2D as a result
  217. * @param other the second BoundingInfo2D to compute the union with this one
  218. * @return a new instance containing the result of the union
  219. */
  220. public union(other: BoundingInfo2D): BoundingInfo2D {
  221. var r = new BoundingInfo2D();
  222. this.unionToRef(other, r);
  223. return r;
  224. }
  225. public worldAABBIntersectionTest(other: BoundingInfo2D): boolean {
  226. let a = this.worldAABB;
  227. let b = other.worldAABB;
  228. return b.z >= a.x && b.x <= a.z && b.w >= a.y && b.y <= a.w;
  229. }
  230. private static _transform: Array<Vector2> = new Array<Vector2>(Vector2.Zero(), Vector2.Zero(), Vector2.Zero(), Vector2.Zero());
  231. /**
  232. * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
  233. * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
  234. * @param matrix The matrix to use to compute the transformation
  235. * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
  236. */
  237. public transformToRef(matrix: Matrix, result: BoundingInfo2D) {
  238. // Construct a bounding box based on the extent values
  239. let p = BoundingInfo2D._transform;
  240. p[0].x = this.center.x + this.extent.x;
  241. p[0].y = this.center.y + this.extent.y;
  242. p[1].x = this.center.x + this.extent.x;
  243. p[1].y = this.center.y - this.extent.y;
  244. p[2].x = this.center.x - this.extent.x;
  245. p[2].y = this.center.y - this.extent.y;
  246. p[3].x = this.center.x - this.extent.x;
  247. p[3].y = this.center.y + this.extent.y;
  248. // Transform the four points of the bounding box with the matrix
  249. for (let i = 0; i < 4; i++) {
  250. Vector2.TransformToRef(p[i], matrix, p[i]);
  251. }
  252. BoundingInfo2D.CreateFromPointsToRef(p, result);
  253. }
  254. private _updateWorldAABB(worldMatrix: Matrix) {
  255. // Construct a bounding box based on the extent values
  256. let p = BoundingInfo2D._transform;
  257. p[0].x = this.center.x + this.extent.x;
  258. p[0].y = this.center.y + this.extent.y;
  259. p[1].x = this.center.x + this.extent.x;
  260. p[1].y = this.center.y - this.extent.y;
  261. p[2].x = this.center.x - this.extent.x;
  262. p[2].y = this.center.y - this.extent.y;
  263. p[3].x = this.center.x - this.extent.x;
  264. p[3].y = this.center.y + this.extent.y;
  265. // Transform the four points of the bounding box with the matrix
  266. for (let i = 0; i < 4; i++) {
  267. Vector2.TransformToRef(p[i], worldMatrix, p[i]);
  268. }
  269. this._worldAABB.x = Math.min(Math.min(p[0].x, p[1].x), Math.min(p[2].x, p[3].x));
  270. this._worldAABB.y = Math.min(Math.min(p[0].y, p[1].y), Math.min(p[2].y, p[3].y));
  271. this._worldAABB.z = Math.max(Math.max(p[0].x, p[1].x), Math.max(p[2].x, p[3].x));
  272. this._worldAABB.w = Math.max(Math.max(p[0].y, p[1].y), Math.max(p[2].y, p[3].y));
  273. }
  274. public worldMatrixAccess: () => Matrix;
  275. public get worldAABBDirtyObservable(): Observable<BoundingInfo2D> {
  276. if (!this._worldAABBDirtyObservable) {
  277. this._worldAABBDirtyObservable = new Observable<BoundingInfo2D>();
  278. }
  279. return this._worldAABBDirtyObservable;
  280. }
  281. public get isWorldAABBDirty() {
  282. return this._worldAABBDirty;
  283. }
  284. public dirtyWorldAABB() {
  285. if (this._worldAABBDirty) {
  286. return;
  287. }
  288. this._worldAABBDirty = true;
  289. if (this._worldAABBDirtyObservable && this._worldAABBDirtyObservable.hasObservers()) {
  290. this._worldAABBDirtyObservable.notifyObservers(this);
  291. }
  292. }
  293. /**
  294. * Retrieve the world AABB, the Vector4's data is x=xmin, y=ymin, z=xmax, w=ymax
  295. */
  296. public get worldAABB(): Vector4 {
  297. if (this._worldAABBDirty) {
  298. if (!this.worldMatrixAccess) {
  299. throw new Error("you must set the worldMatrixAccess function first");
  300. }
  301. this._updateWorldAABB(this.worldMatrixAccess());
  302. this._worldAABBDirty = false;
  303. }
  304. return this._worldAABB;
  305. }
  306. /**
  307. * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
  308. * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
  309. * @param other the second object used to compute the union
  310. * @param result a VALID BoundingInfo2D instance (i.e. allocated) where the result will be stored
  311. */
  312. public unionToRef(other: BoundingInfo2D, result: BoundingInfo2D) {
  313. let xmax = Math.max(this.center.x + this.extent.x, other.center.x + other.extent.x);
  314. let ymax = Math.max(this.center.y + this.extent.y, other.center.y + other.extent.y);
  315. let xmin = Math.min(this.center.x - this.extent.x, other.center.x - other.extent.x);
  316. let ymin = Math.min(this.center.y - this.extent.y, other.center.y - other.extent.y);
  317. BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, result);
  318. }
  319. /**
  320. * Check if the given point is inside the BoundingInfo.
  321. * The test is first made on the radius, then inside the rectangle described by the extent
  322. * @param pickPosition the position to test
  323. * @return true if the point is inside, false otherwise
  324. */
  325. public doesIntersect(pickPosition: Vector2): boolean {
  326. // is it inside the radius?
  327. let pickLocal = pickPosition.subtract(this.center);
  328. if (pickLocal.lengthSquared() <= (this.radius * this.radius)) {
  329. // is it inside the rectangle?
  330. return ((Math.abs(pickLocal.x) <= this.extent.x) && (Math.abs(pickLocal.y) <= this.extent.y));
  331. }
  332. return false;
  333. }
  334. private _worldAABBDirtyObservable: Observable<BoundingInfo2D>;
  335. private _worldAABBDirty: boolean;
  336. private _worldAABB: Vector4;
  337. }
  338. }