babylon.math2D.ts 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. module BABYLON {
  2. /**
  3. * This class stores the data to make 2D transformation using a Translation (tX, tY), a Scale (sX, sY) and a rotation around the Z axis (rZ).
  4. * You can multiply two Transform2D object to produce the result of their concatenation.
  5. * You can transform a given Point (a Vector2D instance) with a Transform2D object or with the Invert of the Transform2D object.
  6. * There no need to compute/store the Invert of a Transform2D as the invertTranform methods are almost as fast as the transform ones.
  7. * This class is as light as it could be and the transformation operations are pretty optimal.
  8. */
  9. export class Transform2D {
  10. /**
  11. * A 2D Vector representing the translation to the origin
  12. */
  13. public translation: Vector2;
  14. /**
  15. * A number (in radian) representing the rotation around the Z axis at the origin
  16. */
  17. public rotation: number;
  18. /**
  19. * A 2D Vector representing the scale to apply at the origin
  20. */
  21. public scale: Vector2;
  22. constructor() {
  23. this.translation = Vector2.Zero();
  24. this.rotation = 0;
  25. this.scale = new Vector2(1, 1);
  26. }
  27. /**
  28. * Set the Transform2D object with the given values
  29. * @param translation The translation to set
  30. * @param rotation The rotation (in radian) to set
  31. * @param scale The scale to set
  32. */
  33. public set(translation: Vector2, rotation: number, scale: Vector2) {
  34. this.translation.copyFrom(translation);
  35. this.rotation = rotation;
  36. this.scale.copyFrom(scale);
  37. }
  38. /**
  39. * Set the Transform2D object from float values
  40. * @param transX The translation on X axis, nothing is set if not specified
  41. * @param transY The translation on Y axis, nothing is set if not specified
  42. * @param rotation The rotation in radian, nothing is set if not specified
  43. * @param scaleX The scale along the X axis, nothing is set if not specified
  44. * @param scaleY The scale along the Y axis, nothing is set if not specified
  45. */
  46. public setFromFloats(transX?: number, transY?: number, rotation?: number, scaleX?: number, scaleY?: number) {
  47. if (transX) {
  48. this.translation.x = transX;
  49. }
  50. if (transY) {
  51. this.translation.y = transY;
  52. }
  53. if (rotation) {
  54. this.rotation = rotation;
  55. }
  56. if (scaleX) {
  57. this.scale.x = scaleX;
  58. }
  59. if (scaleY) {
  60. this.scale.y = scaleY;
  61. }
  62. }
  63. /**
  64. * Return a copy of the object
  65. */
  66. public clone(): Transform2D {
  67. let res = new Transform2D();
  68. res.translation.copyFrom(this.translation);
  69. res.rotation = this.rotation;
  70. res.scale.copyFrom(this.scale);
  71. return res;
  72. }
  73. /**
  74. * Convert a given degree angle into its radian equivalent
  75. * @param angleDegree the number to convert
  76. */
  77. public static ToRadian(angleDegree: number): number {
  78. return angleDegree * Math.PI * 2 / 360;
  79. }
  80. /**
  81. * Create a new instance and returns it
  82. * @param translation The translation to store, default is (0,0)
  83. * @param rotation The rotation to store, default is 0
  84. * @param scale The scale to store, default is (1,1)
  85. */
  86. public static Make(translation?: Vector2, rotation?: number, scale?: Vector2): Transform2D {
  87. let res = new Transform2D();
  88. if (translation) {
  89. res.translation.copyFrom(translation);
  90. }
  91. if (rotation) {
  92. res.rotation = rotation;
  93. }
  94. if (scale) {
  95. res.scale.copyFrom(scale);
  96. }
  97. return res;
  98. }
  99. /**
  100. * Set the given Transform2D object with the given values
  101. * @param translation The translation to store, default is (0,0)
  102. * @param rotation The rotation to store, default is 0
  103. * @param scale The scale to store, default is (1,1)
  104. */
  105. public static MakeToRef(res: Transform2D, translation?: Vector2, rotation?: number, scale?: Vector2) {
  106. if (translation) {
  107. res.translation.copyFrom(translation);
  108. } else {
  109. res.translation.copyFromFloats(0, 0);
  110. }
  111. if (rotation) {
  112. res.rotation = rotation;
  113. } else {
  114. res.rotation = 0;
  115. }
  116. if (scale) {
  117. res.scale.copyFrom(scale);
  118. } else {
  119. res.scale.copyFromFloats(1, 1);
  120. }
  121. }
  122. /**
  123. * Create a Transform2D object from float values
  124. * @param transX The translation on X axis, 0 per default
  125. * @param transY The translation on Y axis, 0 per default
  126. * @param rotation The rotation in radian, 0 per default
  127. * @param scaleX The scale along the X axis, 1 per default
  128. * @param scaleY The scale along the Y axis, 1 per default
  129. */
  130. public static MakeFromFloats(transX?: number, transY?: number, rotation?: number, scaleX?: number, scaleY?: number): Transform2D {
  131. let res = new Transform2D();
  132. if (transX) {
  133. res.translation.x = transX;
  134. }
  135. if (transY) {
  136. res.translation.y = transY;
  137. }
  138. if (rotation) {
  139. res.rotation = rotation;
  140. }
  141. if (scaleX) {
  142. res.scale.x = scaleX;
  143. }
  144. if (scaleY) {
  145. res.scale.y = scaleY;
  146. }
  147. return res;
  148. }
  149. /**
  150. * Set the given Transform2D object with the given float values
  151. * @param transX The translation on X axis, 0 per default
  152. * @param transY The translation on Y axis, 0 per default
  153. * @param rotation The rotation in radian, 0 per default
  154. * @param scaleX The scale along the X axis, 1 per default
  155. * @param scaleY The scale along the Y axis, 1 per default
  156. */
  157. public static MakeFromFloatsToRef(res: Transform2D, transX?: number, transY?: number, rotation?: number, scaleX?: number, scaleY?: number) {
  158. res.translation.x = (transX!=null) ? transX : 0;
  159. res.translation.y = (transY!=null) ? transY : 0;
  160. res.rotation = (rotation!=null) ? rotation : 0;
  161. res.scale.x = (scaleX!=null) ? scaleX : 1;
  162. res.scale.y = (scaleY!=null) ? scaleY : 1;
  163. }
  164. /**
  165. * Create a Transform2D containing only Zeroed values
  166. */
  167. public static Zero(): Transform2D {
  168. let res = new Transform2D();
  169. res.scale.copyFromFloats(0, 0);
  170. return res;
  171. }
  172. /**
  173. * Copy the value of the other object into 'this'
  174. * @param other The other object to copy values from
  175. */
  176. public copyFrom(other: Transform2D) {
  177. this.translation.copyFrom(other.translation);
  178. this.rotation = other.rotation;
  179. this.scale.copyFrom(other.scale);
  180. }
  181. public toMatrix2D(): Matrix2D {
  182. let res = new Matrix2D();
  183. this.toMatrix2DToRef(res);
  184. return res;
  185. }
  186. public toMatrix2DToRef(res: Matrix2D) {
  187. let tx = this.translation.x;
  188. let ty = this.translation.y;
  189. let r = this.rotation;
  190. let cosr = Math.cos(r);
  191. let sinr = Math.sin(r);
  192. let sx = this.scale.x;
  193. let sy = this.scale.y;
  194. res.m[0] = cosr * sx; res.m[1] = sinr * sy;
  195. res.m[2] = -sinr* sx; res.m[3] = cosr * sy;
  196. res.m[4] = tx; res.m[5] = ty;
  197. }
  198. /**
  199. * In place transformation from a parent matrix.
  200. * @param parent transform object. "this" will be the result of parent * this
  201. */
  202. public multiplyToThis(parent: Transform2D) {
  203. this.multiplyToRef(parent, this);
  204. }
  205. /**
  206. * Transform this object with a parent and return the result. Result = parent * this
  207. * @param parent The parent transformation
  208. */
  209. public multiply(parent: Transform2D): Transform2D {
  210. let res = new Transform2D();
  211. this.multiplyToRef(parent, res);
  212. return res;
  213. }
  214. /**
  215. * Transform a point and store the result in the very same object
  216. * @param p Transform this point and change the values with the transformed ones
  217. */
  218. public transformPointInPlace(p: Vector2) {
  219. this.transformPointToRef(p, p);
  220. }
  221. /**
  222. * Transform a point and store the result into a reference object
  223. * @param p The point to transform
  224. * @param res Will contain the new transformed coordinates. Can be the object of 'p'.
  225. */
  226. public transformPointToRef(p: Vector2, res: Vector2) {
  227. this.transformFloatsToRef(p.x, p.y, res);
  228. }
  229. /**
  230. * Transform this object with a parent and store the result in reference object
  231. * @param parent The parent transformation
  232. * @param result Will contain parent * this. Can be the object of either parent or 'this'
  233. */
  234. public multiplyToRef(parent: Transform2D, result: Transform2D) {
  235. if (!parent || !result) {
  236. throw new Error("Valid parent and result objects must be specified");
  237. }
  238. let tx = this.translation.x;
  239. let ty = this.translation.y;
  240. let ptx = parent.translation.x;
  241. let pty = parent.translation.y;
  242. let pr = parent.rotation;
  243. let psx = parent.scale.x;
  244. let psy = parent.scale.y;
  245. let cosr = Math.cos(pr);
  246. let sinr = Math.sin(pr);
  247. result.translation.x = (((tx * cosr) - (ty * sinr)) * psx) + ptx;
  248. result.translation.y = (((tx * sinr) + (ty * cosr)) * psy) + pty;
  249. this.scale.multiplyToRef(parent.scale, result.scale);
  250. result.rotation = this.rotation;
  251. }
  252. /**
  253. * Transform the given coordinates and store the result in a Vector2 object
  254. * @param x The X coordinate to transform
  255. * @param y The Y coordinate to transform
  256. * @param res The Vector2 object that will contain the result of the transformation
  257. */
  258. public transformFloatsToRef(x: number, y: number, res: Vector2) {
  259. let tx = this.translation.x;
  260. let ty = this.translation.y;
  261. let pr = this.rotation;
  262. let sx = this.scale.x;
  263. let sy = this.scale.y;
  264. let cosr = Math.cos(pr);
  265. let sinr = Math.sin(pr);
  266. res.x = (((x * cosr) - (y * sinr)) * sx) + tx;
  267. res.y = (((x * sinr) + (y * cosr)) * sy) + ty;
  268. }
  269. /**
  270. * Invert transform the given coordinates and store the result in a reference object. res = invert(this) * (x,y)
  271. * @param p Transform this point and change the values with the transformed ones
  272. * @param res Will contain the result of the invert transformation.
  273. */
  274. public invertTransformFloatsToRef(x: number, y: number, res: Vector2) {
  275. let px = x - this.translation.x;
  276. let py = y - this.translation.y;
  277. let pr = -this.rotation;
  278. let sx = this.scale.x;
  279. let sy = this.scale.y;
  280. let psx = (sx===1) ? 1 : (1/sx);
  281. let psy = (sy===1) ? 1 : (1/sy);
  282. let cosr = Math.cos(pr);
  283. let sinr = Math.sin(pr);
  284. res.x = (((px * cosr) - (py * sinr)) * psx);
  285. res.y = (((px * sinr) + (py * cosr)) * psy);
  286. }
  287. /**
  288. * Transform a point and return the result
  289. * @param p the point to transform
  290. */
  291. public transformPoint(p: Vector2): Vector2 {
  292. let res = Vector2.Zero();
  293. this.transformPointToRef(p, res);
  294. return res;
  295. }
  296. /**
  297. * Transform the given coordinates and return the result in a Vector2 object
  298. * @param x The X coordinate to transform
  299. * @param y The Y coordinate to transform
  300. */
  301. public transformFloats(x: number, y: number): Vector2 {
  302. let res = Vector2.Zero();
  303. this.transformFloatsToRef(x, y, res);
  304. return res;
  305. }
  306. /**
  307. * Invert transform a given point and store the result in the very same object. p = invert(this) * p
  308. * @param p Transform this point and change the values with the transformed ones
  309. */
  310. public invertTransformPointInPlace(p: Vector2) {
  311. this.invertTransformPointToRef(p, p);
  312. }
  313. /**
  314. * Invert transform a given point and store the result in a reference object. res = invert(this) * p
  315. * @param p Transform this point and change the values with the transformed ones
  316. * @param res Will contain the result of the invert transformation. 'res' can be the same object as 'p'
  317. */
  318. public invertTransformPointToRef(p: Vector2, res: Vector2) {
  319. this.invertTransformFloatsToRef(p.x, p.y, res);
  320. }
  321. /**
  322. * Invert transform a given point and return the result. return = invert(this) * p
  323. * @param p The Point to transform
  324. */
  325. public invertTransformPoint(p: Vector2): Vector2 {
  326. let res = Vector2.Zero();
  327. this.invertTransformPointToRef(p, res);
  328. return res;
  329. }
  330. /**
  331. * Invert transform the given coordinates and return the result. return = invert(this) * (x,y)
  332. * @param x The X coordinate to transform
  333. * @param y The Y coordinate to transform
  334. */
  335. public invertTransformFloats(x: number, y: number): Vector2 {
  336. let res = Vector2.Zero();
  337. this.invertTransformFloatsToRef(x, y, res);
  338. return res;
  339. }
  340. }
  341. /**
  342. * A class storing a Matrix for 2D transformations
  343. * The stored matrix is a 2*3 Matrix
  344. * I [0,1] [mX, mY] R [ CosZ, SinZ] T [ 0, 0] S [Sx, 0]
  345. * D = [2,3] = [nX, nY] O = [-SinZ, CosZ] R = [ 0, 0] C = [ 0, Sy]
  346. * X [4,5] [tX, tY] T [ 0 , 0 ] N [Tx, Ty] L [ 0, 0]
  347. *
  348. * IDX = index, zero based. ROT = Z axis Rotation. TRN = Translation. SCL = Scale.
  349. */
  350. export class Matrix2D {
  351. public static Identity(): Matrix2D {
  352. let res = new Matrix2D();
  353. Matrix2D.IdentityToRef(res);
  354. return res;
  355. }
  356. public static IdentityToRef(res: Matrix2D) {
  357. res.m[1] = res.m[2] = res.m[4] = res.m[5] = 0;
  358. res.m[0] = res.m[3] = 1;
  359. }
  360. public copyFrom(other: Matrix2D) {
  361. for (let i = 0; i < 6; i++) {
  362. this.m[i] = other.m[i];
  363. }
  364. }
  365. public determinant(): number {
  366. return (this.m[0] * this.m[3]) - (this.m[1] * this.m[2]);
  367. }
  368. public invertToThis() {
  369. this.invertToRef(this);
  370. }
  371. public invert(): Matrix2D {
  372. let res = new Matrix2D();
  373. this.invertToRef(res);
  374. return res;
  375. }
  376. public invertToRef(res: Matrix2D) {
  377. let a00 = this.m[0], a01 = this.m[1],
  378. a10 = this.m[2], a11 = this.m[3],
  379. a20 = this.m[4], a21 = this.m[5];
  380. let det21 = a21 * a10 - a11 * a20;
  381. let det = (a00 * a11) - (a01 * a10);
  382. if (det < (Epsilon*Epsilon)) {
  383. throw new Error("Can't invert matrix, near null determinant");
  384. }
  385. det = 1 / det;
  386. res.m[0] = a11 * det;
  387. res.m[1] = -a01 * det;
  388. res.m[2] = -a10 * det;
  389. res.m[3] = a00 * det;
  390. res.m[4] = det21 * det;
  391. res.m[5] = (-a21 * a00 + a01 * a20) * det;
  392. }
  393. public multiplyToThis(other: Matrix2D) {
  394. this.multiplyToRef(other, this);
  395. }
  396. public multiply(other: Matrix2D): Matrix2D {
  397. let res = new Matrix2D();
  398. this.multiplyToRef(other, res);
  399. return res;
  400. }
  401. public multiplyToRef(other: Matrix2D, result: Matrix2D) {
  402. var tm0 = this.m[0];
  403. var tm1 = this.m[1];
  404. //var tm2 = this.m[2];
  405. //var tm3 = this.m[3];
  406. var tm4 = this.m[2];
  407. var tm5 = this.m[3];
  408. //var tm6 = this.m[6];
  409. //var tm7 = this.m[7];
  410. var tm8 = this.m[4];
  411. var tm9 = this.m[5];
  412. //var tm10 = this.m[10];
  413. //var tm11 = this.m[11];
  414. //var tm12 = this.m[12];
  415. //var tm13 = this.m[13];
  416. //var tm14 = this.m[14];
  417. //var tm15 = this.m[15];
  418. var om0 = other.m[0];
  419. var om1 = other.m[1];
  420. //var om2 = other.m[2];
  421. //var om3 = other.m[3];
  422. var om4 = other.m[2];
  423. var om5 = other.m[3];
  424. //var om6 = other.m[6];
  425. //var om7 = other.m[7];
  426. var om8 = other.m[4];
  427. var om9 = other.m[5];
  428. //var om10 = other.m[10];
  429. //var om11 = other.m[11];
  430. //var om12 = other.m[12];
  431. //var om13 = other.m[13];
  432. //var om14 = other.m[14];
  433. //var om15 = other.m[15];
  434. result.m[0] = tm0 * om0 + tm1 * om4;
  435. result.m[1] = tm0 * om1 + tm1 * om5;
  436. //result.m[2] = tm0 * om2 + tm1 * om6 + tm2 * om10 + tm3 * om14;
  437. //result.m[3] = tm0 * om3 + tm1 * om7 + tm2 * om11 + tm3 * om15;
  438. result.m[2] = tm4 * om0 + tm5 * om4;
  439. result.m[3] = tm4 * om1 + tm5 * om5;
  440. //result.m[6] = tm4 * om2 + tm5 * om6 + tm6 * om10 + tm7 * om14;
  441. //result.m[7] = tm4 * om3 + tm5 * om7 + tm6 * om11 + tm7 * om15;
  442. result.m[4] = tm8 * om0 + tm9 * om4 + om8;
  443. result.m[5] = tm8 * om1 + tm9 * om5 + om9;
  444. //result.m[10] = tm8 * om2 + tm9 * om6 + tm10 * om10 + tm11 * om14;
  445. //result.m[11] = tm8 * om3 + tm9 * om7 + tm10 * om11 + tm11 * om15;
  446. //result.m[12] = tm12 * om0 + tm13 * om4 + tm14 * om8 + tm15 * om12;
  447. //result.m[13] = tm12 * om1 + tm13 * om5 + tm14 * om9 + tm15 * om13;
  448. //result.m[14] = tm12 * om2 + tm13 * om6 + tm14 * om10 + tm15 * om14;
  449. //result.m[15] = tm12 * om3 + tm13 * om7 + tm14 * om11 + tm15 * om15;
  450. }
  451. public transformFloats(x: number, y: number): Vector2 {
  452. let res = Vector2.Zero();
  453. this.transformFloatsToRef(x, y, res);
  454. return res;
  455. }
  456. public transformFloatsToRef(x: number, y: number, r: Vector2) {
  457. r.x = x * this.m[0] + y * this.m[2] + this.m[4];
  458. r.y = x * this.m[1] + y * this.m[3] + this.m[5];
  459. }
  460. public transformPoint(p: Vector2): Vector2 {
  461. let res = Vector2.Zero();
  462. this.transformFloatsToRef(p.x, p.y, res);
  463. return res;
  464. }
  465. public transformPointToRef(p: Vector2, r: Vector2) {
  466. this.transformFloatsToRef(p.x, p.y, r);
  467. }
  468. public m = new Float32Array(6);
  469. }
  470. /**
  471. * Stores information about a 2D Triangle.
  472. * This class stores the 3 vertices but also the center and radius of the triangle
  473. */
  474. export class Tri2DInfo {
  475. /**
  476. * Construct an instance of Tri2DInfo, you can either pass null to a, b and c and the instance will be allocated "clear", or give actual triangle info and the center/radius will be computed
  477. */
  478. constructor(a: Vector2, b: Vector2, c: Vector2) {
  479. if (a === null && b === null && c === null) {
  480. this.a = Vector2.Zero();
  481. this.b = Vector2.Zero();
  482. this.c = Vector2.Zero();
  483. this.center = Vector2.Zero();
  484. this.radius = 0;
  485. return;
  486. }
  487. this.a = a.clone();
  488. this.b = b.clone();
  489. this.c = c.clone();
  490. this._updateCenterRadius();
  491. }
  492. a: Vector2;
  493. b: Vector2;
  494. c: Vector2;
  495. center: Vector2;
  496. radius: number;
  497. public static Zero() {
  498. return new Tri2DInfo(null, null, null);
  499. }
  500. public set(a: Vector2, b: Vector2, c: Vector2) {
  501. this.a.copyFrom(a);
  502. this.b.copyFrom(b);
  503. this.c.copyFrom(c);
  504. this._updateCenterRadius();
  505. }
  506. public transformInPlace(transform: Matrix) {
  507. Vector2.TransformToRef(this.a, transform, this.a);
  508. Vector2.TransformToRef(this.b, transform, this.b);
  509. Vector2.TransformToRef(this.c, transform, this.c);
  510. this._updateCenterRadius();
  511. }
  512. public doesContain(p: Vector2): boolean {
  513. return Vector2.PointInTriangle(p, this.a, this.b, this.c);
  514. }
  515. private _updateCenterRadius() {
  516. this.center.x = (this.a.x + this.b.x + this.c.x) / 3;
  517. this.center.y = (this.a.y + this.b.y + this.c.y) / 3;
  518. let la = Vector2.DistanceSquared(this.a, this.center);
  519. let lb = Vector2.DistanceSquared(this.b, this.center);
  520. let lc = Vector2.DistanceSquared(this.c, this.center);
  521. let rs = Math.max(Math.max(la, lb), lc);
  522. this.radius = Math.sqrt(rs);
  523. }
  524. }
  525. /**
  526. * Stores an array of 2D Triangles.
  527. * Internally the data is stored as a Float32Array to minimize the memory footprint.
  528. * This can use the Tri2DInfo class as proxy for storing/retrieving data.
  529. * The array can't grow, it's fixed size.
  530. */
  531. export class Tri2DArray {
  532. constructor(count: number) {
  533. this._count = count;
  534. this._array = new Float32Array(9 * count);
  535. }
  536. /**
  537. * Clear the content and allocate a new array to store the given count of triangles
  538. * @param count The new count of triangles to store
  539. */
  540. public clear(count: number) {
  541. if (this._count === count) {
  542. return;
  543. }
  544. this._count = count;
  545. this._array = new Float32Array(9 * count);
  546. }
  547. /**
  548. * Store a given triangle at the given index
  549. * @param index the 0 based index to store the triangle in the array
  550. * @param a the A vertex of the triangle
  551. * @param b the B vertex of the triangle
  552. * @param c the C vertex of the triangle
  553. */
  554. public storeTriangle(index: number, a: Vector2, b: Vector2, c: Vector2) {
  555. let center = new Vector2((a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3);
  556. let la = Vector2.DistanceSquared(a, center);
  557. let lb = Vector2.DistanceSquared(b, center);
  558. let lc = Vector2.DistanceSquared(c, center);
  559. let rs = Math.max(Math.max(la, lb), lc);
  560. let radius = Math.sqrt(rs);
  561. let offset = index * 9;
  562. this._array[offset + 0] = a.x;
  563. this._array[offset + 1] = a.y;
  564. this._array[offset + 2] = b.x;
  565. this._array[offset + 3] = b.y;
  566. this._array[offset + 4] = c.x;
  567. this._array[offset + 5] = c.y;
  568. this._array[offset + 6] = center.x;
  569. this._array[offset + 7] = center.y;
  570. this._array[offset + 8] = radius;
  571. }
  572. /**
  573. * Store a triangle in a Tri2DInfo object
  574. * @param index the index of the triangle to store
  575. * @param tri2dInfo the instance that will contain the data, it must be already allocated with its inner object also allocated
  576. */
  577. public storeToTri2DInfo(index: number, tri2dInfo: Tri2DInfo) {
  578. if (index >= this._count) {
  579. throw new Error(`Can't fetch the triangle at index ${index}, max index is ${this._count - 1}`);
  580. }
  581. let offset = index * 9;
  582. tri2dInfo.a.x = this._array[offset + 0];
  583. tri2dInfo.a.y = this._array[offset + 1];
  584. tri2dInfo.b.x = this._array[offset + 2];
  585. tri2dInfo.b.y = this._array[offset + 3];
  586. tri2dInfo.c.x = this._array[offset + 4];
  587. tri2dInfo.c.y = this._array[offset + 5];
  588. tri2dInfo.center.x = this._array[offset + 6];
  589. tri2dInfo.center.y = this._array[offset + 7];
  590. tri2dInfo.radius = this._array[offset + 8];
  591. }
  592. /**
  593. * Transform the given triangle and store its result in the array
  594. * @param index The index to store the result to
  595. * @param tri2dInfo The triangle to transform
  596. * @param transform The transformation matrix
  597. */
  598. public transformAndStoreToTri2DInfo(index: number, tri2dInfo: Tri2DInfo, transform: Matrix) {
  599. if (index >= this._count) {
  600. throw new Error(`Can't fetch the triangle at index ${index}, max index is ${this._count - 1}`);
  601. }
  602. let offset = index * 9;
  603. tri2dInfo.a.x = this._array[offset + 0];
  604. tri2dInfo.a.y = this._array[offset + 1];
  605. tri2dInfo.b.x = this._array[offset + 2];
  606. tri2dInfo.b.y = this._array[offset + 3];
  607. tri2dInfo.c.x = this._array[offset + 4];
  608. tri2dInfo.c.y = this._array[offset + 5];
  609. tri2dInfo.transformInPlace(transform);
  610. }
  611. /**
  612. * Get the element count that can be stored in this array
  613. * @returns {}
  614. */
  615. public get count(): number {
  616. return this._count;
  617. }
  618. /**
  619. * Check if a given point intersects with at least one of the triangles stored in the array.
  620. * If true is returned the point is intersecting with at least one triangle, false if it doesn't intersect with any of them
  621. * @param p The point to check
  622. */
  623. public doesContain(p: Vector2): boolean {
  624. Tri2DArray._checkInitStatics();
  625. let a = Tri2DArray.tempT[0];
  626. for (let i = 0; i < this.count; i++) {
  627. this.storeToTri2DInfo(i, a);
  628. if (a.doesContain(p)) {
  629. return true;
  630. }
  631. }
  632. return false;
  633. }
  634. /**
  635. * Make a intersection test between two sets of triangles. The triangles of setB will be transformed to the frame of reference of the setA using the given bToATransform matrix.
  636. * If true is returned at least one triangle intersects with another of the other set, otherwise false is returned.
  637. * @param setA The first set of triangles
  638. * @param setB The second set of triangles
  639. * @param bToATransform The transformation matrix to transform the setB triangles into the frame of reference of the setA
  640. */
  641. public static doesIntersect(setA: Tri2DArray, setB: Tri2DArray, bToATransform: Matrix): boolean {
  642. Tri2DArray._checkInitStatics();
  643. let a = Tri2DArray.tempT[0];
  644. let b = Tri2DArray.tempT[1];
  645. let v0 = Tri2DArray.tempV[0];
  646. for (let curB = 0; curB < setB.count; curB++) {
  647. setB.transformAndStoreToTri2DInfo(curB, b, bToATransform);
  648. for (let curA = 0; curA < setA.count; curA++) {
  649. setA.storeToTri2DInfo(curA, a);
  650. // Fast rejection first
  651. v0.x = a.center.x - b.center.x;
  652. v0.y = a.center.y - b.center.y;
  653. if (v0.lengthSquared() > ((a.radius * a.radius) + (b.radius * b.radius))) {
  654. continue;
  655. }
  656. // Actual intersection test
  657. if (Math2D.TriangleTriangleDosIntersect(a.a, a.b, a.c, b.a, b.b, b.c)) {
  658. return true;
  659. }
  660. }
  661. }
  662. return false;
  663. }
  664. private static _checkInitStatics() {
  665. if (Tri2DArray.tempT !== null) {
  666. return;
  667. }
  668. Tri2DArray.tempT = new Array<Tri2DInfo>(2);
  669. Tri2DArray.tempT[0] = new Tri2DInfo(null, null, null);
  670. Tri2DArray.tempT[1] = new Tri2DInfo(null, null, null);
  671. Tri2DArray.tempV = new Array<Vector2>(6);
  672. for (let i = 0; i < 6; i++) {
  673. Tri2DArray.tempV[i] = Vector2.Zero();
  674. }
  675. }
  676. private _count: number;
  677. private _array: Float32Array;
  678. private static tempV: Vector2[] = null;
  679. private static tempT: Tri2DInfo[] = null;
  680. }
  681. /**
  682. * Some 2D Math helpers functions
  683. */
  684. class Math2D {
  685. static Dot(a: Vector2, b: Vector2): number {
  686. return a.x * b.x + a.y * b.y;
  687. }
  688. // From http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
  689. // Note: this one might also be considered with the above one proves to not be good enough: http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
  690. static LineLineDoesIntersect(segA1: Vector2, segA2: Vector2, segB1: Vector2, segB2: Vector2): boolean {
  691. let s1_x = segA2.x - segA1.x; let s1_y = segA2.y - segA1.y;
  692. let s2_x = segB2.x - segB1.x; let s2_y = segB2.y - segB1.y;
  693. let s = (-s1_y * (segA1.x - segB1.x) + s1_x * (segA1.y - segB1.y)) / (-s2_x * s1_y + s1_x * s2_y);
  694. let t = ( s2_x * (segA1.y - segB1.y) - s2_y * (segA1.x - segB1.x)) / (-s2_x * s1_y + s1_x * s2_y);
  695. if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
  696. return true;
  697. }
  698. return false;
  699. }
  700. // From http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
  701. static LineLineIntersection(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2): { res: boolean, xr: number, yr: number } {
  702. let s1_x = p1.x - p0.x; let s1_y = p1.y - p0.y;
  703. let s2_x = p3.x - p2.x; let s2_y = p3.y - p2.y;
  704. let s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / (-s2_x * s1_y + s1_x * s2_y);
  705. let t = (s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / (-s2_x * s1_y + s1_x * s2_y);
  706. if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
  707. return { res: true, xr: p0.x + (t * s1_x), yr: p0.y + (t * s1_y) };
  708. }
  709. return { res: false, xr: 0, yr: 0 };
  710. }
  711. private static v0 = Vector2.Zero();
  712. private static v1 = Vector2.Zero();
  713. private static v2 = Vector2.Zero();
  714. // Tell me that it's slow and I'll answer: yes it is!
  715. // If you fancy to implement the SAT (Separating Axis Theorem) version: BE MY VERY WELCOMED GUEST!
  716. static TriangleTriangleDosIntersect(tri1A: Vector2, tri1B: Vector2, tri1C: Vector2, tri2A: Vector2, tri2B: Vector2, tri2C: Vector2): boolean {
  717. if (Math2D.LineLineDoesIntersect(tri1A, tri1B, tri2A, tri2B)) return true;
  718. if (Math2D.LineLineDoesIntersect(tri1A, tri1B, tri2A, tri2C)) return true;
  719. if (Math2D.LineLineDoesIntersect(tri1A, tri1B, tri2B, tri2C)) return true;
  720. if (Math2D.LineLineDoesIntersect(tri1A, tri1C, tri2A, tri2B)) return true;
  721. if (Math2D.LineLineDoesIntersect(tri1A, tri1C, tri2A, tri2C)) return true;
  722. if (Math2D.LineLineDoesIntersect(tri1A, tri1C, tri2B, tri2C)) return true;
  723. if (Math2D.LineLineDoesIntersect(tri1B, tri1C, tri2A, tri2B)) return true;
  724. if (Math2D.LineLineDoesIntersect(tri1B, tri1C, tri2A, tri2C)) return true;
  725. if (Math2D.LineLineDoesIntersect(tri1B, tri1C, tri2B, tri2C)) return true;
  726. if (Vector2.PointInTriangle(tri2A, tri1A, tri1B, tri1C) &&
  727. Vector2.PointInTriangle(tri2B, tri1A, tri1B, tri1C) &&
  728. Vector2.PointInTriangle(tri2C, tri1A, tri1B, tri1C)) return true;
  729. if (Vector2.PointInTriangle(tri1A, tri2A, tri2B, tri2C) &&
  730. Vector2.PointInTriangle(tri1B, tri2A, tri2B, tri2C) &&
  731. Vector2.PointInTriangle(tri1C, tri2A, tri2B, tri2C)) return true;
  732. return false;
  733. }
  734. }
  735. }