diagram.js 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (global = global || self, factory(global.Diagram = {}));
  5. }(this, function (exports) { 'use strict';
  6. /**
  7. * EventManager is used to manager DOM events creationg and destruction in a single function call.
  8. *
  9. * It is used by objects to make it easier to add and remove events from global DOM objects.
  10. *
  11. * @class
  12. */
  13. function EventManager()
  14. {
  15. /**
  16. * Stores all events in the manager, their target and callback.
  17. *
  18. * Format [target, event, callback, active]
  19. *
  20. * @type {Array}
  21. */
  22. this.events = [];
  23. }
  24. /**
  25. * Add new event to the manager.
  26. *
  27. * @param {DOM} target Event target element.
  28. * @param {String} event Event name.
  29. * @param {Function} callback Callback function.
  30. */
  31. EventManager.prototype.add = function(target, event, callback)
  32. {
  33. this.events.push([target, event, callback, false]);
  34. };
  35. /**
  36. * Destroys this manager and remove all events.
  37. */
  38. EventManager.prototype.clear = function()
  39. {
  40. this.destroy();
  41. this.events = [];
  42. };
  43. /**
  44. * Creates all events in this manager.
  45. */
  46. EventManager.prototype.create = function()
  47. {
  48. for(var i = 0; i < this.events.length; i++)
  49. {
  50. var event = this.events[i];
  51. event[0].addEventListener(event[1], event[2]);
  52. event[3] = true;
  53. }
  54. };
  55. /**
  56. * Removes all events in this manager.
  57. */
  58. EventManager.prototype.destroy = function()
  59. {
  60. for(var i = 0; i < this.events.length; i++)
  61. {
  62. var event = this.events[i];
  63. event[0].removeEventListener(event[1], event[2]);
  64. event[3] = false;
  65. }
  66. };
  67. function Vector2(x, y)
  68. {
  69. this.x = x || 0;
  70. this.y = y || 0;
  71. }
  72. Object.assign(Vector2.prototype,
  73. {
  74. set: function(x, y)
  75. {
  76. this.x = x;
  77. this.y = y;
  78. },
  79. setScalar: function(scalar)
  80. {
  81. this.x = scalar;
  82. this.y = scalar;
  83. },
  84. clone: function()
  85. {
  86. return new Vector2(this.x, this.y);
  87. },
  88. copy: function(v)
  89. {
  90. this.x = v.x;
  91. this.y = v.y;
  92. },
  93. add: function(v, w)
  94. {
  95. this.x += v.x;
  96. this.y += v.y;
  97. },
  98. addScalar: function(s)
  99. {
  100. this.x += s;
  101. this.y += s;
  102. },
  103. addVectors: function(a, b)
  104. {
  105. this.x = a.x + b.x;
  106. this.y = a.y + b.y;
  107. },
  108. addScaledVector: function(v, s)
  109. {
  110. this.x += v.x * s;
  111. this.y += v.y * s;
  112. },
  113. sub: function(v, w)
  114. {
  115. this.x -= v.x;
  116. this.y -= v.y;
  117. },
  118. subScalar: function(s)
  119. {
  120. this.x -= s;
  121. this.y -= s;
  122. },
  123. subVectors: function(a, b)
  124. {
  125. this.x = a.x - b.x;
  126. this.y = a.y - b.y;
  127. },
  128. multiply: function(v)
  129. {
  130. this.x *= v.x;
  131. this.y *= v.y;
  132. },
  133. multiplyScalar: function(scalar)
  134. {
  135. this.x *= scalar;
  136. this.y *= scalar;
  137. },
  138. divide: function(v)
  139. {
  140. this.x /= v.x;
  141. this.y /= v.y;
  142. },
  143. divideScalar: function(scalar)
  144. {
  145. return this.multiplyScalar(1 / scalar);
  146. },
  147. min: function(v)
  148. {
  149. this.x = Math.min(this.x, v.x);
  150. this.y = Math.min(this.y, v.y);
  151. },
  152. max: function(v)
  153. {
  154. this.x = Math.max(this.x, v.x);
  155. this.y = Math.max(this.y, v.y);
  156. },
  157. clamp: function(min, max)
  158. {
  159. // assumes min < max, componentwise
  160. this.x = Math.max(min.x, Math.min(max.x, this.x));
  161. this.y = Math.max(min.y, Math.min(max.y, this.y));
  162. },
  163. clampScalar: function(minVal, maxVal)
  164. {
  165. this.x = Math.max(minVal, Math.min(maxVal, this.x));
  166. this.y = Math.max(minVal, Math.min(maxVal, this.y));
  167. },
  168. clampLength: function(min, max)
  169. {
  170. var length = this.length();
  171. return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length)));
  172. },
  173. floor: function()
  174. {
  175. this.x = Math.floor(this.x);
  176. this.y = Math.floor(this.y);
  177. },
  178. ceil: function()
  179. {
  180. this.x = Math.ceil(this.x);
  181. this.y = Math.ceil(this.y);
  182. },
  183. round: function()
  184. {
  185. this.x = Math.round(this.x);
  186. this.y = Math.round(this.y);
  187. },
  188. roundToZero: function()
  189. {
  190. this.x = (this.x < 0) ? Math.ceil(this.x) : Math.floor(this.x);
  191. this.y = (this.y < 0) ? Math.ceil(this.y) : Math.floor(this.y);
  192. },
  193. negate: function()
  194. {
  195. this.x = -this.x;
  196. this.y = -this.y;
  197. return this;
  198. },
  199. dot: function(v)
  200. {
  201. return this.x * v.x + this.y * v.y;
  202. },
  203. cross: function(v)
  204. {
  205. return this.x * v.y - this.y * v.x;
  206. },
  207. lengthSq: function()
  208. {
  209. return this.x * this.x + this.y * this.y;
  210. },
  211. length: function()
  212. {
  213. return Math.sqrt(this.x * this.x + this.y * this.y);
  214. },
  215. manhattanLength: function()
  216. {
  217. return Math.abs(this.x) + Math.abs(this.y);
  218. },
  219. normalize: function()
  220. {
  221. return this.divideScalar(this.length() || 1);
  222. },
  223. /**
  224. * Computes the angle in radians with respect to the positive x-axis
  225. */
  226. angle: function()
  227. {
  228. var angle = Math.atan2(this.y, this.x);
  229. if(angle < 0) angle += 2 * Math.PI;
  230. return angle;
  231. },
  232. distanceTo: function(v)
  233. {
  234. return Math.sqrt(this.distanceToSquared(v));
  235. },
  236. distanceToSquared: function(v)
  237. {
  238. var dx = this.x - v.x,
  239. dy = this.y - v.y;
  240. return dx * dx + dy * dy;
  241. },
  242. manhattanDistanceTo: function(v)
  243. {
  244. return Math.abs(this.x - v.x) + Math.abs(this.y - v.y);
  245. },
  246. setLength: function(length)
  247. {
  248. return this.normalize().multiplyScalar(length);
  249. },
  250. lerp: function(v, alpha)
  251. {
  252. this.x += (v.x - this.x) * alpha;
  253. this.y += (v.y - this.y) * alpha;
  254. },
  255. lerpVectors: function(v1, v2, alpha)
  256. {
  257. return this.subVectors(v2, v1).multiplyScalar(alpha).add(v1);
  258. },
  259. equals: function(v)
  260. {
  261. return ((v.x === this.x) && (v.y === this.y));
  262. },
  263. fromArray: function(array, offset)
  264. {
  265. if(offset === undefined) offset = 0;
  266. this.x = array[offset];
  267. this.y = array[offset + 1];
  268. },
  269. toArray: function(array, offset)
  270. {
  271. if(array === undefined) array = [];
  272. if(offset === undefined) offset = 0;
  273. array[offset] = this.x;
  274. array[offset + 1] = this.y;
  275. return array;
  276. },
  277. rotateAround: function(center, angle)
  278. {
  279. var c = Math.cos(angle),
  280. s = Math.sin(angle);
  281. var x = this.x - center.x;
  282. var y = this.y - center.y;
  283. this.x = x * c - y * s + center.x;
  284. this.y = x * s + y * c + center.y;
  285. }
  286. });
  287. /**
  288. * 2D 3x2 transformation matrix, applied to the canvas elements.
  289. *
  290. * @class
  291. */
  292. function Matrix(values)
  293. {
  294. if(values !== undefined)
  295. {
  296. this.m = values;
  297. }
  298. else
  299. {
  300. this.identity();
  301. }
  302. }
  303. /**
  304. * Copy the content of another matrix and store in this one.
  305. */
  306. Matrix.prototype.copy = function(mat)
  307. {
  308. this.m = mat.m.slice(0);
  309. };
  310. /**
  311. * Create a new matrix object with a copy of the content of this one.
  312. */
  313. Matrix.prototype.clone = function()
  314. {
  315. return new Matrix(this.m.slice(0))
  316. };
  317. /**
  318. * Reset this matrix to indentity.
  319. */
  320. Matrix.prototype.identity = function()
  321. {
  322. this.m = [1, 0, 0, 1, 0, 0];
  323. };
  324. /**
  325. * Multiply another matrix by this one and store the result.
  326. *
  327. * @param mat Matrix array.
  328. */
  329. Matrix.prototype.multiply = function(mat)
  330. {
  331. var m0 = this.m[0] * mat.m[0] + this.m[2] * mat.m[1];
  332. var m1 = this.m[1] * mat.m[0] + this.m[3] * mat.m[1];
  333. var m2 = this.m[0] * mat.m[2] + this.m[2] * mat.m[3];
  334. var m3 = this.m[1] * mat.m[2] + this.m[3] * mat.m[3];
  335. var m4 = this.m[0] * mat.m[4] + this.m[2] * mat.m[5] + this.m[4];
  336. var m5 = this.m[1] * mat.m[4] + this.m[3] * mat.m[5] + this.m[5];
  337. this.m = [m0, m1, m2, m3, m4, m5];
  338. };
  339. /**
  340. * Premultiply another matrix by this one and store the result.
  341. *
  342. * @param mat Matrix array to multiply.
  343. */
  344. Matrix.prototype.premultiply = function(mat)
  345. {
  346. var m0 = mat.m[0] * this.m[0] + mat.m[2] * this.m[1];
  347. var m1 = mat.m[1] * this.m[0] + mat.m[3] * this.m[1];
  348. var m2 = mat.m[0] * this.m[2] + mat.m[2] * this.m[3];
  349. var m3 = mat.m[1] * this.m[2] + mat.m[3] * this.m[3];
  350. var m4 = mat.m[0] * this.m[4] + mat.m[2] * this.m[5] + mat.m[4];
  351. var m5 = mat.m[1] * this.m[4] + mat.m[3] * this.m[5] + mat.m[5];
  352. this.m = [m0, m1, m2, m3, m4, m5];
  353. };
  354. /**
  355. * Compose this transformation matrix with position scale and rotation.
  356. */
  357. Matrix.prototype.compose = function(px, py, sx, sy, a)
  358. {
  359. this.m = [1, 0, 0, 1, px, py];
  360. var c = Math.cos(a);
  361. var s = Math.sin(a);
  362. this.multiply(new Matrix([c, s, -s, c, 0, 0]));
  363. this.scale(sx, sy);
  364. };
  365. /**
  366. * Apply translation to this matrix.
  367. */
  368. Matrix.prototype.translate = function(x, y)
  369. {
  370. this.m[4] += this.m[0] * x + this.m[2] * y;
  371. this.m[5] += this.m[1] * x + this.m[3] * y;
  372. };
  373. /**
  374. * Apply rotation to this matrix.
  375. *
  376. * @param angle Angle in radians.
  377. */
  378. Matrix.prototype.rotate = function(rad)
  379. {
  380. var c = Math.cos(rad);
  381. var s = Math.sin(rad);
  382. var m11 = this.m[0] * c + this.m[2] * s;
  383. var m12 = this.m[1] * c + this.m[3] * s;
  384. var m21 = this.m[0] * -s + this.m[2] * c;
  385. var m22 = this.m[1] * -s + this.m[3] * c;
  386. this.m[0] = m11;
  387. this.m[1] = m12;
  388. this.m[2] = m21;
  389. this.m[3] = m22;
  390. };
  391. /**
  392. * Apply scale to this matrix.
  393. */
  394. Matrix.prototype.scale = function(sx, sy)
  395. {
  396. this.m[0] *= sx;
  397. this.m[1] *= sx;
  398. this.m[2] *= sy;
  399. this.m[3] *= sy;
  400. };
  401. /**
  402. * Set the position of the transformation matrix.
  403. */
  404. Matrix.prototype.setPosition = function(x, y)
  405. {
  406. this.m[4] = x;
  407. this.m[5] = y;
  408. };
  409. /**
  410. * Get the scale from the transformation matrix.
  411. */
  412. Matrix.prototype.getScale = function()
  413. {
  414. return new Vector2(this.m[0], this.m[3]);
  415. };
  416. /**
  417. * Get the position from the transformation matrix.
  418. */
  419. Matrix.prototype.getPosition = function()
  420. {
  421. return new Vector2(this.m[4], this.m[5]);
  422. };
  423. /**
  424. * Apply skew to this matrix.
  425. */
  426. Matrix.prototype.skew = function(radianX, radianY)
  427. {
  428. this.multiply(new Matrix([1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0]));
  429. };
  430. /**
  431. * Get the matrix determinant.
  432. */
  433. Matrix.prototype.determinant = function()
  434. {
  435. return 1 / (this.m[0] * this.m[3] - this.m[1] * this.m[2]);
  436. };
  437. /**
  438. * Get the inverse matrix.
  439. */
  440. Matrix.prototype.getInverse = function()
  441. {
  442. var d = this.determinant();
  443. return new Matrix([this.m[3] * d, -this.m[1] * d, -this.m[2] * d, this.m[0] * d, d * (this.m[2] * this.m[5] - this.m[3] * this.m[4]), d * (this.m[1] * this.m[4] - this.m[0] * this.m[5])]);
  444. };
  445. /**
  446. * Transform a point using this matrix.
  447. */
  448. Matrix.prototype.transformPoint = function(p)
  449. {
  450. var px = p.x * this.m[0] + p.y * this.m[2] + this.m[4];
  451. var py = p.x * this.m[1] + p.y * this.m[3] + this.m[5];
  452. return new Vector2(px, py);
  453. };
  454. /**
  455. * Set a canvas context to use this transformation.
  456. */
  457. Matrix.prototype.setContextTransform = function(context)
  458. {
  459. context.setTransform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  460. };
  461. /**
  462. * Transform on top of the current context transformation.
  463. */
  464. Matrix.prototype.tranformContext = function(context)
  465. {
  466. context.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  467. };
  468. /**
  469. * Implements all UUID related methods.
  470. *
  471. * @class
  472. */
  473. function UUID(){}
  474. /**
  475. * Generate new random UUID v4 as string.
  476. *
  477. * http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  478. */
  479. UUID.generate = (function ()
  480. {
  481. var lut = [];
  482. for(var i = 0; i < 256; i++)
  483. {
  484. lut[i] = (i < 16 ? "0" : "") + (i).toString(16);
  485. }
  486. return function generateUUID()
  487. {
  488. var d0 = Math.random() * 0XFFFFFFFF | 0;
  489. var d1 = Math.random() * 0XFFFFFFFF | 0;
  490. var d2 = Math.random() * 0XFFFFFFFF | 0;
  491. var d3 = Math.random() * 0XFFFFFFFF | 0;
  492. var uuid = lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + "-" +
  493. lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + "-" + lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + "-" +
  494. lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + "-" + lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] +
  495. lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] + lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff];
  496. return uuid.toUpperCase();
  497. };
  498. })();
  499. /**
  500. * Base 2D object class, implements all the object positioning and scalling features.
  501. *
  502. * @class
  503. */
  504. function Object2D()
  505. {
  506. /**
  507. * UUID of the object.
  508. */
  509. this.uuid = UUID.generate();
  510. /**
  511. * List of children objects attached to the object.
  512. */
  513. this.children = [];
  514. /**
  515. * Parent object, the object position is affected by its parent position.
  516. */
  517. this.parent = null;
  518. /**
  519. * Position of the object.
  520. */
  521. this.position = new Vector2(0, 0);
  522. /**
  523. * Scale of the object.
  524. */
  525. this.scale = new Vector2(1, 1);
  526. /**
  527. * Rotation of the object relative to its center.
  528. */
  529. this.rotation = 0.0;
  530. /**
  531. * Indicates if the object is visible.
  532. */
  533. this.visible = true;
  534. /**
  535. * Layer of this object, objects are sorted by layer value.
  536. *
  537. * Lower layer value is draw first.
  538. */
  539. this.layer = 0;
  540. /**
  541. * Local transformation matrix applied to the object.
  542. */
  543. this.matrix = new Matrix();
  544. /**
  545. * Global transformation matrix multiplied by the parent matrix.
  546. *
  547. * Used to transform the object before projecting into screen coordinates.
  548. */
  549. this.globalMatrix = new Matrix();
  550. /**
  551. * Inverse of the global matrix.
  552. *
  553. * Used to convert pointer input points into object coordinates.
  554. */
  555. this.inverseGlobalMatrix = new Matrix();
  556. /**
  557. * If true the matrix is updated before rendering the object.
  558. */
  559. this.matrixNeedsUpdate = true;
  560. /**
  561. * Indicates if its possible to drag the object around.
  562. *
  563. * If true the onPointerDrag callback is used to update the state of the object.
  564. */
  565. this.draggable = true;
  566. /**
  567. * Flag indicating if the pointer is inside of the element.
  568. *
  569. * Used to control object event.
  570. */
  571. this.pointerInside = false;
  572. /**
  573. * Flag to indicate if the object is currently being dragged.
  574. */
  575. this.beingDragged = false;
  576. }
  577. /**
  578. * Traverse the object tree and run a function for all objects.
  579. *
  580. * @param callback Callback function that receives the object as parameter.
  581. */
  582. Object2D.prototype.traverse = function(callback)
  583. {
  584. callback(this);
  585. var children = this.children;
  586. for(var i = 0; i < children.length; i++)
  587. {
  588. children[i].traverse(callback);
  589. }
  590. };
  591. /**
  592. * Attach a children to the object.
  593. *
  594. * @param object Object to attach to this object.
  595. */
  596. Object2D.prototype.add = function(object)
  597. {
  598. object.parent = this;
  599. this.children.push(object);
  600. };
  601. /**
  602. * Remove object from the children list.
  603. *
  604. * @param object Object to be removed.
  605. */
  606. Object2D.prototype.remove = function(object)
  607. {
  608. var index = this.children.indexOf(object);
  609. if(index !== -1)
  610. {
  611. this.children[index].parent = null;
  612. this.children.splice(index, 1);
  613. }
  614. };
  615. /**
  616. * Check if a point is inside of the object.
  617. */
  618. Object2D.prototype.isInside = function(point)
  619. {
  620. return false;
  621. };
  622. /**
  623. * Update the transformation matrix of the object.
  624. */
  625. Object2D.prototype.updateMatrix = function(context)
  626. {
  627. if(this.matrixNeedsUpdate)
  628. {
  629. this.matrix.compose(this.position.x, this.position.y, this.scale.x, this.scale.y, this.rotation);
  630. this.globalMatrix.copy(this.matrix);
  631. if(this.parent !== null)
  632. {
  633. this.globalMatrix.premultiply(this.parent.globalMatrix);
  634. }
  635. this.inverseGlobalMatrix = this.globalMatrix.getInverse();
  636. //this.matrixNeedsUpdate = false;
  637. }
  638. };
  639. /**
  640. * Draw the object into the canvas.
  641. *
  642. * Has to be implemented by underlying classes.
  643. *
  644. * @param context Canvas 2d drawing context.
  645. */
  646. Object2D.prototype.draw = function(context){};
  647. /**
  648. * Callback method called every time before the object is draw into the canvas.
  649. *
  650. * Can be used to run preparation code, move the object, etc.
  651. */
  652. Object2D.prototype.onUpdate = null;
  653. /**
  654. * Callback method called when the pointer enters the object.
  655. *
  656. * Receives (pointer, viewport) as arguments.
  657. */
  658. Object2D.prototype.onPointerEnter = null;
  659. /**
  660. * Callback method called when the was inside of the object and leaves the object.
  661. *
  662. * Receives (pointer, viewport) as arguments.
  663. */
  664. Object2D.prototype.onPointerLeave = null;
  665. /**
  666. * Callback method while the pointer is over (inside) of the object.
  667. *
  668. * Receives (pointer, viewport) as arguments.
  669. */
  670. Object2D.prototype.onPointerOver = null;
  671. /**
  672. * Callback method while the object is being dragged across the screen.
  673. *
  674. * Receives (pointer, viewport, delta) as arguments. Delta is the movement of the pointer already translated into local object coordinates.
  675. */
  676. Object2D.prototype.onPointerDrag = null;
  677. /**
  678. * Callback method called while the pointer button is pressed.
  679. *
  680. * Receives (pointer, viewport) as arguments.
  681. */
  682. Object2D.prototype.onButtonPressed = null;
  683. /**
  684. * Callback method called when the pointer button is pressed down (single time).
  685. */
  686. Object2D.prototype.onButtonDown = null;
  687. /**
  688. * Callback method called when the pointer button is released (single time).
  689. */
  690. Object2D.prototype.onButtonUp = null;
  691. /**
  692. * Key is used by Keyboard, Pointer, etc, to represent a key state.
  693. *
  694. * @class
  695. */
  696. function Key()
  697. {
  698. /**
  699. * Indicates if this key is currently pressed.
  700. */
  701. this.pressed = false;
  702. /**
  703. * Indicates if this key was just pressed.
  704. */
  705. this.justPressed = false;
  706. /**
  707. * Indicates if this key was just released.
  708. */
  709. this.justReleased = false;
  710. }
  711. Key.DOWN = -1;
  712. Key.UP = 1;
  713. Key.RESET = 0;
  714. Key.prototype.constructor = Key;
  715. /**
  716. * Update Key status based on new key state.
  717. */
  718. Key.prototype.update = function(action)
  719. {
  720. this.justPressed = false;
  721. this.justReleased = false;
  722. if(action === Key.DOWN)
  723. {
  724. if(this.pressed === false)
  725. {
  726. this.justPressed = true;
  727. }
  728. this.pressed = true;
  729. }
  730. else if(action === Key.UP)
  731. {
  732. if(this.pressed)
  733. {
  734. this.justReleased = true;
  735. }
  736. this.pressed = false;
  737. }
  738. else if(action === Key.RESET)
  739. {
  740. this.justReleased = false;
  741. this.justPressed = false;
  742. }
  743. };
  744. /**
  745. * Set this key attributes manually.
  746. */
  747. Key.prototype.set = function(justPressed, pressed, justReleased)
  748. {
  749. this.justPressed = justPressed;
  750. this.pressed = pressed;
  751. this.justReleased = justReleased;
  752. };
  753. /**
  754. * Reset key to default values.
  755. */
  756. Key.prototype.reset = function()
  757. {
  758. this.justPressed = false;
  759. this.pressed = false;
  760. this.justReleased = false;
  761. };
  762. /**
  763. * Pointer instance for input in sync with the running 3D application.
  764. *
  765. * The pointer object provided by scripts is automatically updated by the runtime handler.
  766. *
  767. * @class
  768. * @param {DOM} domElement DOM element to craete the pointer events.
  769. * @param {Boolean} dontInitialize If true the pointer events are not created.
  770. */
  771. function Pointer(domElement)
  772. {
  773. //Raw data
  774. this._keys = new Array(5);
  775. this._position = new Vector2(0, 0);
  776. this._positionUpdated = false;
  777. this._delta = new Vector2(0, 0);
  778. this._wheel = 0;
  779. this._wheelUpdated = false;
  780. this._doubleClicked = new Array(5);
  781. /**
  782. * Array with pointer buttons status.
  783. */
  784. this.keys = new Array(5);
  785. /**
  786. * Pointer position inside of the window (coordinates in window space).
  787. */
  788. this.position = new Vector2(0, 0);
  789. /**
  790. * Pointer movement (coordinates in window space).
  791. */
  792. this.delta = new Vector2(0, 0);
  793. /**
  794. * Pointer scroll wheel movement.
  795. */
  796. this.wheel = 0;
  797. /**
  798. * Indicates a button of the pointer was double clicked.
  799. */
  800. this.doubleClicked = new Array(5);
  801. /**
  802. * DOM element where to attach the pointer events.
  803. */
  804. this.domElement = (domElement !== undefined) ? domElement : window;
  805. /**
  806. * Canvas attached to this pointer instance used to calculate position and delta in element space coordinates.
  807. */
  808. this.canvas = null;
  809. /**
  810. * Event manager responsible for updating the raw data variables.
  811. *
  812. * Diferent events are used depending on the host platform.
  813. *
  814. * When the update method is called the raw data is reset.
  815. */
  816. this.events = new EventManager();
  817. //Initialize key instances
  818. for(var i = 0; i < 5; i++)
  819. {
  820. this._doubleClicked[i] = false;
  821. this.doubleClicked[i] = false;
  822. this._keys[i] = new Key();
  823. this.keys[i] = new Key();
  824. }
  825. //Self pointer
  826. var self = this;
  827. //Scroll wheel
  828. if(window.onmousewheel !== undefined)
  829. {
  830. //Chrome, edge
  831. this.events.add(this.domElement, "mousewheel", function(event)
  832. {
  833. self._wheel = event.deltaY;
  834. self._wheelUpdated = true;
  835. });
  836. }
  837. else if(window.addEventListener !== undefined)
  838. {
  839. //Firefox
  840. this.events.add(this.domElement, "DOMMouseScroll", function(event)
  841. {
  842. self._wheel = event.detail * 30;
  843. self._wheelUpdated = true;
  844. });
  845. }
  846. else
  847. {
  848. this.events.add(this.domElement, "wheel", function(event)
  849. {
  850. self._wheel = event.deltaY;
  851. self._wheelUpdated = true;
  852. });
  853. }
  854. //Touchscreen input events
  855. if(window.ontouchstart !== undefined || navigator.msMaxTouchPoints > 0)
  856. {
  857. //Auxiliar variables to calculate touch delta
  858. var lastTouch = new Vector2(0, 0);
  859. //Touch start event
  860. this.events.add(this.domElement, "touchstart", function(event)
  861. {
  862. var touch = event.touches[0];
  863. self.updatePosition(touch.clientX, touch.clientY, 0, 0);
  864. self.updateKey(Pointer.LEFT, Key.DOWN);
  865. lastTouch.set(touch.clientX, touch.clientY);
  866. });
  867. //Touch end event
  868. this.events.add(this.domElement, "touchend", function(event)
  869. {
  870. self.updateKey(Pointer.LEFT, Key.UP);
  871. });
  872. //Touch cancel event
  873. this.events.add(this.domElement, "touchcancel", function(event)
  874. {
  875. self.updateKey(Pointer.LEFT, Key.UP);
  876. });
  877. //Touch move event
  878. this.events.add(document.body, "touchmove", function(event)
  879. {
  880. var touch = event.touches[0];
  881. self.updatePosition(touch.clientX, touch.clientY, touch.clientX - lastTouch.x, touch.clientY - lastTouch.y);
  882. lastTouch.set(touch.clientX, touch.clientY);
  883. });
  884. }
  885. //Move
  886. this.events.add(this.domElement, "mousemove", function(event)
  887. {
  888. self.updatePosition(event.clientX, event.clientY, event.movementX, event.movementY);
  889. });
  890. //Button pressed
  891. this.events.add(this.domElement, "mousedown", function(event)
  892. {
  893. self.updateKey(event.which - 1, Key.DOWN);
  894. });
  895. //Button released
  896. this.events.add(this.domElement, "mouseup", function(event)
  897. {
  898. self.updateKey(event.which - 1, Key.UP);
  899. });
  900. //Drag start
  901. this.events.add(this.domElement, "dragstart", function(event)
  902. {
  903. self.updateKey(event.which - 1, Key.UP);
  904. });
  905. //Pointer double click
  906. this.events.add(this.domElement, "dblclick", function(event)
  907. {
  908. self._doubleClicked[event.which - 1] = true;
  909. });
  910. this.create();
  911. }
  912. Pointer.prototype = Pointer;
  913. Pointer.prototype.constructor = Pointer;
  914. /**
  915. * Left pointer button.
  916. */
  917. Pointer.LEFT = 0;
  918. /**
  919. * Middle pointer button.
  920. */
  921. Pointer.MIDDLE = 1;
  922. /**
  923. * Right pointer button.
  924. */
  925. Pointer.RIGHT = 2;
  926. /**
  927. * Back pointer navigation button.
  928. */
  929. Pointer.BACK = 3;
  930. /**
  931. * Forward pointer navigation button.
  932. */
  933. Pointer.FORWARD = 4;
  934. /**
  935. * Element to be used for coordinates calculation relative to that canvas.
  936. *
  937. * @param {DOM} canvas Canvas to be attached to the Pointer instance
  938. */
  939. Pointer.setCanvas = function(element)
  940. {
  941. this.canvas = element;
  942. element.pointerInside = false;
  943. element.addEventListener("mouseenter", function()
  944. {
  945. this.pointerInside = true;
  946. });
  947. element.addEventListener("mouseleave", function()
  948. {
  949. this.pointerInside = false;
  950. });
  951. };
  952. /**
  953. * Check if pointer is inside attached canvas (updated async).
  954. *
  955. * @return {boolean} True if pointer is currently inside the canvas
  956. */
  957. Pointer.insideCanvas = function()
  958. {
  959. return this.canvas !== null && this.canvas.pointerInside;
  960. };
  961. /**
  962. * Check if pointer button is currently pressed.
  963. *
  964. * @param {Number} button Button to check status of
  965. * @return {boolean} True if button is currently pressed
  966. */
  967. Pointer.buttonPressed = function(button)
  968. {
  969. return this.keys[button].pressed;
  970. };
  971. /**
  972. * Check if pointer button was double clicked.
  973. *
  974. * @param {Number} button Button to check status of
  975. * @return {boolean} True if some pointer button was just double clicked
  976. */
  977. Pointer.buttonDoubleClicked = function(button)
  978. {
  979. return this.doubleClicked[button];
  980. };
  981. /**
  982. * Check if a pointer button was just pressed.
  983. *
  984. * @param {Number} button Button to check status of
  985. * @return {boolean} True if button was just pressed
  986. */
  987. Pointer.buttonJustPressed = function(button)
  988. {
  989. return this.keys[button].justPressed;
  990. };
  991. /**
  992. * Check if a pointer button was just released.
  993. *
  994. * @param {Number} button Button to check status of
  995. * @return {boolean} True if button was just released
  996. */
  997. Pointer.buttonJustReleased = function(button)
  998. {
  999. return this.keys[button].justReleased;
  1000. };
  1001. /**
  1002. * Update pointer position.
  1003. *
  1004. * Automatically called by the runtime.
  1005. *
  1006. * @param {Number} x
  1007. * @param {Number} y
  1008. * @param {Number} xDiff
  1009. * @param {Number} yDiff
  1010. */
  1011. Pointer.updatePosition = function(x, y, xDiff, yDiff)
  1012. {
  1013. if(this.canvas !== null)
  1014. {
  1015. var rect = this.canvas.getBoundingClientRect();
  1016. x -= rect.left;
  1017. y -= rect.top;
  1018. }
  1019. this._position.set(x, y);
  1020. this._delta.x += xDiff;
  1021. this._delta.y += yDiff;
  1022. this._positionUpdated = true;
  1023. };
  1024. /**
  1025. * Update a pointer button.
  1026. *
  1027. * Automatically called by the runtime.
  1028. *
  1029. * @param {Number} button
  1030. * @param {Number} action
  1031. */
  1032. Pointer.updateKey = function(button, action)
  1033. {
  1034. if(button > -1)
  1035. {
  1036. this._keys[button].update(action);
  1037. }
  1038. };
  1039. /**
  1040. * Update pointer buttons state, position, wheel and delta synchronously.
  1041. */
  1042. Pointer.update = function()
  1043. {
  1044. //Update pointer keys state
  1045. for(var i = 0; i < 5; i++)
  1046. {
  1047. if(this._keys[i].justPressed && this.keys[i].justPressed)
  1048. {
  1049. this._keys[i].justPressed = false;
  1050. }
  1051. if(this._keys[i].justReleased && this.keys[i].justReleased)
  1052. {
  1053. this._keys[i].justReleased = false;
  1054. }
  1055. this.keys[i].set(this._keys[i].justPressed, this._keys[i].pressed, this._keys[i].justReleased);
  1056. //Update pointer double click
  1057. if(this._doubleClicked[i] === true)
  1058. {
  1059. this.doubleClicked[i] = true;
  1060. this._doubleClicked[i] = false;
  1061. }
  1062. else
  1063. {
  1064. this.doubleClicked[i] = false;
  1065. }
  1066. }
  1067. //Update pointer wheel
  1068. if(this._wheelUpdated)
  1069. {
  1070. this.wheel = this._wheel;
  1071. this._wheelUpdated = false;
  1072. }
  1073. else
  1074. {
  1075. this.wheel = 0;
  1076. }
  1077. //Update pointer Position if needed
  1078. if(this._positionUpdated)
  1079. {
  1080. this.delta.copy(this._delta);
  1081. this.position.copy(this._position);
  1082. this._delta.set(0,0);
  1083. this._positionUpdated = false;
  1084. }
  1085. else
  1086. {
  1087. this.delta.x = 0;
  1088. this.delta.y = 0;
  1089. }
  1090. };
  1091. /**
  1092. * Create pointer events.
  1093. */
  1094. Pointer.create = function()
  1095. {
  1096. this.events.create();
  1097. };
  1098. /**
  1099. * Dispose pointer events.
  1100. */
  1101. Pointer.dispose = function()
  1102. {
  1103. this.events.destroy();
  1104. };
  1105. /**
  1106. * The renderer is resposible for drawing the structure into the canvas element.
  1107. *
  1108. * Its also resposible for managing the canvas state.
  1109. *
  1110. * @class
  1111. */
  1112. function Renderer(canvas)
  1113. {
  1114. /**
  1115. * Canvas DOM element, has to be managed by the user.
  1116. */
  1117. this.canvas = canvas;
  1118. /**
  1119. * Canvas 2D rendering context used to draw content.
  1120. */
  1121. this.context = canvas.getContext("2d");
  1122. this.context.imageSmoothingEnabled = true;
  1123. this.context.globalCompositeOperation = "source-over";
  1124. /**
  1125. * Pointer input handler object.
  1126. */
  1127. this.pointer = new Pointer();
  1128. this.pointer.setCanvas(canvas);
  1129. }
  1130. /**
  1131. * Update the renderer state, update the input handlers, calculate the object and viewport transformation matrices.
  1132. *
  1133. * @param object Object to be updated.
  1134. * @param viewport Viewport to be updated (should be the one where the objects will be rendered after).
  1135. */
  1136. Renderer.prototype.update = function(object, viewport)
  1137. {
  1138. this.pointer.update();
  1139. var pointer = this.pointer;
  1140. // Viewport transform matrix
  1141. viewport.updateControls(pointer);
  1142. viewport.updateMatrix();
  1143. // Project pointer coordinates
  1144. var point = pointer.position.clone();
  1145. var viewportPoint = viewport.inverseMatrix.transformPoint(point);
  1146. // Object transformation matrices
  1147. object.traverse(function(child)
  1148. {
  1149. var childPoint = child.inverseGlobalMatrix.transformPoint(viewportPoint);
  1150. // Check if the pointer pointer is inside
  1151. if(child.isInside(childPoint))
  1152. {
  1153. // Pointer enter
  1154. if(!child.pointerInside && child.onPointerEnter !== null)
  1155. {
  1156. child.onPointerEnter(pointer, viewport);
  1157. }
  1158. // Pointer over
  1159. if(child.onPointerOver !== null)
  1160. {
  1161. child.onPointerOver(pointer, viewport);
  1162. }
  1163. // Pointer just pressed
  1164. if(pointer.buttonJustPressed(Pointer.LEFT))
  1165. {
  1166. if(child.onButtonDown !== null)
  1167. {
  1168. child.onButtonDown(pointer, viewport);
  1169. }
  1170. if(child.draggable)
  1171. {
  1172. child.beingDragged = true;
  1173. }
  1174. }
  1175. // Pointer pressed
  1176. if(pointer.buttonPressed(Pointer.LEFT) && child.onButtonPressed !== null)
  1177. {
  1178. child.onButtonPressed(pointer, viewport);
  1179. }
  1180. // Just released
  1181. if(pointer.buttonJustReleased(Pointer.LEFT) && child.onButtonUp !== null)
  1182. {
  1183. child.onButtonUp(pointer, viewport);
  1184. }
  1185. child.pointerInside = true;
  1186. }
  1187. else if(child.pointerInside)
  1188. {
  1189. // Pointer leave
  1190. if(child.onPointerLeave !== null)
  1191. {
  1192. child.onPointerLeave(pointer, viewport);
  1193. }
  1194. child.pointerInside = false;
  1195. }
  1196. // Stop object drag
  1197. if(pointer.buttonJustReleased(Pointer.LEFT))
  1198. {
  1199. if(child.draggable)
  1200. {
  1201. child.beingDragged = false;
  1202. }
  1203. }
  1204. // Pointer drag event
  1205. if(child.beingDragged)
  1206. {
  1207. var matrix = viewport.inverseMatrix.clone();
  1208. matrix.multiply(child.inverseGlobalMatrix);
  1209. matrix.setPosition(0, 0);
  1210. var delta = matrix.transformPoint(pointer.delta);
  1211. child.position.add(delta);
  1212. if(child.onPointerDrag !== null)
  1213. {
  1214. child.onPointerDrag(pointer, viewport, delta);
  1215. }
  1216. }
  1217. // On update
  1218. if(child.onUpdate !== null)
  1219. {
  1220. child.onUpdate();
  1221. }
  1222. child.updateMatrix();
  1223. });
  1224. };
  1225. /**
  1226. * Render the object using the viewport into a canvas element.
  1227. *
  1228. * The canvas state is saved and restored for each individual object, ensuring that the code of one object does not affect another one.
  1229. *
  1230. * @param object Object to be rendered.
  1231. * @param viewport Viewport to render the objects.
  1232. */
  1233. Renderer.prototype.render = function(object, viewport)
  1234. {
  1235. var context = this.context;
  1236. // Clear canvas
  1237. context.setTransform(1, 0, 0, 1, 0, 0);
  1238. context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  1239. // Set viewport matrix transform
  1240. viewport.matrix.setContextTransform(context);
  1241. // Get objects to be rendered
  1242. var objects = [];
  1243. object.traverse(function(child)
  1244. {
  1245. if(child.visible)
  1246. {
  1247. objects.push(child);
  1248. }
  1249. });
  1250. // Sort objects by layer
  1251. objects.sort(function(a, b)
  1252. {
  1253. return a.layer - b.layer;
  1254. });
  1255. // Render into the canvas
  1256. for(var i = 0; i < objects.length; i++)
  1257. {
  1258. context.save();
  1259. objects[i].globalMatrix.tranformContext(context);
  1260. objects[i].draw(context);
  1261. context.restore();
  1262. }
  1263. };
  1264. /**
  1265. * Used to indicate how the user views the content inside of the canvas.
  1266. *
  1267. * @class
  1268. */
  1269. function Viewport()
  1270. {
  1271. /**
  1272. * UUID of the object.
  1273. */
  1274. this.uuid = UUID.generate();
  1275. /**
  1276. * Position of the object.
  1277. */
  1278. this.position = new Vector2(0, 0);
  1279. /**
  1280. * Scale of the object.
  1281. */
  1282. this.scale = 1.0;
  1283. /**
  1284. * Rotation of the object relative to its center.
  1285. */
  1286. this.rotation = 0.0;
  1287. /**
  1288. * Local transformation matrix applied to the object.
  1289. */
  1290. this.matrix = new Matrix();
  1291. /**
  1292. * Inverse of the local transformation matrix.
  1293. */
  1294. this.inverseMatrix = new Matrix();
  1295. /**
  1296. * If true the matrix is updated before rendering the object.
  1297. */
  1298. this.matrixNeedsUpdate = true;
  1299. /**
  1300. * Flag to indicate if the viewport should move when scalling.
  1301. *
  1302. * For some application its easier to focus the target if the viewport moves to the pointer location while scalling.
  1303. */
  1304. this.moveOnScale = true;
  1305. }
  1306. /**
  1307. * Update the viewport controls using the pointer object.
  1308. */
  1309. Viewport.prototype.updateControls = function(pointer)
  1310. {
  1311. if(pointer.wheel !== 0)
  1312. {
  1313. this.scale -= pointer.wheel * 1e-3 * this.scale;
  1314. if(this.moveOnScale)
  1315. {
  1316. var speed = pointer.wheel / this.scale;
  1317. var halfWidth = pointer.canvas.width / 2;
  1318. var halfWeight = pointer.canvas.height / 2;
  1319. this.position.x += ((pointer.position.x - halfWidth) / halfWidth) * speed;
  1320. this.position.y += ((pointer.position.y - halfWeight) / halfWeight) * speed;
  1321. }
  1322. }
  1323. if(pointer.buttonPressed(Pointer.RIGHT))
  1324. {
  1325. this.position.x += pointer.delta.x;
  1326. this.position.y += pointer.delta.y;
  1327. }
  1328. };
  1329. /**
  1330. * Calculate and update the viewport transformation matrix.
  1331. */
  1332. Viewport.prototype.updateMatrix = function()
  1333. {
  1334. if(this.matrixNeedsUpdate)
  1335. {
  1336. this.matrix.compose(this.position.x, this.position.y, this.scale, this.scale, this.rotation);
  1337. this.inverseMatrix = this.matrix.getInverse();
  1338. //this.matrixNeedsUpdate = false;
  1339. }
  1340. };
  1341. /**
  1342. * Box is described by a minimum and maximum points.
  1343. *
  1344. * Can be used for collision detection with points and other boxes.
  1345. */
  1346. function Box2(min, max)
  1347. {
  1348. this.min = (min !== undefined) ? min : new Vector2();
  1349. this.max = (max !== undefined) ? max : new Vector2();
  1350. }
  1351. Object.assign(Box2.prototype,
  1352. {
  1353. set: function(min, max)
  1354. {
  1355. this.min.copy(min);
  1356. this.max.copy(max);
  1357. return this;
  1358. },
  1359. setFromPoints: function(points)
  1360. {
  1361. this.min = new Vector2(+Infinity, +Infinity);
  1362. this.max = new Vector2(-Infinity, -Infinity);
  1363. for(var i = 0, il = points.length; i < il; i++)
  1364. {
  1365. this.expandByPoint(points[i]);
  1366. }
  1367. return this;
  1368. },
  1369. setFromCenterAndSize: function(center, size)
  1370. {
  1371. var v1 = new Vector2();
  1372. var halfSize = v1.copy(size).multiplyScalar(0.5);
  1373. this.min.copy(center).sub(halfSize);
  1374. this.max.copy(center).add(halfSize);
  1375. return this;
  1376. },
  1377. clone: function()
  1378. {
  1379. var box = new Box2();
  1380. box.copy(this);
  1381. return box;
  1382. },
  1383. copy: function(box)
  1384. {
  1385. this.min.copy(box.min);
  1386. this.max.copy(box.max);
  1387. return this;
  1388. },
  1389. isEmpty: function()
  1390. {
  1391. // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
  1392. return (this.max.x < this.min.x) || (this.max.y < this.min.y);
  1393. },
  1394. getCenter: function(target)
  1395. {
  1396. return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5);
  1397. },
  1398. getSize: function(target)
  1399. {
  1400. return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min);
  1401. },
  1402. expandByPoint: function(point)
  1403. {
  1404. this.min.min(point);
  1405. this.max.max(point);
  1406. return this;
  1407. },
  1408. expandByVector: function(vector)
  1409. {
  1410. this.min.sub(vector);
  1411. this.max.add(vector);
  1412. return this;
  1413. },
  1414. expandByScalar: function(scalar)
  1415. {
  1416. this.min.addScalar(-scalar);
  1417. this.max.addScalar(scalar);
  1418. return this;
  1419. },
  1420. containsPoint: function(point)
  1421. {
  1422. return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true;
  1423. },
  1424. containsBox: function(box)
  1425. {
  1426. return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y;
  1427. },
  1428. getParameter: function(point, target)
  1429. {
  1430. // This can potentially have a divide by zero if the box
  1431. // has a size dimension of 0.
  1432. return target.set(
  1433. (point.x - this.min.x) / (this.max.x - this.min.x),
  1434. (point.y - this.min.y) / (this.max.y - this.min.y)
  1435. );
  1436. },
  1437. intersectsBox: function(box)
  1438. {
  1439. // using 4 splitting planes to rule out intersections
  1440. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true;
  1441. },
  1442. clampPoint: function(point, target)
  1443. {
  1444. return target.copy(point).clamp(this.min, this.max);
  1445. },
  1446. distanceToPoint: function(point)
  1447. {
  1448. var v = new Vector2();
  1449. var clampedPoint = v.copy(point).clamp(this.min, this.max);
  1450. return clampedPoint.sub(point).length();
  1451. },
  1452. intersect: function(box)
  1453. {
  1454. this.min.max(box.min);
  1455. this.max.min(box.max);
  1456. return this;
  1457. },
  1458. union: function(box)
  1459. {
  1460. this.min.min(box.min);
  1461. this.max.max(box.max);
  1462. return this;
  1463. },
  1464. translate: function(offset)
  1465. {
  1466. this.min.add(offset);
  1467. this.max.add(offset);
  1468. return this;
  1469. },
  1470. equals: function(box)
  1471. {
  1472. return box.min.equals(this.min) && box.max.equals(this.max);
  1473. }
  1474. });
  1475. /**
  1476. * Circle object draw a circular object.
  1477. */
  1478. function Circle()
  1479. {
  1480. Object2D.call(this);
  1481. /**
  1482. * Radius of the circle.
  1483. */
  1484. this.radius = 10.0;
  1485. /**
  1486. * Color of the circle border line.
  1487. */
  1488. this.strokeStyle = "#000000";
  1489. /**
  1490. * Background color of the circle.
  1491. */
  1492. this.fillStyle = "#FFFFFF";
  1493. }
  1494. Circle.prototype = Object.create(Object2D.prototype);
  1495. Circle.prototype.isInside = function(point)
  1496. {
  1497. return point.length() <= this.radius;
  1498. };
  1499. Circle.prototype.onPointerEnter = function(pointer, viewport)
  1500. {
  1501. this.fillStyle = "#CCCCCC";
  1502. };
  1503. Circle.prototype.onPointerLeave = function(pointer, viewport)
  1504. {
  1505. this.fillStyle = "#FFFFFF";
  1506. };
  1507. Circle.prototype.draw = function(context)
  1508. {
  1509. context.fillStyle = this.fillStyle;
  1510. context.beginPath();
  1511. context.arc(0, 0, this.radius, 0, 2 * Math.PI);
  1512. context.fill();
  1513. context.lineWidth = 1;
  1514. context.strokeStyle = this.strokeStyle;
  1515. context.beginPath();
  1516. context.arc(0, 0, this.radius, 0, 2 * Math.PI);
  1517. context.stroke();
  1518. };
  1519. /**
  1520. * Box object draw a box.
  1521. */
  1522. function Box(resizable)
  1523. {
  1524. Object2D.call(this);
  1525. /**
  1526. * Box object containing the size of the object.
  1527. */
  1528. this.box = new Box2(new Vector2(-50, -35), new Vector2(50, 35));
  1529. /**
  1530. * Color of the box border line.
  1531. */
  1532. this.strokeStyle = "#000000";
  1533. /**
  1534. * Background color of the box.
  1535. */
  1536. this.fillStyle = "#FFFFFF";
  1537. if(resizable)
  1538. {
  1539. this.createResizeHelpers();
  1540. }
  1541. }
  1542. Box.prototype = Object.create(Object2D.prototype);
  1543. Box.prototype.createResizeHelpers = function(first_argument)
  1544. {
  1545. var self = this;
  1546. function updateHelpers()
  1547. {
  1548. topRight.position.copy(self.box.min);
  1549. bottomLeft.position.copy(self.box.max);
  1550. topLeft.position.set(self.box.max.x, self.box.min.y);
  1551. bottomRight.position.set(self.box.min.x, self.box.max.y);
  1552. }
  1553. var topRight = new Circle();
  1554. topRight.radius = 4;
  1555. topRight.onPointerDrag = function(pointer, viewport, delta)
  1556. {
  1557. self.box.min.copy(topRight.position);
  1558. updateHelpers();
  1559. };
  1560. this.add(topRight);
  1561. var topLeft = new Circle();
  1562. topLeft.radius = 4;
  1563. topLeft.onPointerDrag = function(pointer, viewport, delta)
  1564. {
  1565. self.box.max.x = topLeft.position.x;
  1566. self.box.min.y = topLeft.position.y;
  1567. updateHelpers();
  1568. };
  1569. this.add(topLeft);
  1570. var bottomLeft = new Circle();
  1571. bottomLeft.radius = 4;
  1572. bottomLeft.onPointerDrag = function(pointer, viewport, delta)
  1573. {
  1574. self.box.max.copy(bottomLeft.position);
  1575. updateHelpers();
  1576. };
  1577. this.add(bottomLeft);
  1578. var bottomRight = new Circle();
  1579. bottomRight.radius = 4;
  1580. bottomRight.onPointerDrag = function(pointer, viewport, delta)
  1581. {
  1582. self.box.min.x = bottomRight.position.x;
  1583. self.box.max.y = bottomRight.position.y;
  1584. updateHelpers();
  1585. };
  1586. this.add(bottomRight);
  1587. updateHelpers();
  1588. };
  1589. Box.prototype.onPointerEnter = function(pointer, viewport)
  1590. {
  1591. this.fillStyle = "#CCCCCC";
  1592. };
  1593. Box.prototype.onPointerLeave = function(pointer, viewport)
  1594. {
  1595. this.fillStyle = "#FFFFFF";
  1596. };
  1597. Box.prototype.isInside = function(point)
  1598. {
  1599. return this.box.containsPoint(point);
  1600. };
  1601. Box.prototype.draw = function(context)
  1602. {
  1603. var width = this.box.max.x - this.box.min.x;
  1604. var height = this.box.max.y - this.box.min.y;
  1605. context.fillStyle = this.fillStyle;
  1606. context.fillRect(this.box.min.x, this.box.min.y, width, height);
  1607. context.lineWidth = 1;
  1608. context.strokeStyle = this.strokeStyle;
  1609. context.strokeRect(this.box.min.x, this.box.min.y, width, height);
  1610. };
  1611. /**
  1612. * Line object draw a line from one point to another.
  1613. */
  1614. function Line()
  1615. {
  1616. Object2D.call(this);
  1617. /**
  1618. * Initial point of the line.
  1619. *
  1620. * Can be equal to the position object of another object. (Making it automatically follow that object.)
  1621. */
  1622. this.from = new Vector2();
  1623. /**
  1624. * Final point of the line.
  1625. *
  1626. * Can be equal to the position object of another object. (Making it automatically follow that object.)
  1627. */
  1628. this.to = new Vector2();
  1629. /**
  1630. * Color of the line.
  1631. */
  1632. this.strokeStyle = "#000000";
  1633. }
  1634. Line.prototype = Object.create(Object2D.prototype);
  1635. Line.prototype.draw = function(context)
  1636. {
  1637. context.lineWidth = 1;
  1638. context.strokeStyle = this.strokeStyle;
  1639. context.setLineDash([5, 5]);
  1640. context.beginPath();
  1641. context.moveTo(this.from.x, this.from.y);
  1642. context.lineTo(this.to.x, this.to.y);
  1643. context.stroke();
  1644. };
  1645. function Text()
  1646. {
  1647. Object2D.call(this);
  1648. /**
  1649. * Text value.
  1650. */
  1651. this.text = "";
  1652. /**
  1653. * Font of the text.
  1654. */
  1655. this.font = "16px Arial";
  1656. /**
  1657. * Color (style) of the text.
  1658. */
  1659. this.color = "#000000";
  1660. }
  1661. Text.prototype = Object.create(Object2D.prototype);
  1662. Text.prototype.draw = function(context)
  1663. {
  1664. context.font = this.font;
  1665. context.textAlign = "center";
  1666. context.fillStyle = this.color;
  1667. context.fillText(this.text, 0, 0);
  1668. };
  1669. function Image(src)
  1670. {
  1671. Object2D.call(this);
  1672. this.image = document.createElement("img");
  1673. this.image.src = src;
  1674. }
  1675. Image.prototype = Object.create(Object2D.prototype);
  1676. Image.prototype.draw = function(context)
  1677. {
  1678. context.drawImage(this.image, 0, 0);
  1679. };
  1680. exports.Box = Box;
  1681. exports.Box2 = Box2;
  1682. exports.Circle = Circle;
  1683. exports.EventManager = EventManager;
  1684. exports.Image = Image;
  1685. exports.Key = Key;
  1686. exports.Line = Line;
  1687. exports.Matrix = Matrix;
  1688. exports.Object2D = Object2D;
  1689. exports.Pointer = Pointer;
  1690. exports.Renderer = Renderer;
  1691. exports.Text = Text;
  1692. exports.UUID = UUID;
  1693. exports.Vector2 = Vector2;
  1694. exports.Viewport = Viewport;
  1695. Object.defineProperty(exports, '__esModule', { value: true });
  1696. }));