escher.js 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360
  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.Escher = {}));
  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. /**
  68. * Class representing a 2D vector. A 2D vector is an ordered pair of numbers (labeled x and y), which can be used to represent points in space, directions, etc.
  69. *
  70. * @class
  71. * @param {number} x
  72. * @param {number} y
  73. */
  74. function Vector2(x, y)
  75. {
  76. this.x = x || 0;
  77. this.y = y || 0;
  78. }
  79. /**
  80. * Set vector x and y values.
  81. *
  82. * @param {number} x
  83. * @param {number} y
  84. */
  85. Vector2.prototype.set = function(x, y)
  86. {
  87. this.x = x;
  88. this.y = y;
  89. };
  90. /**
  91. * Set a scalar value into the x and y values.
  92. */
  93. Vector2.prototype.setScalar = function(scalar)
  94. {
  95. this.x = scalar;
  96. this.y = scalar;
  97. };
  98. /**
  99. * Create a clone of this vector object.
  100. */
  101. Vector2.prototype.clone = function()
  102. {
  103. return new Vector2(this.x, this.y);
  104. };
  105. /**
  106. * Copy the content of another vector into this one.
  107. *
  108. * @param {Vector2} v
  109. */
  110. Vector2.prototype.copy = function(v)
  111. {
  112. this.x = v.x;
  113. this.y = v.y;
  114. };
  115. /**
  116. * Add the content of another vector to this one.
  117. *
  118. * @param {Vector2} v
  119. */
  120. Vector2.prototype.add = function(v)
  121. {
  122. this.x += v.x;
  123. this.y += v.y;
  124. };
  125. /**
  126. * Add a scalar value to booth vector components.
  127. *
  128. * @param {number} s
  129. */
  130. Vector2.prototype.addScalar = function(s)
  131. {
  132. this.x += s;
  133. this.y += s;
  134. };
  135. /**
  136. * Add two vectors and store the result in this vector.
  137. *
  138. * @param {Vector2} a
  139. * @param {Vector2} b
  140. */
  141. Vector2.prototype.addVectors = function(a, b)
  142. {
  143. this.x = a.x + b.x;
  144. this.y = a.y + b.y;
  145. };
  146. /**
  147. * Scale a vector components and add the result to this vector.
  148. *
  149. * @param {Vector2} v
  150. * @param {number} s
  151. */
  152. Vector2.prototype.addScaledVector = function(v, s)
  153. {
  154. this.x += v.x * s;
  155. this.y += v.y * s;
  156. };
  157. /**
  158. * Subtract the content of another vector to this one.
  159. *
  160. * @param {Vector2} v
  161. */
  162. Vector2.prototype.sub = function(v)
  163. {
  164. this.x -= v.x;
  165. this.y -= v.y;
  166. };
  167. /**
  168. * Subtract a scalar value to booth vector components.
  169. *
  170. * @param {number} s
  171. */
  172. Vector2.prototype.subScalar = function(s)
  173. {
  174. this.x -= s;
  175. this.y -= s;
  176. };
  177. /**
  178. * Subtract two vectors and store the result in this vector.
  179. *
  180. * @param {Vector2} a
  181. * @param {Vector2} b
  182. */
  183. Vector2.prototype.subVectors = function(a, b)
  184. {
  185. this.x = a.x - b.x;
  186. this.y = a.y - b.y;
  187. };
  188. /**
  189. * Multiply the content of another vector to this one.
  190. *
  191. * @param {Vector2} v
  192. */
  193. Vector2.prototype.multiply = function(v)
  194. {
  195. this.x *= v.x;
  196. this.y *= v.y;
  197. };
  198. /**
  199. * Multiply a scalar value by booth vector components.
  200. *
  201. * @param {number} s
  202. */
  203. Vector2.prototype.multiplyScalar = function(scalar)
  204. {
  205. this.x *= scalar;
  206. this.y *= scalar;
  207. };
  208. /**
  209. * Divide the content of another vector from this one.
  210. *
  211. * @param {Vector2} v
  212. */
  213. Vector2.prototype.divide = function(v)
  214. {
  215. this.x /= v.x;
  216. this.y /= v.y;
  217. };
  218. /**
  219. * Divide a scalar value by booth vector components.
  220. *
  221. * @param {number} s
  222. */
  223. Vector2.prototype.divideScalar = function(scalar)
  224. {
  225. return this.multiplyScalar(1 / scalar);
  226. };
  227. /**
  228. * Set the minimum of x and y coordinates between two vectors.
  229. *
  230. * X is set as the min between this vector and the other vector.
  231. *
  232. * @param {Vector2} v
  233. */
  234. Vector2.prototype.min = function(v)
  235. {
  236. this.x = Math.min(this.x, v.x);
  237. this.y = Math.min(this.y, v.y);
  238. };
  239. /**
  240. * Set the maximum of x and y coordinates between two vectors.
  241. *
  242. * X is set as the max between this vector and the other vector.
  243. *
  244. * @param {Vector2} v
  245. */
  246. Vector2.prototype.max = function(v)
  247. {
  248. this.x = Math.max(this.x, v.x);
  249. this.y = Math.max(this.y, v.y);
  250. };
  251. Vector2.prototype.clamp = function(min, max)
  252. {
  253. // assumes min < max, componentwise
  254. this.x = Math.max(min.x, Math.min(max.x, this.x));
  255. this.y = Math.max(min.y, Math.min(max.y, this.y));
  256. };
  257. Vector2.prototype.clampScalar = function(minVal, maxVal)
  258. {
  259. this.x = Math.max(minVal, Math.min(maxVal, this.x));
  260. this.y = Math.max(minVal, Math.min(maxVal, this.y));
  261. };
  262. Vector2.prototype.clampLength = function(min, max)
  263. {
  264. var length = this.length();
  265. return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length)));
  266. };
  267. /**
  268. * Round the vector coordinates to integer by flooring to the smaller integer.
  269. */
  270. Vector2.prototype.floor = function()
  271. {
  272. this.x = Math.floor(this.x);
  273. this.y = Math.floor(this.y);
  274. };
  275. /**
  276. * Round the vector coordinates to integer by ceiling to the bigger integer.
  277. */
  278. Vector2.prototype.ceil = function()
  279. {
  280. this.x = Math.ceil(this.x);
  281. this.y = Math.ceil(this.y);
  282. };
  283. /**
  284. * Round the vector coordinates to their closest integer.
  285. */
  286. Vector2.prototype.round = function()
  287. {
  288. this.x = Math.round(this.x);
  289. this.y = Math.round(this.y);
  290. };
  291. /**
  292. * Negate the coordinates of this vector.
  293. */
  294. Vector2.prototype.negate = function()
  295. {
  296. this.x = -this.x;
  297. this.y = -this.y;
  298. return this;
  299. };
  300. /**
  301. * Dot multiplication between this vector and another vector.
  302. *
  303. * @param {Vector2} vector
  304. * @return {number} Result of the dot multiplication.
  305. */
  306. Vector2.prototype.dot = function(v)
  307. {
  308. return this.x * v.x + this.y * v.y;
  309. };
  310. /**
  311. * Cross multiplication between this vector and another vector.
  312. *
  313. * @param {Vector2} vector
  314. * @return {number} Result of the cross multiplication.
  315. */
  316. Vector2.prototype.cross = function(v)
  317. {
  318. return this.x * v.y - this.y * v.x;
  319. };
  320. /**
  321. * Squared length of the vector.
  322. *
  323. * Faster for comparions.
  324. */
  325. Vector2.prototype.lengthSq = function()
  326. {
  327. return this.x * this.x + this.y * this.y;
  328. };
  329. /**
  330. * Length of the vector.
  331. */
  332. Vector2.prototype.length = function()
  333. {
  334. return Math.sqrt(this.x * this.x + this.y * this.y);
  335. };
  336. /**
  337. * Manhattan length of the vector.
  338. */
  339. Vector2.prototype.manhattanLength = function()
  340. {
  341. return Math.abs(this.x) + Math.abs(this.y);
  342. };
  343. /**
  344. * Normalize the vector (make it length one).
  345. */
  346. Vector2.prototype.normalize = function()
  347. {
  348. return this.divideScalar(this.length() || 1);
  349. };
  350. /**
  351. * Computes the angle in radians with respect to the positive x-axis
  352. */
  353. Vector2.prototype.angle = function()
  354. {
  355. var angle = Math.atan2(this.y, this.x);
  356. if(angle < 0)
  357. {
  358. angle += 2 * Math.PI;
  359. }
  360. return angle;
  361. };
  362. /**
  363. * Distance between two vector positions.
  364. */
  365. Vector2.prototype.distanceTo = function(v)
  366. {
  367. return Math.sqrt(this.distanceToSquared(v));
  368. };
  369. /**
  370. * Distance between two vector positions squared.
  371. *
  372. * Faster for comparisons.
  373. */
  374. Vector2.prototype.distanceToSquared = function(v)
  375. {
  376. var dx = this.x - v.x;
  377. var dy = this.y - v.y;
  378. return dx * dx + dy * dy;
  379. };
  380. /**
  381. * Manhattan distance between two vector positions.
  382. */
  383. Vector2.prototype.manhattanDistanceTo = function(v)
  384. {
  385. return Math.abs(this.x - v.x) + Math.abs(this.y - v.y);
  386. };
  387. /**
  388. * Scale the vector to have a defined length value.
  389. */
  390. Vector2.prototype.setLength = function(length)
  391. {
  392. return this.normalize().multiplyScalar(length);
  393. };
  394. Vector2.prototype.lerp = function(v, alpha)
  395. {
  396. this.x += (v.x - this.x) * alpha;
  397. this.y += (v.y - this.y) * alpha;
  398. };
  399. Vector2.prototype.lerpVectors = function(v1, v2, alpha)
  400. {
  401. return this.subVectors(v2, v1).multiplyScalar(alpha).add(v1);
  402. };
  403. /**
  404. * Check if two vectors are equal.
  405. *
  406. * @param {Vector2} v
  407. */
  408. Vector2.prototype.equals = function(v)
  409. {
  410. return ((v.x === this.x) && (v.y === this.y));
  411. };
  412. /**
  413. * Set vector value from array with a offset.
  414. *
  415. * @param {array} array
  416. * @param {number} [offset]
  417. */
  418. Vector2.prototype.fromArray = function(array, offset)
  419. {
  420. if(offset === undefined) offset = 0;
  421. this.x = array[offset];
  422. this.y = array[offset + 1];
  423. };
  424. /**
  425. * Convert this vector to an array.
  426. *
  427. * @param {array} array
  428. * @param {number} [offset]
  429. */
  430. Vector2.prototype.toArray = function(array, offset)
  431. {
  432. if(array === undefined) array = [];
  433. if(offset === undefined) offset = 0;
  434. array[offset] = this.x;
  435. array[offset + 1] = this.y;
  436. return array;
  437. };
  438. /**
  439. * Rotate the vector around a central point.
  440. *
  441. * @param {Vector2} center
  442. * @param {number} angle
  443. */
  444. Vector2.prototype.rotateAround = function(center, angle)
  445. {
  446. var c = Math.cos(angle);
  447. var s = Math.sin(angle);
  448. var x = this.x - center.x;
  449. var y = this.y - center.y;
  450. this.x = x * c - y * s + center.x;
  451. this.y = x * s + y * c + center.y;
  452. };
  453. /**
  454. * 2D 3x2 transformation matrix, applied to the canvas elements.
  455. *
  456. * The values of the matrix are stored in a numeric array.
  457. *
  458. * @class
  459. * @param {array} [values]
  460. */
  461. function Matrix(values)
  462. {
  463. if(values !== undefined)
  464. {
  465. this.m = values;
  466. }
  467. else
  468. {
  469. this.identity();
  470. }
  471. }
  472. /**
  473. * Copy the content of another matrix and store in this one.
  474. *
  475. * @param {Matrix} mat
  476. */
  477. Matrix.prototype.copy = function(mat)
  478. {
  479. this.m = mat.m.slice(0);
  480. };
  481. /**
  482. * Create a new matrix object with a copy of the content of this one.
  483. */
  484. Matrix.prototype.clone = function()
  485. {
  486. return new Matrix(this.m.slice(0))
  487. };
  488. /**
  489. * Reset this matrix to indentity.
  490. */
  491. Matrix.prototype.identity = function()
  492. {
  493. this.m = [1, 0, 0, 1, 0, 0];
  494. };
  495. /**
  496. * Multiply another matrix by this one and store the result.
  497. *
  498. * @param {Matrix} mat
  499. */
  500. Matrix.prototype.multiply = function(mat)
  501. {
  502. var m0 = this.m[0] * mat.m[0] + this.m[2] * mat.m[1];
  503. var m1 = this.m[1] * mat.m[0] + this.m[3] * mat.m[1];
  504. var m2 = this.m[0] * mat.m[2] + this.m[2] * mat.m[3];
  505. var m3 = this.m[1] * mat.m[2] + this.m[3] * mat.m[3];
  506. var m4 = this.m[0] * mat.m[4] + this.m[2] * mat.m[5] + this.m[4];
  507. var m5 = this.m[1] * mat.m[4] + this.m[3] * mat.m[5] + this.m[5];
  508. this.m = [m0, m1, m2, m3, m4, m5];
  509. };
  510. /**
  511. * Premultiply another matrix by this one and store the result.
  512. *
  513. * @param {Matrix} mat
  514. */
  515. Matrix.prototype.premultiply = function(mat)
  516. {
  517. var m0 = mat.m[0] * this.m[0] + mat.m[2] * this.m[1];
  518. var m1 = mat.m[1] * this.m[0] + mat.m[3] * this.m[1];
  519. var m2 = mat.m[0] * this.m[2] + mat.m[2] * this.m[3];
  520. var m3 = mat.m[1] * this.m[2] + mat.m[3] * this.m[3];
  521. var m4 = mat.m[0] * this.m[4] + mat.m[2] * this.m[5] + mat.m[4];
  522. var m5 = mat.m[1] * this.m[4] + mat.m[3] * this.m[5] + mat.m[5];
  523. this.m = [m0, m1, m2, m3, m4, m5];
  524. };
  525. /**
  526. * Compose this transformation matrix with position scale and rotation and origin point.
  527. *
  528. * @param {number} px Position X
  529. * @param {number} py Position Y
  530. * @param {number} sx Scale X
  531. * @param {number} sy Scale Y
  532. * @param {number} ox Origin X (applied before scale and rotation)
  533. * @param {number} oy Origin Y (applied before scale and rotation)
  534. * @param {number} a Rotation angle (radians).
  535. */
  536. Matrix.prototype.compose = function(px, py, sx, sy, ox, oy, a)
  537. {
  538. this.m = [1, 0, 0, 1, px, py];
  539. if(a !== 0)
  540. {
  541. var c = Math.cos(a);
  542. var s = Math.sin(a);
  543. this.multiply(new Matrix([c, s, -s, c, 0, 0]));
  544. }
  545. if(sx !== 1 || sy !== 1)
  546. {
  547. this.scale(sx, sy);
  548. }
  549. if(ox !== 0 || oy !== 0)
  550. {
  551. this.multiply(new Matrix([1, 0, 0, 1, -ox, -oy]));
  552. }
  553. };
  554. /**
  555. * Apply translation to this matrix.
  556. *
  557. * @param {number} x
  558. * @param {number} y
  559. */
  560. Matrix.prototype.translate = function(x, y)
  561. {
  562. this.m[4] += this.m[0] * x + this.m[2] * y;
  563. this.m[5] += this.m[1] * x + this.m[3] * y;
  564. };
  565. /**
  566. * Apply rotation to this matrix.
  567. *
  568. * @param {number} angle Angle in radians.
  569. */
  570. Matrix.prototype.rotate = function(rad)
  571. {
  572. var c = Math.cos(rad);
  573. var s = Math.sin(rad);
  574. var m11 = this.m[0] * c + this.m[2] * s;
  575. var m12 = this.m[1] * c + this.m[3] * s;
  576. var m21 = this.m[0] * -s + this.m[2] * c;
  577. var m22 = this.m[1] * -s + this.m[3] * c;
  578. this.m[0] = m11;
  579. this.m[1] = m12;
  580. this.m[2] = m21;
  581. this.m[3] = m22;
  582. };
  583. /**
  584. * Apply scale to this matrix.
  585. *
  586. * @param {number} sx
  587. * @param {number} sy
  588. */
  589. Matrix.prototype.scale = function(sx, sy)
  590. {
  591. this.m[0] *= sx;
  592. this.m[1] *= sx;
  593. this.m[2] *= sy;
  594. this.m[3] *= sy;
  595. };
  596. /**
  597. * Set the position of the transformation matrix.
  598. *
  599. * @param {number} x
  600. * @param {number} y
  601. */
  602. Matrix.prototype.setPosition = function(x, y)
  603. {
  604. this.m[4] = x;
  605. this.m[5] = y;
  606. };
  607. /**
  608. * Get the scale from the transformation matrix.
  609. *
  610. * @return {Vector2} Scale of the matrix transformation.
  611. */
  612. Matrix.prototype.getScale = function()
  613. {
  614. return new Vector2(this.m[0], this.m[3]);
  615. };
  616. /**
  617. * Get the position from the transformation matrix.
  618. *
  619. * @return {Vector2} Position of the matrix transformation.
  620. */
  621. Matrix.prototype.getPosition = function()
  622. {
  623. return new Vector2(this.m[4], this.m[5]);
  624. };
  625. /**
  626. * Apply skew to this matrix.
  627. */
  628. Matrix.prototype.skew = function(radianX, radianY)
  629. {
  630. this.multiply(new Matrix([1, Math.tan(radianY), Math.tan(radianX), 1, 0, 0]));
  631. };
  632. /**
  633. * Get the matrix determinant.
  634. */
  635. Matrix.prototype.determinant = function()
  636. {
  637. return 1 / (this.m[0] * this.m[3] - this.m[1] * this.m[2]);
  638. };
  639. /**
  640. * Get the inverse matrix.
  641. */
  642. Matrix.prototype.getInverse = function()
  643. {
  644. var d = this.determinant();
  645. 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])]);
  646. };
  647. /**
  648. * Transform a point using this matrix.
  649. */
  650. Matrix.prototype.transformPoint = function(p)
  651. {
  652. var px = p.x * this.m[0] + p.y * this.m[2] + this.m[4];
  653. var py = p.x * this.m[1] + p.y * this.m[3] + this.m[5];
  654. return new Vector2(px, py);
  655. };
  656. /**
  657. * Set a canvas context to use this transformation.
  658. */
  659. Matrix.prototype.setContextTransform = function(context)
  660. {
  661. context.setTransform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  662. };
  663. /**
  664. * Transform on top of the current context transformation.
  665. */
  666. Matrix.prototype.tranformContext = function(context)
  667. {
  668. context.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  669. };
  670. Matrix.prototype.cssTransform = function()
  671. {
  672. return "matrix(" + this.m[0] + "," + this.m[1] + "," + this.m[2] + "," + this.m[3] + "," + this.m[4] + "," + this.m[5] + ")";
  673. };
  674. /**
  675. * Class to implement UUID generation methods.
  676. *
  677. * @class
  678. */
  679. function UUID(){}
  680. /**
  681. * Generate new random UUID v4 as string.
  682. *
  683. * http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  684. *
  685. * @static
  686. */
  687. UUID.generate = (function ()
  688. {
  689. var lut = [];
  690. for(var i = 0; i < 256; i++)
  691. {
  692. lut[i] = (i < 16 ? "0" : "") + (i).toString(16);
  693. }
  694. return function generateUUID()
  695. {
  696. var d0 = Math.random() * 0XFFFFFFFF | 0;
  697. var d1 = Math.random() * 0XFFFFFFFF | 0;
  698. var d2 = Math.random() * 0XFFFFFFFF | 0;
  699. var d3 = Math.random() * 0XFFFFFFFF | 0;
  700. var uuid = lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + "-" +
  701. lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + "-" + lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + "-" +
  702. lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + "-" + lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] +
  703. lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] + lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff];
  704. return uuid.toUpperCase();
  705. };
  706. })();
  707. /**
  708. * Base object class, implements all the object positioning and scalling features.
  709. *
  710. * Stores all the base properties shared between all objects as the position, transformation properties, children etc.
  711. *
  712. * Object2D should be used as a group to store all the other objects drawn.
  713. *
  714. * @class
  715. */
  716. function Object2D()
  717. {
  718. /**
  719. * UUID of the object.
  720. */
  721. this.uuid = UUID.generate();
  722. /**
  723. * List of children objects attached to the object.
  724. */
  725. this.children = [];
  726. /**
  727. * Parent object, the object position is affected by its parent position.
  728. */
  729. this.parent = null;
  730. /**
  731. * Depth level in the object tree, objects with higher depth are drawn on top.
  732. *
  733. * The layer value is considered first.
  734. */
  735. this.level = 0;
  736. /**
  737. * Position of the object.
  738. */
  739. this.position = new Vector2(0, 0);
  740. /**
  741. * Origin of the object used as point of rotation.
  742. */
  743. this.origin = new Vector2(0, 0);
  744. /**
  745. * Scale of the object.
  746. */
  747. this.scale = new Vector2(1, 1);
  748. /**
  749. * Rotation of the object relative to its center.
  750. */
  751. this.rotation = 0.0;
  752. /**
  753. * Indicates if the object is visible.
  754. */
  755. this.visible = true;
  756. /**
  757. * Layer of this object, objects are sorted by layer value.
  758. *
  759. * Lower layer value is draw first.
  760. */
  761. this.layer = 0;
  762. /**
  763. * Local transformation matrix applied to the object.
  764. */
  765. this.matrix = new Matrix();
  766. /**
  767. * Global transformation matrix multiplied by the parent matrix.
  768. *
  769. * Used to transform the object before projecting into screen coordinates.
  770. */
  771. this.globalMatrix = new Matrix();
  772. /**
  773. * Inverse of the global matrix.
  774. *
  775. * Used to convert pointer input points into object coordinates.
  776. */
  777. this.inverseGlobalMatrix = new Matrix();
  778. /**
  779. * Masks being applied to this object.
  780. *
  781. * Multiple masks can be used simultaneously.
  782. */
  783. this.masks = [];
  784. /**
  785. * If true the matrix is updated before rendering the object.
  786. */
  787. this.matrixNeedsUpdate = true;
  788. /**
  789. * Indicates if its possible to drag the object around.
  790. *
  791. * If true the onPointerDrag callback is used to update the state of the object.
  792. */
  793. this.draggable = false;
  794. /**
  795. * Indicates if this object uses pointer events.
  796. *
  797. * Can be set false to skip the pointer interaction events.
  798. */
  799. this.pointerEvents = true;
  800. /**
  801. * Flag to indicate wheter this objet ignores the viewport transformation.
  802. */
  803. this.ignoreViewport = false;
  804. /**
  805. * Flag to indicate if the context of canvas should be saved before render.
  806. */
  807. this.saveContextState = true;
  808. /**
  809. * Flag to indicate if the context of canvas should be restored after render.
  810. */
  811. this.restoreContextState = true;
  812. /**
  813. * Flag indicating if the pointer is inside of the element.
  814. *
  815. * Used to control object event.
  816. */
  817. this.pointerInside = false;
  818. /**
  819. * Flag to indicate if the object is currently being dragged.
  820. */
  821. this.beingDragged = false;
  822. }
  823. /**
  824. * Traverse the object tree and run a function for all objects.
  825. *
  826. * @param {Function} callback Callback function that receives the object as parameter.
  827. */
  828. Object2D.prototype.traverse = function(callback)
  829. {
  830. callback(this);
  831. var children = this.children;
  832. for(var i = 0; i < children.length; i++)
  833. {
  834. children[i].traverse(callback);
  835. }
  836. };
  837. /**
  838. * Get a object from its children list by its UUID.
  839. *
  840. * @param {String} uuid UUID of the object to get.
  841. * @return {Object2D} The object that has the UUID specified, null if the object was not found.
  842. */
  843. Object2D.prototype.getChildByUUID = function(uuid)
  844. {
  845. var object = null;
  846. this.traverse(function(child)
  847. {
  848. if(child.uuid === uuid)
  849. {
  850. object = child;
  851. }
  852. });
  853. return object;
  854. };
  855. /**
  856. * Attach a children to this object.
  857. *
  858. * The object is set as children of this object and the transformations applied to this object are traversed to its children.
  859. *
  860. * @param {Object2D} object Object to attach to this object.
  861. */
  862. Object2D.prototype.add = function(object)
  863. {
  864. object.parent = this;
  865. object.level = this.level + 1;
  866. object.traverse(function(child)
  867. {
  868. if(child.onAdd !== null)
  869. {
  870. child.onAdd(this);
  871. }
  872. });
  873. this.children.push(object);
  874. };
  875. /**
  876. * Remove object from the children list.
  877. *
  878. * @param {Object2D} object Object to be removed.
  879. */
  880. Object2D.prototype.remove = function(object)
  881. {
  882. var index = this.children.indexOf(object);
  883. if(index !== -1)
  884. {
  885. var object = this.children[index];
  886. object.parent = null;
  887. object.level = 0;
  888. object.traverse(function(child)
  889. {
  890. if(child.onRemove !== null)
  891. {
  892. child.onRemove(this);
  893. }
  894. });
  895. this.children.splice(index, 1);
  896. }
  897. };
  898. /**
  899. * Check if a point is inside of the object.
  900. *
  901. * Used to update the point events attached to the object.
  902. *
  903. * @return {boolean} True if the point is inside of the object.
  904. */
  905. Object2D.prototype.isInside = function(point)
  906. {
  907. return false;
  908. };
  909. /**
  910. * Update the transformation matrix of the object.
  911. *
  912. * @param {CanvasContext} context
  913. */
  914. Object2D.prototype.updateMatrix = function(context)
  915. {
  916. if(this.matrixNeedsUpdate)
  917. {
  918. this.matrix.compose(this.position.x, this.position.y, this.scale.x, this.scale.y, this.origin.x, this.origin.y, this.rotation);
  919. this.globalMatrix.copy(this.matrix);
  920. if(this.parent !== null)
  921. {
  922. this.globalMatrix.premultiply(this.parent.globalMatrix);
  923. }
  924. this.inverseGlobalMatrix = this.globalMatrix.getInverse();
  925. //this.matrixNeedsUpdate = false;
  926. }
  927. };
  928. /**
  929. * Apply the transform to the rendering context.
  930. *
  931. * It is assumed that the viewport transform is pre-applied to the context.
  932. *
  933. * Can also be used for pre rendering logic.
  934. *
  935. * @param {CanvasContext} context Canvas 2d drawing context.
  936. * @param {Viewport} viewport Viewport applied to the canvas.
  937. */
  938. Object2D.prototype.transform = function(context, viewport)
  939. {
  940. this.globalMatrix.tranformContext(context);
  941. };
  942. /**
  943. * Draw the object into the canvas.
  944. *
  945. * Has to be implemented by underlying classes.
  946. *
  947. * @param {CanvasContext} context Canvas 2d drawing context.
  948. * @param {Viewport} viewport Viewport applied to the canvas.
  949. * @param {DOM} canvas DOM canvas element where the content is being drawn.
  950. */
  951. Object2D.prototype.draw = function(context, viewport, canvas){};
  952. /**
  953. * Callback method while the object is being dragged across the screen.
  954. *
  955. * By default is adds the delta value to the object position (making it follow the mouse movement).
  956. *
  957. * Delta is the movement of the pointer already translated into local object coordinates.
  958. *
  959. * Receives (pointer, viewport, delta) as arguments.
  960. *
  961. * @param {Pointer} pointer Pointer object that receives the user input.
  962. * @param {Viewport} viewport Viewport where the object is drawn.
  963. * @param {Vector2} delta Pointer movement in world space.
  964. */
  965. Object2D.prototype.onPointerDrag = function(pointer, viewport, delta)
  966. {
  967. this.position.add(delta);
  968. };
  969. /**
  970. * Method called when the object its added to a parent.
  971. *
  972. * @param {Object2D} parent Parent object were it was added.
  973. */
  974. Object2D.prototype.onAdd = null;
  975. /**
  976. * Method called when the object gets removed from its parent
  977. *
  978. * @param {Object2D} parent Parent object from were the object is being removed.
  979. */
  980. Object2D.prototype.onRemove = null;
  981. /**
  982. * Callback method called every time before the object is draw into the canvas.
  983. *
  984. * Can be used to run preparation code, move the object, etc.
  985. */
  986. Object2D.prototype.onUpdate = null;
  987. /**
  988. * Callback method called when the pointer enters the object.
  989. *
  990. * Receives (pointer, viewport) as arguments.
  991. *
  992. * @param {Pointer} pointer Pointer object that receives the user input.
  993. * @param {Viewport} viewport Viewport where the object is drawn.
  994. */
  995. Object2D.prototype.onPointerEnter = null;
  996. /**
  997. * Callback method called when the was inside of the object and leaves the object.
  998. *
  999. * Receives (pointer, viewport) as arguments.
  1000. *
  1001. * @param {Pointer} pointer Pointer object that receives the user input.
  1002. * @param {Viewport} viewport Viewport where the object is drawn.
  1003. */
  1004. Object2D.prototype.onPointerLeave = null;
  1005. /**
  1006. * Callback method while the pointer is over (inside) of the object.
  1007. *
  1008. * Receives (pointer, viewport) as arguments.
  1009. *
  1010. * @param {Pointer} pointer Pointer object that receives the user input.
  1011. * @param {Viewport} viewport Viewport where the object is drawn.
  1012. */
  1013. Object2D.prototype.onPointerOver = null;
  1014. /**
  1015. * Callback method called while the pointer button is pressed.
  1016. *
  1017. * Receives (pointer, viewport) as arguments.
  1018. *
  1019. * @param {Pointer} pointer Pointer object that receives the user input.
  1020. * @param {Viewport} viewport Viewport where the object is drawn.
  1021. */
  1022. Object2D.prototype.onButtonPressed = null;
  1023. /**
  1024. * Callback method called while the pointer button is double clicked.
  1025. *
  1026. * Receives (pointer, viewport) as arguments.
  1027. *
  1028. * @param {Pointer} pointer Pointer object that receives the user input.
  1029. * @param {Viewport} viewport Viewport where the object is drawn.
  1030. */
  1031. Object2D.prototype.onDoubleClick = null;
  1032. /**
  1033. * Callback method called when the pointer button is pressed down (single time).
  1034. *
  1035. * Receives (pointer, viewport) as arguments.
  1036. *
  1037. * @param {Pointer} pointer Pointer object that receives the user input.
  1038. * @param {Viewport} viewport Viewport where the object is drawn.
  1039. */
  1040. Object2D.prototype.onButtonDown = null;
  1041. /**
  1042. * Callback method called when the pointer button is released (single time).
  1043. *
  1044. * Receives (pointer, viewport) as arguments.
  1045. *
  1046. * @param {Pointer} pointer Pointer object that receives the user input.
  1047. * @param {Viewport} viewport Viewport where the object is drawn.
  1048. */
  1049. Object2D.prototype.onButtonUp = null;
  1050. /**
  1051. * Key is used by Keyboard, Pointer, etc, to represent a key state.
  1052. *
  1053. * @class
  1054. */
  1055. function Key()
  1056. {
  1057. /**
  1058. * Indicates if this key is currently pressed.
  1059. */
  1060. this.pressed = false;
  1061. /**
  1062. * Indicates if this key was just pressed.
  1063. */
  1064. this.justPressed = false;
  1065. /**
  1066. * Indicates if this key was just released.
  1067. */
  1068. this.justReleased = false;
  1069. }
  1070. Key.DOWN = -1;
  1071. Key.UP = 1;
  1072. Key.RESET = 0;
  1073. Key.prototype.constructor = Key;
  1074. /**
  1075. * Update Key status based on new key state.
  1076. */
  1077. Key.prototype.update = function(action)
  1078. {
  1079. this.justPressed = false;
  1080. this.justReleased = false;
  1081. if(action === Key.DOWN)
  1082. {
  1083. if(this.pressed === false)
  1084. {
  1085. this.justPressed = true;
  1086. }
  1087. this.pressed = true;
  1088. }
  1089. else if(action === Key.UP)
  1090. {
  1091. if(this.pressed)
  1092. {
  1093. this.justReleased = true;
  1094. }
  1095. this.pressed = false;
  1096. }
  1097. else if(action === Key.RESET)
  1098. {
  1099. this.justReleased = false;
  1100. this.justPressed = false;
  1101. }
  1102. };
  1103. /**
  1104. * Set this key attributes manually.
  1105. */
  1106. Key.prototype.set = function(justPressed, pressed, justReleased)
  1107. {
  1108. this.justPressed = justPressed;
  1109. this.pressed = pressed;
  1110. this.justReleased = justReleased;
  1111. };
  1112. /**
  1113. * Reset key to default values.
  1114. */
  1115. Key.prototype.reset = function()
  1116. {
  1117. this.justPressed = false;
  1118. this.pressed = false;
  1119. this.justReleased = false;
  1120. };
  1121. /**
  1122. * Pointer object is used to colled input from the user, works for booth mouse or touch screens.
  1123. *
  1124. * It is responsible for syncronizing user input with the render of the graphics.
  1125. *
  1126. * @class
  1127. * @param {DOM} domElement DOM element to craete the pointer events.
  1128. * @param {DOM} canvas Canvas DOM element where the content is being drawn.
  1129. */
  1130. function Pointer(domElement, canvas)
  1131. {
  1132. //Raw data
  1133. this._keys = new Array(5);
  1134. this._position = new Vector2(0, 0);
  1135. this._positionUpdated = false;
  1136. this._delta = new Vector2(0, 0);
  1137. this._wheel = 0;
  1138. this._wheelUpdated = false;
  1139. this._doubleClicked = new Array(5);
  1140. /**
  1141. * Array with pointer buttons status.
  1142. */
  1143. this.keys = new Array(5);
  1144. /**
  1145. * Pointer position inside of the window (coordinates in window space).
  1146. */
  1147. this.position = new Vector2(0, 0);
  1148. /**
  1149. * Pointer movement (coordinates in window space).
  1150. */
  1151. this.delta = new Vector2(0, 0);
  1152. /**
  1153. * Pointer scroll wheel movement.
  1154. */
  1155. this.wheel = 0;
  1156. /**
  1157. * Indicates a button of the pointer was double clicked.
  1158. */
  1159. this.doubleClicked = new Array(5);
  1160. /**
  1161. * DOM element where to attach the pointer events.
  1162. */
  1163. this.domElement = (domElement !== undefined) ? domElement : window;
  1164. /**
  1165. * Canvas attached to this pointer instance used to calculate position and delta in element space coordinates.
  1166. */
  1167. this.canvas = null;
  1168. if(canvas !== undefined)
  1169. {
  1170. this.setCanvas(canvas);
  1171. }
  1172. /**
  1173. * Event manager responsible for updating the raw data variables.
  1174. *
  1175. * Diferent events are used depending on the host platform.
  1176. *
  1177. * When the update method is called the raw data is reset.
  1178. */
  1179. this.events = new EventManager();
  1180. //Initialize key instances
  1181. for(var i = 0; i < 5; i++)
  1182. {
  1183. this._doubleClicked[i] = false;
  1184. this.doubleClicked[i] = false;
  1185. this._keys[i] = new Key();
  1186. this.keys[i] = new Key();
  1187. }
  1188. //Self pointer
  1189. var self = this;
  1190. //Scroll wheel
  1191. if(window.onmousewheel !== undefined)
  1192. {
  1193. //Chrome, edge
  1194. this.events.add(this.domElement, "mousewheel", function(event)
  1195. {
  1196. self._wheel = event.deltaY;
  1197. self._wheelUpdated = true;
  1198. });
  1199. }
  1200. else if(window.addEventListener !== undefined)
  1201. {
  1202. //Firefox
  1203. this.events.add(this.domElement, "DOMMouseScroll", function(event)
  1204. {
  1205. self._wheel = event.detail * 30;
  1206. self._wheelUpdated = true;
  1207. });
  1208. }
  1209. else
  1210. {
  1211. this.events.add(this.domElement, "wheel", function(event)
  1212. {
  1213. self._wheel = event.deltaY;
  1214. self._wheelUpdated = true;
  1215. });
  1216. }
  1217. //Touchscreen input events
  1218. if(window.ontouchstart !== undefined || navigator.msMaxTouchPoints > 0)
  1219. {
  1220. //Auxiliar variables to calculate touch delta
  1221. var lastTouch = new Vector2(0, 0);
  1222. //Touch start event
  1223. this.events.add(this.domElement, "touchstart", function(event)
  1224. {
  1225. var touch = event.touches[0];
  1226. self.updatePosition(touch.clientX, touch.clientY, 0, 0);
  1227. self.updateKey(Pointer.LEFT, Key.DOWN);
  1228. lastTouch.set(touch.clientX, touch.clientY);
  1229. });
  1230. //Touch end event
  1231. this.events.add(this.domElement, "touchend", function(event)
  1232. {
  1233. self.updateKey(Pointer.LEFT, Key.UP);
  1234. });
  1235. //Touch cancel event
  1236. this.events.add(this.domElement, "touchcancel", function(event)
  1237. {
  1238. self.updateKey(Pointer.LEFT, Key.UP);
  1239. });
  1240. //Touch move event
  1241. this.events.add(document.body, "touchmove", function(event)
  1242. {
  1243. var touch = event.touches[0];
  1244. self.updatePosition(touch.clientX, touch.clientY, touch.clientX - lastTouch.x, touch.clientY - lastTouch.y);
  1245. lastTouch.set(touch.clientX, touch.clientY);
  1246. });
  1247. }
  1248. //Move
  1249. this.events.add(this.domElement, "mousemove", function(event)
  1250. {
  1251. self.updatePosition(event.clientX, event.clientY, event.movementX, event.movementY);
  1252. });
  1253. //Button pressed
  1254. this.events.add(this.domElement, "mousedown", function(event)
  1255. {
  1256. self.updateKey(event.which - 1, Key.DOWN);
  1257. });
  1258. //Button released
  1259. this.events.add(this.domElement, "mouseup", function(event)
  1260. {
  1261. self.updateKey(event.which - 1, Key.UP);
  1262. });
  1263. //Drag start
  1264. this.events.add(this.domElement, "dragstart", function(event)
  1265. {
  1266. self.updateKey(event.which - 1, Key.UP);
  1267. });
  1268. //Pointer double click
  1269. this.events.add(this.domElement, "dblclick", function(event)
  1270. {
  1271. self._doubleClicked[event.which - 1] = true;
  1272. });
  1273. this.create();
  1274. }
  1275. Pointer.prototype = Pointer;
  1276. Pointer.prototype.constructor = Pointer;
  1277. /**
  1278. * Left pointer button.
  1279. */
  1280. Pointer.LEFT = 0;
  1281. /**
  1282. * Middle pointer button.
  1283. */
  1284. Pointer.MIDDLE = 1;
  1285. /**
  1286. * Right pointer button.
  1287. */
  1288. Pointer.RIGHT = 2;
  1289. /**
  1290. * Back pointer navigation button.
  1291. */
  1292. Pointer.BACK = 3;
  1293. /**
  1294. * Forward pointer navigation button.
  1295. */
  1296. Pointer.FORWARD = 4;
  1297. /**
  1298. * Element to be used for coordinates calculation relative to that canvas.
  1299. *
  1300. * @param {DOM} canvas Canvas to be attached to the Pointer instance
  1301. */
  1302. Pointer.setCanvas = function(element)
  1303. {
  1304. this.canvas = element;
  1305. element.pointerInside = false;
  1306. element.addEventListener("mouseenter", function()
  1307. {
  1308. this.pointerInside = true;
  1309. });
  1310. element.addEventListener("mouseleave", function()
  1311. {
  1312. this.pointerInside = false;
  1313. });
  1314. };
  1315. /**
  1316. * Check if pointer is inside attached canvas (updated async).
  1317. *
  1318. * @return {boolean} True if pointer is currently inside the canvas
  1319. */
  1320. Pointer.insideCanvas = function()
  1321. {
  1322. return this.canvas !== null && this.canvas.pointerInside;
  1323. };
  1324. /**
  1325. * Check if pointer button is currently pressed.
  1326. *
  1327. * @param {Number} button Button to check status of
  1328. * @return {boolean} True if button is currently pressed
  1329. */
  1330. Pointer.buttonPressed = function(button)
  1331. {
  1332. return this.keys[button].pressed;
  1333. };
  1334. /**
  1335. * Check if pointer button was double clicked.
  1336. *
  1337. * @param {Number} button Button to check status of
  1338. * @return {boolean} True if some pointer button was just double clicked
  1339. */
  1340. Pointer.buttonDoubleClicked = function(button)
  1341. {
  1342. return this.doubleClicked[button];
  1343. };
  1344. /**
  1345. * Check if a pointer button was just pressed.
  1346. *
  1347. * @param {Number} button Button to check status of
  1348. * @return {boolean} True if button was just pressed
  1349. */
  1350. Pointer.buttonJustPressed = function(button)
  1351. {
  1352. return this.keys[button].justPressed;
  1353. };
  1354. /**
  1355. * Check if a pointer button was just released.
  1356. *
  1357. * @param {Number} button Button to check status of
  1358. * @return {boolean} True if button was just released
  1359. */
  1360. Pointer.buttonJustReleased = function(button)
  1361. {
  1362. return this.keys[button].justReleased;
  1363. };
  1364. /**
  1365. * Update pointer position.
  1366. *
  1367. * Automatically called by the runtime.
  1368. *
  1369. * @param {Number} x
  1370. * @param {Number} y
  1371. * @param {Number} xDiff
  1372. * @param {Number} yDiff
  1373. */
  1374. Pointer.updatePosition = function(x, y, xDiff, yDiff)
  1375. {
  1376. if(this.canvas !== null)
  1377. {
  1378. var rect = this.canvas.getBoundingClientRect();
  1379. x -= rect.left;
  1380. y -= rect.top;
  1381. }
  1382. this._position.set(x, y);
  1383. this._delta.x += xDiff;
  1384. this._delta.y += yDiff;
  1385. this._positionUpdated = true;
  1386. };
  1387. /**
  1388. * Update a pointer button.
  1389. *
  1390. * Automatically called by the runtime.
  1391. *
  1392. * @param {Number} button
  1393. * @param {Number} action
  1394. */
  1395. Pointer.updateKey = function(button, action)
  1396. {
  1397. if(button > -1)
  1398. {
  1399. this._keys[button].update(action);
  1400. }
  1401. };
  1402. /**
  1403. * Update pointer buttons state, position, wheel and delta synchronously.
  1404. */
  1405. Pointer.update = function()
  1406. {
  1407. //Update pointer keys state
  1408. for(var i = 0; i < 5; i++)
  1409. {
  1410. if(this._keys[i].justPressed && this.keys[i].justPressed)
  1411. {
  1412. this._keys[i].justPressed = false;
  1413. }
  1414. if(this._keys[i].justReleased && this.keys[i].justReleased)
  1415. {
  1416. this._keys[i].justReleased = false;
  1417. }
  1418. this.keys[i].set(this._keys[i].justPressed, this._keys[i].pressed, this._keys[i].justReleased);
  1419. //Update pointer double click
  1420. if(this._doubleClicked[i] === true)
  1421. {
  1422. this.doubleClicked[i] = true;
  1423. this._doubleClicked[i] = false;
  1424. }
  1425. else
  1426. {
  1427. this.doubleClicked[i] = false;
  1428. }
  1429. }
  1430. //Update pointer wheel
  1431. if(this._wheelUpdated)
  1432. {
  1433. this.wheel = this._wheel;
  1434. this._wheelUpdated = false;
  1435. }
  1436. else
  1437. {
  1438. this.wheel = 0;
  1439. }
  1440. //Update pointer Position if needed
  1441. if(this._positionUpdated)
  1442. {
  1443. this.delta.copy(this._delta);
  1444. this.position.copy(this._position);
  1445. this._delta.set(0,0);
  1446. this._positionUpdated = false;
  1447. }
  1448. else
  1449. {
  1450. this.delta.x = 0;
  1451. this.delta.y = 0;
  1452. }
  1453. };
  1454. /**
  1455. * Create pointer events.
  1456. */
  1457. Pointer.create = function()
  1458. {
  1459. this.events.create();
  1460. };
  1461. /**
  1462. * Dispose pointer events.
  1463. */
  1464. Pointer.dispose = function()
  1465. {
  1466. this.events.destroy();
  1467. };
  1468. /**
  1469. * Used to indicate how the user views the content inside of the canvas.
  1470. *
  1471. * @class
  1472. * @param {DOM} canvas Canvas DOM element where the viewport is being rendered.
  1473. */
  1474. function Viewport(canvas)
  1475. {
  1476. /**
  1477. * UUID of the object.
  1478. */
  1479. this.uuid = UUID.generate();
  1480. /**
  1481. * Canvas DOM element where the viewport is being rendered.
  1482. */
  1483. this.canvas = canvas;
  1484. /**
  1485. * Position of the object.
  1486. */
  1487. this.position = new Vector2(0, 0);
  1488. /**
  1489. * Scale of the object.
  1490. */
  1491. this.scale = 1.0;
  1492. /**
  1493. * Rotation of the object relative to its center.
  1494. */
  1495. this.rotation = 0.0;
  1496. /**
  1497. * Local transformation matrix applied to the object.
  1498. */
  1499. this.matrix = new Matrix();
  1500. /**
  1501. * Inverse of the local transformation matrix.
  1502. */
  1503. this.inverseMatrix = new Matrix();
  1504. /**
  1505. * If true the matrix is updated before rendering the object.
  1506. */
  1507. this.matrixNeedsUpdate = true;
  1508. /**
  1509. * Flag to indicate if the viewport should move when scalling.
  1510. *
  1511. * For some application its easier to focus the target if the viewport moves to the pointer location while scalling.
  1512. */
  1513. this.moveOnScale = false;
  1514. /**
  1515. * Value of the initial point of rotation if the viewport is being rotated.
  1516. *
  1517. * Is set to null when the viewport is not being rotated.
  1518. */
  1519. this.rotationPoint = null;
  1520. }
  1521. /**
  1522. * Calculate and update the viewport transformation matrix.
  1523. *
  1524. * Also updates the inverse matrix of the viewport.
  1525. */
  1526. Viewport.prototype.updateMatrix = function()
  1527. {
  1528. if(this.matrixNeedsUpdate)
  1529. {
  1530. this.matrix.m = [1, 0, 0, 1, this.position.x, this.position.y];
  1531. if(this.rotation !== 0)
  1532. {
  1533. var c = Math.cos(this.rotation);
  1534. var s = Math.sin(this.rotation);
  1535. this.matrix.multiply(new Matrix([c, s, -s, c, 0, 0]));
  1536. }
  1537. if(this.scale !== 1)
  1538. {
  1539. this.matrix.scale(this.scale, this.scale);
  1540. }
  1541. this.inverseMatrix = this.matrix.getInverse();
  1542. this.matrixNeedsUpdate = false;
  1543. }
  1544. };
  1545. /**
  1546. * Center the viewport relative to a object.
  1547. *
  1548. * The position of the object is used a central point, this method does not consider "box" attributes or other strucures in the object.
  1549. *
  1550. * @param {Object2D} object Object to be centered on the viewport.
  1551. * @param {DOM} canvas Canvas element where the image is drawn.
  1552. */
  1553. Viewport.prototype.centerObject = function(object, canvas)
  1554. {
  1555. var position = object.globalMatrix.transformPoint(new Vector2());
  1556. position.multiplyScalar(-this.scale);
  1557. position.x += canvas.width / 2;
  1558. position.y += canvas.height / 2;
  1559. this.position.copy(position);
  1560. this.matrixNeedsUpdate = true;
  1561. };
  1562. /**
  1563. * Viewport controls are used to allow the user to control the viewport.
  1564. *
  1565. * @class
  1566. * @param {Viewport} viewport
  1567. */
  1568. function ViewportControls(viewport)
  1569. {
  1570. /**
  1571. * Viewport being controlled by this object.
  1572. */
  1573. this.viewport = viewport;
  1574. /**
  1575. * Button used to drag and viewport around.
  1576. *
  1577. * On touch enabled devices the touch event is represented as a LEFT button.
  1578. */
  1579. this.dragButton = Pointer.RIGHT;
  1580. /**
  1581. * Is set to true allow the viewport to be scalled.
  1582. *
  1583. * Scaling is performed using the pointer scroll.
  1584. */
  1585. this.allowScale = true;
  1586. /**
  1587. * Flag to indicate if the viewport should move when scalling.
  1588. *
  1589. * For some application its easier to focus the target if the viewport moves to the pointer location while scalling.
  1590. */
  1591. this.moveOnScale = false;
  1592. /**
  1593. * If true allows the viewport to be rotated.
  1594. *
  1595. * Rotation is performed by holding the RIGHT and LEFT pointer buttons and rotating around the initial point.
  1596. */
  1597. this.allowRotation = true;
  1598. /**
  1599. * Value of the initial point of rotation if the viewport is being rotated.
  1600. *
  1601. * Is set to null when the viewport is not being rotated.
  1602. */
  1603. this.rotationPoint = null;
  1604. /**
  1605. * Initial rotation of the viewport.
  1606. */
  1607. this.rotationInitial = 0;
  1608. }
  1609. /**
  1610. * Update the viewport controls using the pointer object.
  1611. *
  1612. * Should be called every frame before rendering.
  1613. *
  1614. * @param {Pointer} pointer
  1615. */
  1616. ViewportControls.prototype.update = function(pointer)
  1617. {
  1618. // Scale
  1619. if(this.allowScale && pointer.wheel !== 0)
  1620. {
  1621. var scale = pointer.wheel * 1e-3 * this.viewport.scale;
  1622. this.viewport.scale -= scale;
  1623. this.viewport.matrixNeedsUpdate = true;
  1624. // Move on scale
  1625. if(this.moveOnScale && pointer.canvas !== null)
  1626. {
  1627. this.viewport.updateMatrix();
  1628. var pointerWorld = this.viewport.inverseMatrix.transformPoint(pointer.position);
  1629. var centerWorld = new Vector2(pointer.canvas.width / 2.0, pointer.canvas.height / 2.0);
  1630. centerWorld = this.viewport.inverseMatrix.transformPoint(centerWorld);
  1631. var delta = pointerWorld.clone();
  1632. delta.sub(centerWorld);
  1633. delta.multiplyScalar(0.1);
  1634. this.viewport.position.sub(delta);
  1635. this.viewport.matrixNeedsUpdate = true;
  1636. }
  1637. }
  1638. // Rotation
  1639. if(this.allowRotation && pointer.buttonPressed(Pointer.RIGHT) && pointer.buttonPressed(Pointer.LEFT))
  1640. {
  1641. // Rotation pivot
  1642. if(this.rotationPoint === null)
  1643. {
  1644. this.rotationPoint = pointer.position.clone();
  1645. this.rotationInitial = this.viewport.rotation;
  1646. }
  1647. else
  1648. {
  1649. var pointer = pointer.position.clone();
  1650. pointer.sub(this.rotationPoint);
  1651. this.viewport.rotation = this.rotationInitial + pointer.angle();
  1652. this.viewport.matrixNeedsUpdate = true;
  1653. }
  1654. }
  1655. // Drag
  1656. else
  1657. {
  1658. this.rotationPoint = null;
  1659. if(pointer.buttonPressed(this.dragButton))
  1660. {
  1661. this.viewport.position.x += pointer.delta.x;
  1662. this.viewport.position.y += pointer.delta.y;
  1663. this.viewport.matrixNeedsUpdate = true;
  1664. }
  1665. }
  1666. };
  1667. /**
  1668. * The renderer is resposible for drawing the structure into the canvas element.
  1669. *
  1670. * Its also resposible for managing the canvas state.
  1671. *
  1672. * @class
  1673. * @param {DOM} canvas Canvas to render the content.
  1674. * @param {Object} options Renderer canvas options.
  1675. */
  1676. function Renderer(canvas, options)
  1677. {
  1678. if(options === undefined)
  1679. {
  1680. options =
  1681. {
  1682. alpha: true
  1683. };
  1684. }
  1685. /**
  1686. * Canvas DOM element, has to be managed by the user.
  1687. */
  1688. this.canvas = canvas;
  1689. /**
  1690. * Canvas 2D rendering context used to draw content.
  1691. */
  1692. this.context = canvas.getContext("2d", {alpha: options.alpha});
  1693. this.context.imageSmoothingEnabled = true;
  1694. this.context.globalCompositeOperation = "source-over";
  1695. /**
  1696. * Pointer input handler object.
  1697. */
  1698. this.pointer = new Pointer(window, canvas);
  1699. /**
  1700. * Indicates if the canvas should be automatically cleared on each new frame.
  1701. */
  1702. this.autoClear = true;
  1703. }
  1704. /**
  1705. * Creates a infinite render loop to render the group into a viewport each frame.
  1706. *
  1707. * The render loop cannot be destroyed, and it automatically creates a viewport controls object.
  1708. *
  1709. * @param {Object2D} group Group to be rendererd.
  1710. * @param {Viewport} viewport Viewport into the objects.
  1711. * @param {Function} onUpdate Function called before rendering the frame.
  1712. */
  1713. Renderer.prototype.createRenderLoop = function(group, viewport, onUpdate)
  1714. {
  1715. var self = this;
  1716. var controls = new ViewportControls(viewport);
  1717. function loop()
  1718. {
  1719. if(onUpdate !== undefined)
  1720. {
  1721. onUpdate();
  1722. }
  1723. controls.update(self.pointer);
  1724. self.update(group, viewport);
  1725. requestAnimationFrame(loop);
  1726. }
  1727. loop();
  1728. };
  1729. /**
  1730. * Update the renderer state, update the input handlers, calculate the object and viewport transformation matrices.
  1731. *
  1732. * Render the object using the viewport into a canvas element.
  1733. *
  1734. * The canvas state is saved and restored for each individual object, ensuring that the code of one object does not affect another one.
  1735. *
  1736. * Should be called at a fixed rate preferably using the requestAnimationFrame() method, its also possible to use the createRenderLoop() method, that automatically creates a infinite render loop.
  1737. *
  1738. * @param object {Object2D} Object to be updated and drawn into the canvas, the Object2D should be used as a group to store all the other objects to be updated and drawn.
  1739. * @param viewport {Viewport} Viewport to be updated (should be the one where the objects will be rendered after).
  1740. */
  1741. Renderer.prototype.update = function(object, viewport)
  1742. {
  1743. // Get objects to be rendered
  1744. var objects = [];
  1745. object.traverse(function(child)
  1746. {
  1747. if(child.visible)
  1748. {
  1749. objects.push(child);
  1750. }
  1751. });
  1752. // Sort objects by layer
  1753. objects.sort(function(a, b)
  1754. {
  1755. if(b.layer === a.layer)
  1756. {
  1757. return b.level - a.level;
  1758. }
  1759. return b.layer - a.layer;
  1760. });
  1761. // Pointer object update
  1762. var pointer = this.pointer;
  1763. pointer.update();
  1764. // Viewport transform matrix
  1765. viewport.updateMatrix();
  1766. // Project pointer coordinates
  1767. var point = pointer.position.clone();
  1768. var viewportPoint = viewport.inverseMatrix.transformPoint(point);
  1769. // Object pointer events
  1770. for(var i = 0; i < objects.length; i++)
  1771. {
  1772. var child = objects[i];
  1773. //Process the
  1774. if(child.pointerEvents)
  1775. {
  1776. var childPoint = child.inverseGlobalMatrix.transformPoint(child.ignoreViewport ? point : viewportPoint);
  1777. // Check if the pointer pointer is inside
  1778. if(child.isInside(childPoint))
  1779. {
  1780. // Pointer enter
  1781. if(!child.pointerInside && child.onPointerEnter !== null)
  1782. {
  1783. child.onPointerEnter(pointer, viewport);
  1784. }
  1785. // Pointer over
  1786. if(child.onPointerOver !== null)
  1787. {
  1788. child.onPointerOver(pointer, viewport);
  1789. }
  1790. // Double click
  1791. if(pointer.buttonDoubleClicked(Pointer.LEFT) && child.onDoubleClick !== null)
  1792. {
  1793. child.onDoubleClick(pointer, viewport);
  1794. }
  1795. // Pointer pressed
  1796. if(pointer.buttonPressed(Pointer.LEFT) && child.onButtonPressed !== null)
  1797. {
  1798. child.onButtonPressed(pointer, viewport);
  1799. }
  1800. // Just released
  1801. if(pointer.buttonJustReleased(Pointer.LEFT) && child.onButtonUp !== null)
  1802. {
  1803. child.onButtonUp(pointer, viewport);
  1804. }
  1805. // Pointer just pressed
  1806. if(pointer.buttonJustPressed(Pointer.LEFT))
  1807. {
  1808. if(child.onButtonDown !== null)
  1809. {
  1810. child.onButtonDown(pointer, viewport);
  1811. }
  1812. // Drag object and break to only start a drag operation on the top element.
  1813. if(child.draggable)
  1814. {
  1815. child.beingDragged = true;
  1816. break;
  1817. }
  1818. }
  1819. child.pointerInside = true;
  1820. }
  1821. else if(child.pointerInside)
  1822. {
  1823. // Pointer leave
  1824. if(child.onPointerLeave !== null)
  1825. {
  1826. child.onPointerLeave(pointer, viewport);
  1827. }
  1828. child.pointerInside = false;
  1829. }
  1830. // Stop object drag
  1831. if(pointer.buttonJustReleased(Pointer.LEFT))
  1832. {
  1833. if(child.draggable)
  1834. {
  1835. child.beingDragged = false;
  1836. }
  1837. }
  1838. }
  1839. }
  1840. // Object drag events and update logic
  1841. for(var i = 0; i < objects.length; i++)
  1842. {
  1843. var child = objects[i];
  1844. // Pointer drag event
  1845. if(child.beingDragged)
  1846. {
  1847. var lastPosition = pointer.position.clone();
  1848. lastPosition.sub(pointer.delta);
  1849. var positionWorld = viewport.inverseMatrix.transformPoint(pointer.position);
  1850. var lastWorld = viewport.inverseMatrix.transformPoint(lastPosition);
  1851. // Mouse delta in world coordinates
  1852. positionWorld.sub(lastWorld);
  1853. if(child.onPointerDrag !== null)
  1854. {
  1855. child.onPointerDrag(pointer, viewport, positionWorld);
  1856. }
  1857. }
  1858. // On update
  1859. if(child.onUpdate !== null)
  1860. {
  1861. child.onUpdate();
  1862. }
  1863. }
  1864. // Update transformation matrices
  1865. object.traverse(function(child)
  1866. {
  1867. child.updateMatrix();
  1868. });
  1869. this.context.setTransform(1, 0, 0, 1, 0, 0);
  1870. // Clear canvas content
  1871. if(this.autoClear)
  1872. {
  1873. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  1874. }
  1875. // Render into the canvas
  1876. for(var i = objects.length - 1; i >= 0; i--)
  1877. {
  1878. if(objects[i].isMask)
  1879. {
  1880. continue;
  1881. }
  1882. if(objects[i].saveContextState)
  1883. {
  1884. this.context.save();
  1885. }
  1886. // Apply all masks
  1887. var masks = objects[i].masks;
  1888. for(var j = 0; j < masks.length; j++)
  1889. {
  1890. if(!masks[j].ignoreViewport)
  1891. {
  1892. viewport.matrix.setContextTransform(this.context);
  1893. }
  1894. masks[j].transform(this.context, viewport, this.canvas);
  1895. masks[j].clip(this.context, viewport, this.canvas);
  1896. }
  1897. // Set the viewport transform
  1898. if(!objects[i].ignoreViewport)
  1899. {
  1900. viewport.matrix.setContextTransform(this.context);
  1901. }
  1902. else if(masks.length > 0)
  1903. {
  1904. this.context.setTransform(1, 0, 0, 1, 0, 0);
  1905. }
  1906. // Apply the object transform to the canvas context
  1907. objects[i].transform(this.context, viewport, this.canvas);
  1908. objects[i].draw(this.context, viewport, this.canvas);
  1909. if(objects[i].restoreContextState)
  1910. {
  1911. this.context.restore();
  1912. }
  1913. }
  1914. };
  1915. /**
  1916. * Box is described by a minimum and maximum points.
  1917. *
  1918. * Can be used for collision detection with points and other boxes.
  1919. *
  1920. * @class
  1921. * @param {Vector2} min
  1922. * @param {Vector2} max
  1923. */
  1924. function Box2(min, max)
  1925. {
  1926. this.min = (min !== undefined) ? min : new Vector2();
  1927. this.max = (max !== undefined) ? max : new Vector2();
  1928. }
  1929. /**
  1930. * Set the box values.
  1931. *
  1932. * @param {Vector2} min
  1933. * @param {Vector2} max
  1934. */
  1935. Box2.prototype.set = function(min, max)
  1936. {
  1937. this.min.copy(min);
  1938. this.max.copy(max);
  1939. return this;
  1940. };
  1941. /**
  1942. * Set the box from a list of Vector2 points.
  1943. *
  1944. * @param {Array} points
  1945. */
  1946. Box2.prototype.setFromPoints = function(points)
  1947. {
  1948. this.min = new Vector2(+Infinity, +Infinity);
  1949. this.max = new Vector2(-Infinity, -Infinity);
  1950. for(var i = 0, il = points.length; i < il; i++)
  1951. {
  1952. this.expandByPoint(points[i]);
  1953. }
  1954. return this;
  1955. };
  1956. /**
  1957. * Set the box minimum and maximum from center point and size.
  1958. *
  1959. * @param {Vector2} center
  1960. * @param {Vector2} size
  1961. */
  1962. Box2.prototype.setFromCenterAndSize = function(center, size)
  1963. {
  1964. var v1 = new Vector2();
  1965. var halfSize = v1.copy(size).multiplyScalar(0.5);
  1966. this.min.copy(center).sub(halfSize);
  1967. this.max.copy(center).add(halfSize);
  1968. return this;
  1969. };
  1970. /**
  1971. * Clone the box into a new object.
  1972. *
  1973. * Should be used when it it necessary to make operations to this box.
  1974. *
  1975. * @return {Box2} New box object with the copy of this object.
  1976. */
  1977. Box2.prototype.clone = function()
  1978. {
  1979. var box = new Box2();
  1980. box.copy(this);
  1981. return box;
  1982. };
  1983. /**
  1984. * Copy the box value from another box.
  1985. *
  1986. * @param {Box2} point
  1987. */
  1988. Box2.prototype.copy = function(box)
  1989. {
  1990. this.min.copy(box.min);
  1991. this.max.copy(box.max);
  1992. };
  1993. /**
  1994. * Check if the box is empty (size equals zero or is negative).
  1995. *
  1996. * The box size is condireded valid on two negative axis.
  1997. *
  1998. * @return {boolean} True if the box is empty.
  1999. */
  2000. Box2.prototype.isEmpty = function()
  2001. {
  2002. return (this.max.x < this.min.x) || (this.max.y < this.min.y);
  2003. };
  2004. /**
  2005. * Calculate the center point of the box.
  2006. *
  2007. * @param {Vector2} [target] Vector to store the result.
  2008. * @return {Vector2} Central point of the box.
  2009. */
  2010. Box2.prototype.getCenter = function(target)
  2011. {
  2012. if(target === undefined)
  2013. {
  2014. target = new Vector2();
  2015. }
  2016. this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5);
  2017. return target;
  2018. };
  2019. /**
  2020. * Get the size of the box from its min and max points.
  2021. *
  2022. * @param {Vector2} [target] Vector to store the result.
  2023. * @return {Vector2} Vector with the calculated size.
  2024. */
  2025. Box2.prototype.getSize = function(target)
  2026. {
  2027. if(target === undefined)
  2028. {
  2029. target = new Vector2();
  2030. }
  2031. this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min);
  2032. return target;
  2033. };
  2034. /**
  2035. * Expand the box to contain a new point.
  2036. *
  2037. * @param {Vector2} point
  2038. */
  2039. Box2.prototype.expandByPoint = function(point)
  2040. {
  2041. this.min.min(point);
  2042. this.max.max(point);
  2043. return this;
  2044. };
  2045. /**
  2046. * Expand the box by adding a border with the vector size.
  2047. *
  2048. * Vector is subtracted from min and added to the max points.
  2049. *
  2050. * @param {Vector2} vector
  2051. */
  2052. Box2.prototype.expandByVector = function(vector)
  2053. {
  2054. this.min.sub(vector);
  2055. this.max.add(vector);
  2056. };
  2057. /**
  2058. * Expand the box by adding a border with the scalar value.
  2059. *
  2060. * @param {number} scalar
  2061. */
  2062. Box2.prototype.expandByScalar = function(scalar)
  2063. {
  2064. this.min.addScalar(-scalar);
  2065. this.max.addScalar(scalar);
  2066. };
  2067. /**
  2068. * Check if the box contains a point inside.
  2069. *
  2070. * @param {Vector2} point
  2071. * @return {boolean} True if the box contains point.
  2072. */
  2073. Box2.prototype.containsPoint = function(point)
  2074. {
  2075. return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true;
  2076. };
  2077. /**
  2078. * Check if the box fully contains another box inside (different from intersects box).
  2079. *
  2080. * Only returns true if the box is fully contained.
  2081. *
  2082. * @param {Box2} box
  2083. * @return {boolean} True if the box contains box.
  2084. */
  2085. Box2.prototype.containsBox = function(box)
  2086. {
  2087. 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;
  2088. };
  2089. /**
  2090. * Check if two boxes intersect each other, using 4 splitting planes to rule out intersections.
  2091. *
  2092. * @param {Box2} box
  2093. * @return {boolean} True if the boxes intersect each other.
  2094. */
  2095. Box2.prototype.intersectsBox = function(box)
  2096. {
  2097. 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;
  2098. };
  2099. /**
  2100. * Calculate the distance to a point.
  2101. *
  2102. * @param {Vector2} point
  2103. * @return {number} Distance to point calculated.
  2104. */
  2105. Box2.prototype.distanceToPoint = function(point)
  2106. {
  2107. var v = new Vector2();
  2108. var clampedPoint = v.copy(point).clamp(this.min, this.max);
  2109. return clampedPoint.sub(point).length();
  2110. };
  2111. /**
  2112. * Make a intersection between this box and another box.
  2113. *
  2114. * Store the result in this object.
  2115. *
  2116. * @param {Box2} box
  2117. */
  2118. Box2.prototype.intersect = function(box)
  2119. {
  2120. this.min.max(box.min);
  2121. this.max.min(box.max);
  2122. };
  2123. /**
  2124. * Make a union between this box and another box.
  2125. *
  2126. * Store the result in this object.
  2127. *
  2128. * @param {Box2} box
  2129. */
  2130. Box2.prototype.union = function(box)
  2131. {
  2132. this.min.min(box.min);
  2133. this.max.max(box.max);
  2134. };
  2135. /**
  2136. * Translate the box by a offset value, adds the offset to booth min and max.
  2137. *
  2138. * @param {Vector2} offset
  2139. */
  2140. Box2.prototype.translate = function(offset)
  2141. {
  2142. this.min.add(offset);
  2143. this.max.add(offset);
  2144. };
  2145. /**
  2146. * Checks if two boxes are equal.
  2147. *
  2148. * @param {Box2} box
  2149. * @return {boolean} True if the two boxes are equal.
  2150. */
  2151. Box2.prototype.equals = function(box)
  2152. {
  2153. return box.min.equals(this.min) && box.max.equals(this.max);
  2154. };
  2155. /**
  2156. * A mask can be used to set the drawing region.
  2157. *
  2158. * Masks are treated as objects their shape is used to filter other objects shape.
  2159. *
  2160. * Multiple mask objects can be active simulatenously, they have to be attached to the object mask list to filter the render region.
  2161. *
  2162. * A mask objects is draw using the context.clip() method.
  2163. *
  2164. * @class
  2165. * @extends {Object2D}
  2166. */
  2167. function Mask()
  2168. {
  2169. Object2D.call(this);
  2170. }
  2171. Mask.prototype = Object.create(Object2D.prototype);
  2172. Mask.prototype.isMask = true;
  2173. /**
  2174. * Clip the canvas context, to ensure that next objects being drawn are cliped to the path stored here.
  2175. *
  2176. * @param {CanvasContext} context Canvas 2d drawing context.
  2177. * @param {Viewport} viewport Viewport applied to the canvas.
  2178. * @param {DOM} canvas DOM canvas element where the content is being drawn.
  2179. */
  2180. Mask.prototype.clip = function(context, viewport, canvas){};
  2181. /**
  2182. * Box mask can be used to clear a box mask region.
  2183. *
  2184. * It will limit the drwaing region to this box.
  2185. *
  2186. * @class
  2187. * @extends {Mask}
  2188. */
  2189. function BoxMask()
  2190. {
  2191. Mask.call(this);
  2192. /**
  2193. * Box object containing the size of the object.
  2194. */
  2195. this.box = new Box2(new Vector2(-50, -35), new Vector2(50, 35));
  2196. /**
  2197. * If inverted the mask considers the outside of the box instead of the inside.
  2198. */
  2199. this.invert = false;
  2200. }
  2201. BoxMask.prototype = Object.create(Mask.prototype);
  2202. BoxMask.prototype.isInside = function(point)
  2203. {
  2204. return this.box.containsPoint(point);
  2205. };
  2206. BoxMask.prototype.clip = function(context, viewport, canvas)
  2207. {
  2208. context.beginPath();
  2209. var width = this.box.max.x - this.box.min.x;
  2210. if(this.invert)
  2211. {
  2212. context.rect(this.box.min.x - 1e4, -5e3, 1e4, 1e4);
  2213. context.rect(this.box.max.x, -5e3, 1e4, 1e4);
  2214. context.rect(this.box.min.x, this.box.min.y - 1e4, width, 1e4);
  2215. context.rect(this.box.min.x, this.box.max.y, width, 1e4);
  2216. }
  2217. else
  2218. {
  2219. var height = this.box.max.y - this.box.min.y;
  2220. context.fillRect(this.box.min.x, this.box.min.y, width, height);
  2221. }
  2222. context.clip();
  2223. };
  2224. /**
  2225. * Circle object draw a circular object, into the canvas.
  2226. *
  2227. * Can be used as a base to implement other circular objects, already implements the circle collision for pointer events.
  2228. *
  2229. * @class
  2230. * @extends {Object2D}
  2231. */
  2232. function Circle()
  2233. {
  2234. Object2D.call(this);
  2235. /**
  2236. * Radius of the circle.
  2237. */
  2238. this.radius = 10.0;
  2239. /**
  2240. * Style of the object border line.
  2241. *
  2242. * If set null it is ignored.
  2243. */
  2244. this.strokeStyle = "#000000";
  2245. /**
  2246. * Line width, only used if a valid strokeStyle is defined.
  2247. */
  2248. this.lineWidth = 1;
  2249. /**
  2250. * Background color of the circle.
  2251. *
  2252. * If set null it is ignored.
  2253. */
  2254. this.fillStyle = "#FFFFFF";
  2255. }
  2256. Circle.prototype = Object.create(Object2D.prototype);
  2257. Circle.prototype.isInside = function(point)
  2258. {
  2259. return point.length() <= this.radius;
  2260. };
  2261. Circle.prototype.onPointerEnter = function(pointer, viewport)
  2262. {
  2263. this.fillStyle = "#CCCCCC";
  2264. };
  2265. Circle.prototype.onPointerLeave = function(pointer, viewport)
  2266. {
  2267. this.fillStyle = "#FFFFFF";
  2268. };
  2269. Circle.prototype.draw = function(context, viewport, canvas)
  2270. {
  2271. context.beginPath();
  2272. context.arc(0, 0, this.radius, 0, 2 * Math.PI);
  2273. if(this.fillStyle !== null)
  2274. {
  2275. context.fillStyle = this.fillStyle;
  2276. context.fill();
  2277. }
  2278. if(this.strokeStyle !== null)
  2279. {
  2280. context.lineWidth = this.lineWidth;
  2281. context.strokeStyle = this.strokeStyle;
  2282. context.stroke();
  2283. }
  2284. };
  2285. /**
  2286. * Class contains helper functions to create editing object tools.
  2287. *
  2288. * @class
  2289. */
  2290. function Helpers(){}
  2291. /**
  2292. * Create a rotation tool helper.
  2293. *
  2294. * When the object is dragged is changes the parent object rotation.
  2295. *
  2296. * @static
  2297. */
  2298. Helpers.rotateTool = function(object)
  2299. {
  2300. var tool = new Circle();
  2301. tool.radius = 4;
  2302. tool.layer = object.layer + 1;
  2303. tool.onPointerDrag = function(pointer, viewport, delta)
  2304. {
  2305. object.rotation += delta.x * 1e-3;
  2306. };
  2307. object.add(tool);
  2308. };
  2309. /**
  2310. * Create a box resize helper and attach it to an object to change the size of the object box.
  2311. *
  2312. * Each helper is positioned on one corner of the box, and the value of the corner is copied to the boxes as they are dragged.
  2313. *
  2314. * This method required to object to have a box property.
  2315. *
  2316. * @static
  2317. */
  2318. Helpers.boxResizeTool = function(object)
  2319. {
  2320. if(object.box === undefined)
  2321. {
  2322. console.warn("escher.js: Helpers.boxResizeTool(), object box property missing.");
  2323. return;
  2324. }
  2325. function updateHelpers()
  2326. {
  2327. topRight.position.copy(object.box.min);
  2328. bottomLeft.position.copy(object.box.max);
  2329. topLeft.position.set(object.box.max.x, object.box.min.y);
  2330. bottomRight.position.set(object.box.min.x, object.box.max.y);
  2331. }
  2332. var topRight = new Circle();
  2333. topRight.radius = 4;
  2334. topRight.layer = object.layer + 1;
  2335. topRight.draggable = true;
  2336. topRight.onPointerDrag = function(pointer, viewport, delta)
  2337. {
  2338. Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
  2339. object.box.min.copy(topRight.position);
  2340. updateHelpers();
  2341. };
  2342. object.add(topRight);
  2343. var topLeft = new Circle();
  2344. topLeft.radius = 4;
  2345. topLeft.layer = object.layer + 1;
  2346. topLeft.draggable = true;
  2347. topLeft.onPointerDrag = function(pointer, viewport, delta)
  2348. {
  2349. Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
  2350. object.box.max.x = topLeft.position.x;
  2351. object.box.min.y = topLeft.position.y;
  2352. updateHelpers();
  2353. };
  2354. object.add(topLeft);
  2355. var bottomLeft = new Circle();
  2356. bottomLeft.radius = 4;
  2357. bottomLeft.layer = object.layer + 1;
  2358. bottomLeft.draggable = true;
  2359. bottomLeft.onPointerDrag = function(pointer, viewport, delta)
  2360. {
  2361. Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
  2362. object.box.max.copy(bottomLeft.position);
  2363. updateHelpers();
  2364. };
  2365. object.add(bottomLeft);
  2366. var bottomRight = new Circle();
  2367. bottomRight.radius = 4;
  2368. bottomRight.layer = object.layer + 1;
  2369. bottomRight.draggable = true;
  2370. bottomRight.onPointerDrag = function(pointer, viewport, delta)
  2371. {
  2372. Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
  2373. object.box.min.x = bottomRight.position.x;
  2374. object.box.max.y = bottomRight.position.y;
  2375. updateHelpers();
  2376. };
  2377. object.add(bottomRight);
  2378. updateHelpers();
  2379. };
  2380. /**
  2381. * Box object draw a rectangular object.
  2382. *
  2383. * Can be used as a base to implement other box objects, already implements collision for pointer events.
  2384. *
  2385. * @class
  2386. * @extends {Object2D}
  2387. */
  2388. function Box()
  2389. {
  2390. Object2D.call(this);
  2391. /**
  2392. * Box object containing the size of the object.
  2393. */
  2394. this.box = new Box2(new Vector2(-50, -35), new Vector2(50, 35));
  2395. /**
  2396. * Style of the object border line.
  2397. *
  2398. * If set null it is ignored.
  2399. */
  2400. this.strokeStyle = "#000000";
  2401. /**
  2402. * Line width, only used if a valid strokeStyle is defined.
  2403. */
  2404. this.lineWidth = 1;
  2405. /**
  2406. * Background color of the box.
  2407. *
  2408. * If set null it is ignored.
  2409. */
  2410. this.fillStyle = "#FFFFFF";
  2411. }
  2412. Box.prototype = Object.create(Object2D.prototype);
  2413. Box.prototype.onPointerEnter = function(pointer, viewport)
  2414. {
  2415. this.fillStyle = "#CCCCCC";
  2416. };
  2417. Box.prototype.onPointerLeave = function(pointer, viewport)
  2418. {
  2419. this.fillStyle = "#FFFFFF";
  2420. };
  2421. Box.prototype.isInside = function(point)
  2422. {
  2423. return this.box.containsPoint(point);
  2424. };
  2425. Box.prototype.draw = function(context, viewport, canvas)
  2426. {
  2427. var width = this.box.max.x - this.box.min.x;
  2428. var height = this.box.max.y - this.box.min.y;
  2429. if(this.fillStyle !== null)
  2430. {
  2431. context.fillStyle = this.fillStyle;
  2432. context.fillRect(this.box.min.x, this.box.min.y, width, height);
  2433. }
  2434. if(this.strokeStyle !== null)
  2435. {
  2436. context.lineWidth = this.lineWidth;
  2437. context.strokeStyle = this.strokeStyle;
  2438. context.strokeRect(this.box.min.x, this.box.min.y, width, height);
  2439. }
  2440. };
  2441. /**
  2442. * Line object draw a line from one point to another.
  2443. *
  2444. * @class
  2445. * @extends {Object2D}
  2446. */
  2447. function Line()
  2448. {
  2449. Object2D.call(this);
  2450. /**
  2451. * Initial point of the line.
  2452. *
  2453. * Can be equal to the position object of another object. (Making it automatically follow that object.)
  2454. */
  2455. this.from = new Vector2();
  2456. /**
  2457. * Final point of the line.
  2458. *
  2459. * Can be equal to the position object of another object. (Making it automatically follow that object.)
  2460. */
  2461. this.to = new Vector2();
  2462. /**
  2463. * Dash line pattern to be used, if empty draws a solid line.
  2464. *
  2465. * Dash parttern is defined as the size of dashes as pairs of space with no line and with line.
  2466. *
  2467. * E.g if the daspattern is [1, 2] we get 1 point with line, 2 without line repeat infinitelly.
  2468. */
  2469. this.dashPattern = [5, 5];
  2470. /**
  2471. * Style of the object line.
  2472. */
  2473. this.strokeStyle = "#000000";
  2474. /**
  2475. * Line width of the line.
  2476. */
  2477. this.lineWidth = 1;
  2478. }
  2479. Line.prototype = Object.create(Object2D.prototype);
  2480. Line.prototype.draw = function(context, viewport, canvas)
  2481. {
  2482. context.lineWidth = this.lineWidth;
  2483. context.strokeStyle = this.strokeStyle;
  2484. context.setLineDash(this.dashPattern);
  2485. context.beginPath();
  2486. context.moveTo(this.from.x, this.from.y);
  2487. context.lineTo(this.to.x, this.to.y);
  2488. context.stroke();
  2489. };
  2490. /**
  2491. * Text element, used to draw text into the canvas.
  2492. *
  2493. * @class
  2494. * @extends {Object2D}
  2495. */
  2496. function Text()
  2497. {
  2498. Object2D.call(this);
  2499. /**
  2500. * Text value.
  2501. */
  2502. this.text = "";
  2503. /**
  2504. * Font of the text.
  2505. */
  2506. this.font = "16px Arial";
  2507. /**
  2508. * Style of the object border line.
  2509. *
  2510. * If set null it is ignored.
  2511. */
  2512. this.strokeStyle = null;
  2513. /**
  2514. * Line width, only used if a valid strokeStyle is defined.
  2515. */
  2516. this.lineWidth = 1;
  2517. /**
  2518. * Background color of the box.
  2519. *
  2520. * If set null it is ignored.
  2521. */
  2522. this.fillStyle = "#000000";
  2523. /**
  2524. * Text align property.
  2525. */
  2526. this.textAlign = "center";
  2527. }
  2528. Text.prototype = Object.create(Object2D.prototype);
  2529. Text.prototype.draw = function(context, viewport, canvas)
  2530. {
  2531. context.font = this.font;
  2532. context.textAlign = this.textAlign;
  2533. context.textBaseline = "middle";
  2534. if(this.fillStyle !== null)
  2535. {
  2536. context.fillStyle = this.fillStyle;
  2537. context.fillText(this.text, 0, 0);
  2538. }
  2539. if(this.strokeStyle !== null)
  2540. {
  2541. context.strokeStyle = this.strokeStyle;
  2542. context.strokeText(this.text, 0, 0);
  2543. }
  2544. };
  2545. /**
  2546. * Image object is used to draw an image from URL.
  2547. *
  2548. * @class
  2549. * @param {string} [src] Source URL of the image.
  2550. * @extends {Object2D}
  2551. */
  2552. function Image(src)
  2553. {
  2554. Object2D.call(this);
  2555. /**
  2556. * Box object containing the size of the object.
  2557. */
  2558. this.box = new Box2();
  2559. /**
  2560. * Image source DOM element.
  2561. */
  2562. this.image = document.createElement("img");
  2563. if(src !== undefined)
  2564. {
  2565. this.setImage(src);
  2566. }
  2567. }
  2568. Image.prototype = Object.create(Object2D.prototype);
  2569. /**
  2570. * Set the image of the object.
  2571. *
  2572. * Automatically sets the box size to match the image.
  2573. *
  2574. * @param {string} src Source URL of the image.
  2575. */
  2576. Image.prototype.setImage = function(src)
  2577. {
  2578. var self = this;
  2579. this.image.onload = function()
  2580. {
  2581. self.box.min.set(0, 0);
  2582. self.box.max.set(this.naturalWidth, this.naturalHeight);
  2583. };
  2584. this.image.src = src;
  2585. };
  2586. Image.prototype.isInside = function(point)
  2587. {
  2588. return this.box.containsPoint(point);
  2589. };
  2590. Image.prototype.draw = function(context, viewport, canvas)
  2591. {
  2592. if(this.image.src.length > 0)
  2593. {
  2594. context.drawImage(this.image, 0, 0, this.image.naturalWidth, this.image.naturalHeight, this.box.min.x, this.box.min.y, this.box.max.x - this.box.min.x, this.box.max.y - this.box.min.y);
  2595. }
  2596. };
  2597. /**
  2598. * A DOM object transformed using CSS3D to ver included in the graph.
  2599. *
  2600. * DOM objects always stay on top of everything else, mouse events are not supported for these.
  2601. *
  2602. * Use the normal DOM events for interaction.
  2603. *
  2604. * @class
  2605. * @param parentDOM Parent DOM element that contains the drawing canvas.
  2606. * @param type Type of the DOM element (e.g. "div", "p", ...)
  2607. * @extends {Object2D}
  2608. */
  2609. function DOM(parentDOM, type)
  2610. {
  2611. Object2D.call(this);
  2612. /**
  2613. * Parent element that contains this DOM div.
  2614. */
  2615. this.parentDOM = parentDOM;
  2616. /**
  2617. * DOM element contained by this object.
  2618. *
  2619. * Bye default it has the pointerEvents style set to none.
  2620. */
  2621. this.element = document.createElement("div");
  2622. this.element.style.transformStyle = "preserve-3d";
  2623. this.element.style.position = "absolute";
  2624. this.element.style.top = "0px";
  2625. this.element.style.bottom = "0px";
  2626. this.element.style.transformOrigin = "0px 0px";
  2627. this.element.style.overflow = "auto";
  2628. this.element.style.pointerEvents = "none";
  2629. /**
  2630. * Size of the DOM element (in world coordinates).
  2631. */
  2632. this.size = new Vector2(100, 100);
  2633. }
  2634. DOM.prototype = Object.create(Object2D.prototype);
  2635. DOM.prototype.onAdd = function()
  2636. {
  2637. this.parentDOM.appendChild(this.element);
  2638. };
  2639. DOM.prototype.onRemove = function()
  2640. {
  2641. this.parentDOM.removeChild(this.element);
  2642. };
  2643. DOM.prototype.transform = function(context, viewport, canvas)
  2644. {
  2645. // CSS transformation matrix
  2646. if(this.ignoreViewport)
  2647. {
  2648. this.element.style.transform = this.globalMatrix.cssTransform();
  2649. }
  2650. else
  2651. {
  2652. var projection = viewport.matrix.clone();
  2653. projection.multiply(this.globalMatrix);
  2654. this.element.style.transform = projection.cssTransform();
  2655. }
  2656. // Size of the element
  2657. this.element.style.width = this.size.x + "px";
  2658. this.element.style.height = this.size.y + "px";
  2659. // Visibility
  2660. this.element.style.display = this.visible ? "block" : "none";
  2661. };
  2662. /**
  2663. * Pattern object draw a image repeated as a pattern.
  2664. *
  2665. * Its similar to the Image class but the image can be repeat infinitly.
  2666. *
  2667. * @class
  2668. * @extends {Object2D}
  2669. */
  2670. function Pattern(src)
  2671. {
  2672. Object2D.call(this);
  2673. /**
  2674. * Box object containing the size of the object.
  2675. */
  2676. this.box = new Box2();
  2677. /**
  2678. * Image source DOM element.
  2679. */
  2680. this.image = document.createElement("img");
  2681. /**
  2682. * A DOMString indicating how to repeat the pattern image.
  2683. */
  2684. this.repetition = "repeat";
  2685. if(src !== undefined)
  2686. {
  2687. this.setImage(src);
  2688. }
  2689. }
  2690. Pattern.prototype = Object.create(Object2D.prototype);
  2691. /**
  2692. * Set the image of the object.
  2693. *
  2694. * Automatically sets the box size to match the image.
  2695. */
  2696. Pattern.prototype.setImage = function(src)
  2697. {
  2698. var self = this;
  2699. this.image.onload = function()
  2700. {
  2701. self.box.min.set(0, 0);
  2702. self.box.max.set(this.naturalWidth, this.naturalHeight);
  2703. };
  2704. this.image.src = src;
  2705. };
  2706. Pattern.prototype.isInside = function(point)
  2707. {
  2708. return this.box.containsPoint(point);
  2709. };
  2710. Pattern.prototype.draw = function(context, viewport, canvas)
  2711. {
  2712. var width = this.box.max.x - this.box.min.x;
  2713. var height = this.box.max.y - this.box.min.y;
  2714. if(this.image.src.length > 0)
  2715. {
  2716. var pattern = context.createPattern(this.image, this.repetition);
  2717. context.fillStyle = pattern;
  2718. context.fillRect(this.box.min.x, this.box.min.y, width, height);
  2719. }
  2720. };
  2721. /**
  2722. * Graph object is used to draw simple graph data into the canvas.
  2723. *
  2724. * Graph data is composed of X, Y values.
  2725. *
  2726. * @class
  2727. * @extends {Object2D}
  2728. */
  2729. function Graph()
  2730. {
  2731. Object2D.call(this);
  2732. /**
  2733. * Graph object containing the size of the object.
  2734. */
  2735. this.box = new Box2(new Vector2(-50, -35), new Vector2(50, 35));
  2736. /**
  2737. * Color of the box border line.
  2738. */
  2739. this.strokeStyle = "rgb(0, 153, 255)";
  2740. /**
  2741. * Line width.
  2742. */
  2743. this.lineWidth = 1;
  2744. /**
  2745. * Background color of the box.
  2746. */
  2747. this.fillStyle = "rgba(0, 153, 255, 0.3)";
  2748. /**
  2749. * Minimum value of the graph.
  2750. */
  2751. this.min = 0;
  2752. /**
  2753. * Maximum value of the graph.
  2754. */
  2755. this.max = 10;
  2756. /**
  2757. * Data to be presented in the graph.
  2758. *
  2759. * The array should store numeric values.
  2760. */
  2761. this.data = [];
  2762. }
  2763. Graph.prototype = Object.create(Object2D.prototype);
  2764. Graph.prototype.isInside = function(point)
  2765. {
  2766. return this.box.containsPoint(point);
  2767. };
  2768. Graph.prototype.draw = function(context, viewport, canvas)
  2769. {
  2770. if(this.data.length === 0)
  2771. {
  2772. return;
  2773. }
  2774. var width = this.box.max.x - this.box.min.x;
  2775. var height = this.box.max.y - this.box.min.y;
  2776. context.lineWidth = this.lineWidth;
  2777. context.strokeStyle = this.strokeStyle;
  2778. context.beginPath();
  2779. var step = width / (this.data.length - 1);
  2780. var gamma = this.max - this.min;
  2781. context.moveTo(this.box.min.x, this.box.max.y - ((this.data[0] - this.min) / gamma) * height);
  2782. for(var i = 1, s = step; i < this.data.length; s += step, i++)
  2783. {
  2784. context.lineTo(this.box.min.x + s, this.box.max.y - ((this.data[i] - this.min) / gamma) * height);
  2785. }
  2786. context.stroke();
  2787. if(this.fillStyle !== null)
  2788. {
  2789. context.fillStyle = this.fillStyle;
  2790. context.lineTo(this.box.max.x, this.box.max.y);
  2791. context.lineTo(this.box.min.x, this.box.max.y);
  2792. context.fill();
  2793. }
  2794. };
  2795. /**
  2796. * BezierCurve object draw as bezier curve between two points.
  2797. *
  2798. * @class
  2799. */
  2800. function BezierCurve()
  2801. {
  2802. Object2D.call(this);
  2803. /**
  2804. * Initial point of the curve.
  2805. *
  2806. * Can be equal to the position object of another object. (Making it automatically follow that object.)
  2807. */
  2808. this.from = new Vector2();
  2809. /**
  2810. * Intial position control point, indicates the tangent of the bezier curve on the first point.
  2811. */
  2812. this.fromCp = new Vector2();
  2813. /**
  2814. * Final point of the curve.
  2815. *
  2816. * Can be equal to the position object of another object. (Making it automatically follow that object.)
  2817. */
  2818. this.to = new Vector2();
  2819. /**
  2820. * Final position control point, indicates the tangent of the bezier curve on the last point.
  2821. */
  2822. this.toCp = new Vector2();
  2823. /**
  2824. * Dash line pattern to be used, if empty draws a solid line.
  2825. *
  2826. * Dash parttern is defined as the size of dashes as pairs of space with no line and with line.
  2827. *
  2828. * E.g if the daspattern is [1, 2] we get 1 point with line, 2 without line repeat infinitelly.
  2829. */
  2830. this.dashPattern = [5, 5];
  2831. /**
  2832. * Style of the object line.
  2833. */
  2834. this.strokeStyle = "#000000";
  2835. /**
  2836. * Line width of the line.
  2837. */
  2838. this.lineWidth = 1;
  2839. }
  2840. BezierCurve.prototype = Object.create(Object2D.prototype);
  2841. /**
  2842. * Create a bezier curve helper, to edit the bezier curve anchor points.
  2843. *
  2844. * @static
  2845. */
  2846. BezierCurve.curveHelper = function(object)
  2847. {
  2848. var fromCp = new Circle();
  2849. fromCp.radius = 3;
  2850. fromCp.layer = object.layer + 1;
  2851. fromCp.draggable = true;
  2852. fromCp.onPointerDrag = function(pointer, viewport, delta)
  2853. {
  2854. Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
  2855. object.fromCp.copy(fromCp.position);
  2856. };
  2857. object.parent.add(fromCp);
  2858. var fromLine = new Line();
  2859. fromLine.from = object.from;
  2860. fromLine.to = object.fromCp;
  2861. object.parent.add(fromLine);
  2862. var toCp = new Circle();
  2863. toCp.radius = 3;
  2864. toCp.layer = object.layer + 1;
  2865. toCp.draggable = true;
  2866. toCp.onPointerDrag = function(pointer, viewport, delta)
  2867. {
  2868. Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
  2869. object.toCp.copy(toCp.position);
  2870. };
  2871. object.parent.add(toCp);
  2872. var toLine = new Line();
  2873. toLine.from = object.to;
  2874. toLine.to = object.toCp;
  2875. object.parent.add(toLine);
  2876. };
  2877. BezierCurve.prototype.draw = function(context, viewport, canvas)
  2878. {
  2879. context.lineWidth = this.lineWidth;
  2880. context.strokeStyle = this.strokeStyle;
  2881. context.setLineDash(this.dashPattern);
  2882. context.beginPath();
  2883. context.moveTo(this.from.x, this.from.y);
  2884. context.bezierCurveTo(this.fromCp.x, this.fromCp.y, this.toCp.x, this.toCp.y, this.to.x, this.to.y);
  2885. context.stroke();
  2886. };
  2887. exports.BezierCurve = BezierCurve;
  2888. exports.Box = Box;
  2889. exports.Box2 = Box2;
  2890. exports.BoxMask = BoxMask;
  2891. exports.Circle = Circle;
  2892. exports.DOM = DOM;
  2893. exports.EventManager = EventManager;
  2894. exports.Graph = Graph;
  2895. exports.Helpers = Helpers;
  2896. exports.Image = Image;
  2897. exports.Key = Key;
  2898. exports.Line = Line;
  2899. exports.Mask = Mask;
  2900. exports.Matrix = Matrix;
  2901. exports.Object2D = Object2D;
  2902. exports.Pattern = Pattern;
  2903. exports.Pointer = Pointer;
  2904. exports.Renderer = Renderer;
  2905. exports.Text = Text;
  2906. exports.UUID = UUID;
  2907. exports.Vector2 = Vector2;
  2908. exports.Viewport = Viewport;
  2909. exports.ViewportControls = ViewportControls;
  2910. Object.defineProperty(exports, '__esModule', { value: true });
  2911. })));