jclic 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +5 -7
  2. package/dist-node/jclic-node.js +14157 -0
  3. package/dist-node/jclic-node.umd.cjs +530 -0
  4. package/package.json +38 -26
  5. package/.vscode/launch.json +0 -33
  6. package/.vscode/settings.json +0 -13
  7. package/CHANGELOG.md +0 -664
  8. package/TRANSLATIONS.md +0 -11
  9. package/build-locales.mjs +0 -82
  10. package/dist/jclic-node.js +0 -31678
  11. package/dist/jclic-node.js.map +0 -1
  12. package/dist/jclic.components.LICENSE +0 -2254
  13. package/dist/jclic.min.js +0 -27
  14. package/dist/jclic.min.js.map +0 -1
  15. package/eslint.config.mjs +0 -31
  16. package/jsdoc.config.js +0 -71
  17. package/locales/ar.po +0 -244
  18. package/locales/ast.po +0 -246
  19. package/locales/bs.po +0 -247
  20. package/locales/ca.po +0 -248
  21. package/locales/ca_ES@valencia.po +0 -248
  22. package/locales/cs.po +0 -244
  23. package/locales/da.po +0 -244
  24. package/locales/de.po +0 -246
  25. package/locales/el.po +0 -244
  26. package/locales/es.po +0 -248
  27. package/locales/eu.po +0 -244
  28. package/locales/fr.po +0 -244
  29. package/locales/gl.po +0 -244
  30. package/locales/he.po +0 -244
  31. package/locales/hr.po +0 -245
  32. package/locales/it.po +0 -246
  33. package/locales/ja.po +0 -242
  34. package/locales/jclic.js.pot +0 -241
  35. package/locales/nb_NO.po +0 -244
  36. package/locales/nl.po +0 -244
  37. package/locales/pl.po +0 -244
  38. package/locales/pt.po +0 -244
  39. package/locales/pt_BR.po +0 -248
  40. package/locales/ro.po +0 -248
  41. package/locales/ru.po +0 -245
  42. package/locales/ta.po +0 -244
  43. package/locales/tr.po +0 -246
  44. package/locales/uk.po +0 -247
  45. package/locales/vec.po +0 -244
  46. package/locales/zh_TW.po +0 -246
  47. package/patches/po2json+1.0.0-beta-3.patch +0 -12
  48. package/src/AWT.js +0 -2067
  49. package/src/Activity.js +0 -1311
  50. package/src/Deps.js +0 -232
  51. package/src/GlobalData.js +0 -5
  52. package/src/JClic.js +0 -196
  53. package/src/JClicPlayer.js +0 -1308
  54. package/src/PlayerHistory.js +0 -305
  55. package/src/Utils.js +0 -1355
  56. package/src/activities/associations/ComplexAssociation.js +0 -321
  57. package/src/activities/associations/SimpleAssociation.js +0 -519
  58. package/src/activities/memory/MemoryGame.js +0 -423
  59. package/src/activities/panels/Explore.js +0 -349
  60. package/src/activities/panels/Identify.js +0 -356
  61. package/src/activities/panels/InformationScreen.js +0 -262
  62. package/src/activities/panels/Menu.js +0 -209
  63. package/src/activities/panels/icons/ico00.png +0 -0
  64. package/src/activities/panels/icons/ico01.png +0 -0
  65. package/src/activities/panels/icons/ico02.png +0 -0
  66. package/src/activities/panels/icons/ico03.png +0 -0
  67. package/src/activities/panels/icons/icofolder.png +0 -0
  68. package/src/activities/puzzles/DoublePuzzle.js +0 -424
  69. package/src/activities/puzzles/ExchangePuzzle.js +0 -374
  70. package/src/activities/puzzles/HolePuzzle.js +0 -360
  71. package/src/activities/text/Complete.js +0 -127
  72. package/src/activities/text/Evaluator.js +0 -534
  73. package/src/activities/text/FillInBlanks.js +0 -426
  74. package/src/activities/text/IdentifyText.js +0 -253
  75. package/src/activities/text/OrderText.js +0 -421
  76. package/src/activities/text/TextActivityBase.js +0 -557
  77. package/src/activities/text/TextActivityDocument.js +0 -658
  78. package/src/activities/text/WrittenAnswer.js +0 -557
  79. package/src/activities/textGrid/CrossWord.js +0 -565
  80. package/src/activities/textGrid/WordSearch.js +0 -458
  81. package/src/activities/textGrid/icons/hIcon.svg +0 -3
  82. package/src/activities/textGrid/icons/vIcon.svg +0 -3
  83. package/src/automation/AutoContentProvider.js +0 -182
  84. package/src/automation/arith/Arith.js +0 -864
  85. package/src/bags/ActivitySequence.js +0 -318
  86. package/src/bags/ActivitySequenceElement.js +0 -161
  87. package/src/bags/ActivitySequenceJump.js +0 -140
  88. package/src/bags/ConditionalJumpInfo.js +0 -113
  89. package/src/bags/JumpInfo.js +0 -136
  90. package/src/bags/MediaBag.js +0 -215
  91. package/src/bags/MediaBagElement.js +0 -516
  92. package/src/boxes/AbstractBox.js +0 -699
  93. package/src/boxes/ActiveBagContent.js +0 -494
  94. package/src/boxes/ActiveBox.js +0 -810
  95. package/src/boxes/ActiveBoxBag.js +0 -357
  96. package/src/boxes/ActiveBoxContent.js +0 -484
  97. package/src/boxes/ActiveBoxGrid.js +0 -179
  98. package/src/boxes/BoxBag.js +0 -500
  99. package/src/boxes/BoxBase.js +0 -398
  100. package/src/boxes/BoxConnector.js +0 -325
  101. package/src/boxes/TextGrid.js +0 -887
  102. package/src/boxes/TextGridContent.js +0 -215
  103. package/src/init-jsdom.js +0 -65
  104. package/src/jclic-node.js +0 -219
  105. package/src/media/ActiveMediaBag.js +0 -145
  106. package/src/media/ActiveMediaPlayer.js +0 -297
  107. package/src/media/AudioBuffer.js +0 -219
  108. package/src/media/EventSounds.js +0 -169
  109. package/src/media/EventSoundsElement.js +0 -155
  110. package/src/media/MediaContent.js +0 -328
  111. package/src/media/MidiAudioPlayer.js +0 -254
  112. package/src/media/icons/audio.svg +0 -3
  113. package/src/media/icons/generic.svg +0 -3
  114. package/src/media/icons/mic.svg +0 -3
  115. package/src/media/icons/movie.svg +0 -3
  116. package/src/media/icons/music.svg +0 -3
  117. package/src/media/icons/url.svg +0 -3
  118. package/src/media/sounds/actionError.mp3 +0 -0
  119. package/src/media/sounds/actionOk.mp3 +0 -0
  120. package/src/media/sounds/click.mp3 +0 -0
  121. package/src/media/sounds/finishedError.mp3 +0 -0
  122. package/src/media/sounds/finishedOk.mp3 +0 -0
  123. package/src/media/sounds/start.mp3 +0 -0
  124. package/src/project/JClicProject.js +0 -282
  125. package/src/project/ProjectSettings.js +0 -273
  126. package/src/report/ActionReg.js +0 -123
  127. package/src/report/ActivityReg.js +0 -271
  128. package/src/report/EncryptMin.js +0 -210
  129. package/src/report/Reporter.js +0 -727
  130. package/src/report/SCORM.js +0 -272
  131. package/src/report/SequenceReg.js +0 -275
  132. package/src/report/SessionReg.js +0 -340
  133. package/src/report/SessionStorageReporter.js +0 -131
  134. package/src/report/TCPReporter.js +0 -628
  135. package/src/shapers/ClassicJigSaw.js +0 -138
  136. package/src/shapers/Holes.js +0 -77
  137. package/src/shapers/JigSaw.js +0 -161
  138. package/src/shapers/Rectangular.js +0 -78
  139. package/src/shapers/Shaper.js +0 -386
  140. package/src/shapers/TriangularJigSaw.js +0 -121
  141. package/src/skins/BlueSkin.js +0 -80
  142. package/src/skins/Counter.js +0 -152
  143. package/src/skins/CustomSkin.js +0 -412
  144. package/src/skins/DefaultSkin.js +0 -376
  145. package/src/skins/EmptySkin.js +0 -82
  146. package/src/skins/GreenSkin.js +0 -94
  147. package/src/skins/MiniSkin.js +0 -130
  148. package/src/skins/OrangeSkin.js +0 -78
  149. package/src/skins/SimpleSkin.js +0 -92
  150. package/src/skins/Skin.js +0 -1021
  151. package/src/skins/assets/actionsIcon.svg +0 -3
  152. package/src/skins/assets/appLogo.svg +0 -8
  153. package/src/skins/assets/basic.css +0 -41
  154. package/src/skins/assets/closeDialogIcon.svg +0 -3
  155. package/src/skins/assets/closeIcon.svg +0 -3
  156. package/src/skins/assets/copyIcon.svg +0 -3
  157. package/src/skins/assets/fullScreenExitIcon.svg +0 -3
  158. package/src/skins/assets/fullScreenIcon.svg +0 -3
  159. package/src/skins/assets/infoIcon.svg +0 -3
  160. package/src/skins/assets/main.css +0 -43
  161. package/src/skins/assets/mainHalf.css +0 -23
  162. package/src/skins/assets/mainTwoThirds.css +0 -23
  163. package/src/skins/assets/mini.css +0 -15
  164. package/src/skins/assets/nextIcon.svg +0 -3
  165. package/src/skins/assets/okDialogIcon.svg +0 -3
  166. package/src/skins/assets/prevIcon.svg +0 -3
  167. package/src/skins/assets/reports.css +0 -156
  168. package/src/skins/assets/reportsIcon.svg +0 -3
  169. package/src/skins/assets/scoreIcon.svg +0 -3
  170. package/src/skins/assets/simple.css +0 -16
  171. package/src/skins/assets/simpleHalf.css +0 -11
  172. package/src/skins/assets/simpleTwoThirds.css +0 -11
  173. package/src/skins/assets/timeIcon.svg +0 -4
  174. package/src/skins/assets/waitAnim.css +0 -54
  175. package/src/skins/assets/waitImgBig.svg +0 -3
  176. package/src/skins/assets/waitImgSmall.svg +0 -3
  177. package/webpack.config.mjs +0 -169
package/src/AWT.js DELETED
@@ -1,2067 +0,0 @@
1
- /**
2
- * File : AWT.js
3
- * Created : 12/04/2015
4
- * By : Francesc Busquets <francesc@gmail.com>
5
- *
6
- * JClic.js
7
- * An HTML5 player of JClic activities
8
- * https://projectestac.github.io/jclic.js
9
- *
10
- * @source https://github.com/projectestac/jclic.js
11
- *
12
- * @license EUPL-1.2
13
- * @licstart
14
- * (c) 2000-2020 Educational Telematic Network of Catalonia (XTEC)
15
- *
16
- * Licensed under the EUPL, Version 1.1 or -as soon they will be approved by
17
- * the European Commission- subsequent versions of the EUPL (the "Licence");
18
- * You may not use this work except in compliance with the Licence.
19
- *
20
- * You may obtain a copy of the Licence at:
21
- * https://joinup.ec.europa.eu/software/page/eupl
22
- *
23
- * Unless required by applicable law or agreed to in writing, software
24
- * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
25
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26
- * Licence for the specific language governing permissions and limitations
27
- * under the Licence.
28
- * @licend
29
- * @module
30
- */
31
-
32
- /* global console, window */
33
-
34
- import $ from 'jquery';
35
- import { settings, findParentsWithChild, getBoolean, getAttr, setAttr, checkColor, colorHasTransparency, fx } from './Utils.js';
36
- import WebFont from 'webfontloader';
37
-
38
- /**
39
- * Font contains properties and provides methods to manage fonts
40
- */
41
- export class Font {
42
- /**
43
- * Font constructor
44
- * @param {string} [family='Arial']
45
- * @param {number} [size=17]
46
- * @param {number} [bold=0]
47
- * @param {number} [italic=0]
48
- * @param {string} [variant='']
49
- */
50
- constructor(family, size, bold, italic, variant) {
51
- if (family)
52
- this.family = family;
53
- if (typeof size === 'number')
54
- this.size = size;
55
- if (bold)
56
- this.bold = bold;
57
- if (italic)
58
- this.italic = italic;
59
- if (variant)
60
- this.variant = variant;
61
- this._metrics = { ascent: -1, descent: -1, height: -1 };
62
- }
63
-
64
- /**
65
- * Finds the XML elements with typeface specifications, checks its value against the font
66
- * substitution list, replacing the `family` attribute and loading the alternative font when needed.
67
- * @param {external:jQuery} $tree - The xml element to be processed
68
- * @param {object} [options] - Optional param that can contain a `fontSubstitutions` attribute with
69
- * a substition table to be added to {@link module:AWT.Font.SUBSTITUTIONS SUBSTITUTIONS}
70
- */
71
- static checkTree($tree, options) {
72
- let substitutions = Font.SUBSTITUTIONS;
73
- // Load own fonts and remove it from the substitution table
74
- if (options && options.ownFonts) {
75
- options.ownFonts.forEach(name => {
76
- // Check WebFont as a workaround to avoid problems with a different version of `webfontloader` in agora.xtec.cat
77
- if (Font.ALREADY_LOADED_FONTS.indexOf(name) < 0 && WebFont && WebFont.load) {
78
- WebFont.load({ custom: { families: [name] } });
79
- Font.ALREADY_LOADED_FONTS.push(name);
80
- delete substitutions[name.trim().toLowerCase()];
81
- }
82
- });
83
- }
84
-
85
- // Add custom font substitutions
86
- if (options && options.fontSubstitutions)
87
- //substitutions = Object.assign({}, substitutions, options.fontSubstitutions)
88
- substitutions = $.extend(Object.create(substitutions), options.fontSubstitutions);
89
-
90
- if ($tree.jquery)
91
- $tree.find('style[family],font[family]').each((_n, style) => {
92
- const $style = $(style),
93
- name = $style.attr('family').trim().toLowerCase();
94
- if (name in substitutions) {
95
- const newName = substitutions[name];
96
- if (newName !== '') {
97
- Font.loadGoogleFont(newName);
98
- $style.attr('family', newName);
99
- }
100
- }
101
- });
102
- else {
103
- findParentsWithChild($tree, 'family').forEach(parent => {
104
- if (typeof parent.family === 'string') {
105
- const name = parent.family;
106
- if (Font.GOOGLEFONTS.includes(name))
107
- Font.loadGoogleFont(name);
108
- else {
109
- const newName = substitutions[name.trim().toLowerCase()];
110
- if (newName) {
111
- Font.loadGoogleFont(newName);
112
- parent.family = newName;
113
- }
114
- }
115
- }
116
- });
117
- }
118
- }
119
-
120
- /**
121
- * Try to load a specific font from [http://www.google.com/fonts]
122
- * @param {string} name - The font family name
123
- */
124
- // Check WebFont as a workaround to avoid problems with a different version of `webfontloader` in agora.xtec.cat
125
- static loadGoogleFont(name) {
126
- if (name && !Font.ALREADY_LOADED_FONTS.includes(name) && WebFont && WebFont.load) {
127
- WebFont.load({ google: { families: [name] } });
128
- Font.ALREADY_LOADED_FONTS.push(name);
129
- }
130
- }
131
-
132
- /**
133
- * Try to load a set of Google fonts
134
- * @param {string[]} fonts - An array of font names
135
- */
136
- static loadGoogleFonts(fonts) {
137
- if (fonts && fonts.forEach)
138
- fonts.forEach(name => Font.loadGoogleFont(name));
139
- }
140
-
141
- /**
142
- * Reads the properties of this Font from an XML element
143
- * @param {external:jQuery} $xml - The xml element to be parsed
144
- * @returns {module:AWT.Font}
145
- */
146
- setProperties($xml) {
147
- if ($xml.attr('family'))
148
- this.family = $xml.attr('family');
149
- if ($xml.attr('size'))
150
- this.size = Number($xml.attr('size'));
151
- if ($xml.attr('bold'))
152
- this.bold = getBoolean($xml.attr('bold'));
153
- if ($xml.attr('italic'))
154
- this.italic = getBoolean($xml.attr('italic'));
155
- if ($xml.attr('variant'))
156
- this.variant = $xml.attr('variant');
157
- return this;
158
- }
159
-
160
- /**
161
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
162
- * parent references, constants and also attributes retaining the default value.
163
- * The resulting object is commonly usued to serialize elements in JSON format.
164
- * @returns {object} - The resulting object, with minimal attrributes
165
- */
166
- getAttributes() {
167
- return getAttr(this, ['family|Arial', 'size|17', 'bold|0', 'italic|0', 'variant']);
168
- }
169
-
170
- /**
171
- * Reads the properties of this Font from a data object
172
- * @param {object} data - The data object to be parsed
173
- * @returns {module:AWT.Font}
174
- */
175
- setAttributes(data) {
176
- return setAttr(this, data, ['family', 'size', 'bold', 'italic', 'variant']);
177
- }
178
-
179
- /**
180
- * Allows to change the `size` member, recalculating the vertical metrics.
181
- * @param {number} size - The new size to set
182
- * @returns {module:AWT.Font}
183
- */
184
- setSize(size) {
185
- const currentSize = this.size;
186
- this.size = size;
187
- if (currentSize !== size)
188
- this._metrics.height = -1;
189
- return this;
190
- }
191
-
192
- /**
193
- * Increases or decreases the current font size by the specified amount
194
- * @param {number} amount - The amount to increase or decrease current size
195
- * @returns {module:AWT.Font}
196
- */
197
- zoom(amount) {
198
- return this.setSize(this.size + amount);
199
- }
200
-
201
- /**
202
- * Calculates the font metrics
203
- * @returns {Object} - The font metrics
204
- */
205
- getMetrics() {
206
- if (this._metrics.height < 0) {
207
- // Look for an equivalent font already calculated
208
- const font = Font.ALREADY_CALCULATED_FONTS.find(font => font.equals(this));
209
- if (font)
210
- Object.assign(this._metrics, font._metrics);
211
-
212
- if (this._metrics.height < 0) {
213
- this._calcHeight();
214
- if (this._metrics.height > 0)
215
- Font.ALREADY_CALCULATED_FONTS.push(this);
216
- }
217
- }
218
- return this._metrics;
219
- }
220
-
221
- /**
222
- * Calculates the font metrics and returns its height
223
- * @returns {number} - The font height
224
- */
225
- getHeight() {
226
- return this.getMetrics().height;
227
- }
228
-
229
- /**
230
- * Translates the Font properties into CSS statements
231
- * @param {object} css - The object where to add CSS properties. When null or undefined, a new
232
- * object will be created and returned.
233
- * @returns {object} - A set of CSS property-values pairs, ready to be used by JQuery
234
- * [.css(properties)](http://api.jquery.com/css/#css-properties).
235
- */
236
- toCss(css) {
237
- if (!css)
238
- css = {};
239
- css['font-family'] = this.family;
240
- css['font-size'] = `${this.size}px`;
241
- if (this.hasOwnProperty('bold'))
242
- css['font-weight'] = this.bold ? 'bold' : 'normal';
243
- if (this.hasOwnProperty('italic'))
244
- css['font-style'] = this.italic ? 'italic' : 'normal';
245
- if (this.hasOwnProperty('variant'))
246
- css['font-variant'] = this.variant;
247
- return css;
248
- }
249
-
250
- /**
251
- * Gets the codification of this font in a single string, suitable to be used in a `font`
252
- * CSS attribute.
253
- * @returns {string} - A string with all the CSS font properties concatenated
254
- */
255
- cssFont() {
256
- return `${this.italic ? 'italic ' : 'normal'} ${this.variant === '' ? 'normal' : this.variant} ${this.bold ? 'bold ' : 'normal'} ${this.size}px ${this.family}`;
257
- }
258
-
259
- /**
260
- * The {@link https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics TextMetrics} object used
261
- * by {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D CanvasRenderingContext2D}
262
- * does not provide a `heigth` value for rendered text.
263
- * This {@link http://stackoverflow.com/questions/1134586/how-can-you-find-the-height-of-text-on-an-html-canvas stackoverflow question}
264
- * has an excellent response by Daniel Earwicker explaining how to measure the
265
- * vertical dimension of rendered text using a `span` element.
266
- * The code has been slighty adapted to deal with Font objects.
267
- *
268
- * _Warning_: Do not call this method direcly. Use {@link module:AWT.Font#getHeight getHeight()} or {@link module:AWT.Font#getMetrics getMetrics()} instead
269
- *
270
- * @returns {module:AWT.Font}
271
- */
272
- _calcHeight() {
273
- const
274
- $text = $('<span/>').html('Hg').css(this.toCss()),
275
- $block = $('<div/>').css({ display: 'inline-block', width: '1px', height: '0px' }),
276
- $div = $('<div/>').append($text, $block);
277
-
278
- $('body').append($div);
279
- try {
280
- $block.css({ verticalAlign: 'baseline' });
281
- this._metrics.ascent = $block.offset().top - $text.offset().top;
282
- $block.css({ verticalAlign: 'bottom' });
283
- this._metrics.height = $block.offset().top - $text.offset().top;
284
- this._metrics.descent = this._metrics.height - this._metrics.ascent;
285
- } finally {
286
- $div.remove();
287
- }
288
- return this;
289
- }
290
-
291
- /**
292
- * Checks if two Font objects are equivalent
293
- * @param {module:AWT.Font} font - The Font object to compare against this one
294
- * @returns {boolean} - `true` if both objects are equivalent, `false` otherwise
295
- */
296
- equals(font) {
297
- return this.family === font.family &&
298
- this.size === font.size &&
299
- this.bold === font.bold &&
300
- this.italic === font.italic &&
301
- this.variant === font.variant;
302
- }
303
- }
304
-
305
-
306
- /**
307
- * Array of font objects with already calculated heights */
308
- Font.ALREADY_CALCULATED_FONTS = [];
309
-
310
- /**
311
- * Array of font names already loaded from Google Fonts, or generic names provided by browsers by default
312
- * See: https://developer.mozilla.org/en-US/docs/Web/CSS/font-family */
313
- Font.ALREADY_LOADED_FONTS = ['serif', 'sans-serif', 'monospace', 'cursive', 'fantasy'];
314
-
315
- /**
316
- * Google Fonts equivalent for special fonts used in some JClic projects.
317
- * More substitutions can be added to the list for specific projects indicating a
318
- * `fontSubstitutions` object in the `data-options` attribute of the HTML `div` element
319
- * containing the player.
320
- * For example:
321
- * `<div class ="JClic" data-project="demo.jclic" data-options='{"fontSubstitutions":{"arial":"Arimo"}}'/>`
322
- */
323
- Font.SUBSTITUTIONS = {
324
- // Lowercase versions of JDK Logical Fonts (see: https://docs.oracle.com/javase/tutorial/2d/text/fonts.html)
325
- 'dialog': 'sans-serif',
326
- 'dialoginput': 'sans-serif',
327
- 'monospaced': 'monospace',
328
- //'serif': 'serif',
329
- 'sansserif': 'sans-serif',
330
- // Other fonts commonly used in JClic activities, mapped to similar Google Fonts
331
- 'abc': 'Kalam',
332
- 'a.c.m.e. secret agent': 'Permanent Marker',
333
- 'comic sans ms': 'Patrick Hand',
334
- 'impact': 'Oswald',
335
- 'massallera': 'Vibur',
336
- 'memima': 'Vibur',
337
- 'memima_n1': 'Vibur',
338
- 'memima_n2': 'Vibur',
339
- 'memimas-regularalternate': 'Vibur',
340
- 'palmemim': 'Vibur',
341
- 'zurichcalligraphic': 'Felipa'
342
- };
343
- /**
344
- * Google Fonts currently used in substitutions
345
- */
346
- Font.GOOGLEFONTS = [
347
- 'Kalam', 'Permanent Marker', 'Patrick Hand', 'Oswald', 'Vibur', 'Felipa',
348
- ];
349
-
350
- Object.assign(Font.prototype, {
351
- /**
352
- * The `font-family` property
353
- * @name module:AWT.Font#family
354
- * @type {string} */
355
- family: 'Arial',
356
- /**
357
- * The font size
358
- * __Warning__: Do not change `size` directly. Use {@link module:AWT.Font#setSize setSize()} instead.
359
- * @name module:AWT.Font#size
360
- * @type {number} */
361
- size: 17,
362
- /**
363
- * The font _bold_ value
364
- * @name module:AWT.Font#bold
365
- * @type {number} */
366
- bold: 0,
367
- /**
368
- * The font _italic_ value
369
- * @name module:AWT.Font#italic
370
- * @type {number} */
371
- italic: 0,
372
- /**
373
- * The font _variant_ value
374
- * @name module:AWT.Font#variant
375
- * @type {string}*/
376
- variant: '',
377
- /**
378
- * The font *_metrics* property contains the values for `ascent`, `descent` and `height`
379
- * attributes. Vertical font metrics are calculated in
380
- * {@link module:AWT.Font#_calcHeight|_calcHeight()} as needed.
381
- * @name module:AWT.Font#_metrics
382
- * @private
383
- * @type {{ascent: number, descent: number, height: number}} */
384
- _metrics: { ascent: -1, descent: -1, height: -1 },
385
- });
386
-
387
- /**
388
- * Contains parameters and methods to draw complex color gradients
389
- */
390
- export class Gradient {
391
- /**
392
- * Gradient constructor
393
- * @param {string} c1 - The initial color, in any CSS-valid form.
394
- * @param {string} c2 - The final color, in any CSS-valid form.
395
- * @param {number} [angle=0] - The inclination of the gradient relative to the horizontal line.
396
- * @param {number} [cycles=1] - The number of times the gradient will be repeated.
397
- */
398
- constructor(c1, c2, angle, cycles) {
399
- if (c1)
400
- this.c1 = c1;
401
- if (c2)
402
- this.c2 = c2;
403
- if (typeof angle === 'number')
404
- this.angle = angle % 360;
405
- if (typeof cycles === 'number')
406
- this.cycles = cycles;
407
- }
408
-
409
- /**
410
- * Reads the properties of this Gradient from an XML element
411
- * @param {external:jQuery} $xml - The xml element to be parsed
412
- * @returns {module:AWT.Gradient}
413
- */
414
- setProperties($xml) {
415
- this.c1 = checkColor($xml.attr('source'), 'black');
416
- this.c2 = checkColor($xml.attr('dest'), 'white');
417
- this.angle = Number($xml.attr('angle') || 0) % 360;
418
- this.cycles = Number($xml.attr('cycles') || 1);
419
- return this;
420
- }
421
-
422
- /**
423
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
424
- * parent references, constants and also attributes retaining the default value.
425
- * The resulting object is commonly usued to serialize elements in JSON format.
426
- * @returns {object} - The resulting object, with minimal attrributes
427
- */
428
- getAttributes() {
429
- return getAttr(this, [
430
- 'c1', 'c2', 'angle|0', 'cycles|1'
431
- ]);
432
- }
433
-
434
- /**
435
- * Reads the properties of this Gradient from a data object
436
- * @param {object} data - The data object to be parsed
437
- * @returns {module:AWT.Gradient}
438
- */
439
- setAttributes(data) {
440
- return setAttr(this, data, ['c1', 'c2', 'angle', 'cycles']);
441
- }
442
-
443
- /**
444
- * Creates a {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient|CanvasGradient}
445
- * based on the provided context and rectangle.
446
- * @param {external:CanvasRenderingContext2D} ctx - The 2D rendering context
447
- * @param {module:AWT.Rectangle} rect - The rectangle where this gradient will be applied to
448
- * @returns {module:AWT.Gradient}
449
- */
450
- getGradient(ctx, rect) {
451
- const
452
- p2 = rect.getOppositeVertex(),
453
- gradient = ctx.createLinearGradient(rect.pos.x, rect.pos.y, p2.x, p2.y),
454
- step = 1 / Math.max(this.cycles, 1);
455
- for (let i = 0; i <= this.cycles; i++)
456
- gradient.addColorStop(i * step, i % 2 ? this.c1 : this.c2);
457
- return gradient;
458
- }
459
-
460
- /**
461
- * Gets the CSS 'linear-gradient' expression of this Gradient
462
- * @returns {string} - A string ready to be used as a value for the `linear-gradient` CSS attribute
463
- */
464
- getCss() {
465
- let result = `linear-gradient(${(this.angle + 90)}deg, ${this.c1}, ${this.c2}`;
466
- for (let i = 1; i < this.cycles; i++)
467
- result = `${result}, ${i % 2 > 0 ? this.c1 : this.c2}`;
468
- return `${result})`;
469
- }
470
-
471
- /**
472
- * Checks if any of the gradient colors has transparency
473
- * @returns {boolean} - `true` if this gradient uses colors with transparency, `false` otherwise.
474
- */
475
- hasTransparency() {
476
- return colorHasTransparency(this.c1) || colorHasTransparency(this.c2);
477
- }
478
- }
479
-
480
- Object.assign(Gradient.prototype, {
481
- /**
482
- * Initial color
483
- * @name module:AWT.Gradient#c1
484
- * @type {string} */
485
- c1: 'white',
486
- /**
487
- * Final color
488
- * @name module:AWT.Gradient#c2
489
- * @type {string} */
490
- c2: 'black',
491
- /**
492
- * Tilt angle
493
- * @name module:AWT.Gradient#angle
494
- * @type {number} */
495
- angle: 0,
496
- /**
497
- * Number of repetitions of the gradient
498
- * @name module:AWT.Gradient#cycles
499
- * @type {number} */
500
- cycles: 1,
501
- });
502
-
503
- /**
504
- * Contains properties used to draw lines in HTML `canvas` elements.
505
- * @see {@link http://bucephalus.org/text/CanvasHandbook/CanvasHandbook.html#line-caps-and-joins}
506
- */
507
- export class Stroke {
508
- /**
509
- * Stroke constructor
510
- * @param {number} [lineWidth=1] - The line width of the stroke
511
- * @param {string} [lineCap='butt'] - The line ending type. Possible values are: `butt`, `round`
512
- * and `square`.
513
- * @param {string} [lineJoin='miter'] - The type of drawing used when two lines join. Possible
514
- * values are: `round`, `bevel` and `miter`.
515
- * @param {number} [miterLimit=10] - The ratio between the miter length and half `lineWidth`.
516
- */
517
- constructor(lineWidth, lineCap, lineJoin, miterLimit) {
518
- if (typeof lineWidth === 'number')
519
- this.lineWidth = lineWidth;
520
- if (lineCap)
521
- this.lineCap = lineCap;
522
- if (lineJoin)
523
- this.lineJoin = lineJoin;
524
- if (typeof miterLimit === 'number')
525
- this.miterLimit = miterLimit;
526
- }
527
-
528
- /**
529
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
530
- * parent references, constants and also attributes retaining the default value.
531
- * The resulting object is commonly usued to serialize elements in JSON format.
532
- * @returns {object} - The resulting object, with minimal attrributes
533
- */
534
- getAttributes() {
535
- return getAttr(this, [
536
- 'lineWidth|1', 'lineCap|butt', 'lineJoin|miter', 'miterLimit|10',
537
- ]);
538
- }
539
-
540
- /**
541
- * Reads the properties of this Stroke from a data object
542
- * @param {object} data - The data object to be parsed
543
- * @returns {module:AWT.Stroke}
544
- */
545
- setAttributes(data) {
546
- return setAttr(this, data, ['lineWidth', 'lineCap', 'lineJoin', 'miterLimit']);
547
- }
548
-
549
- /**
550
- * Sets the properties of this stroke to a CanvasRenderingContext2D
551
- * @param {external:CanvasRenderingContext2D} ctx - The canvas 2D rendering context
552
- * @returns {external:CanvasRenderingContext2D}
553
- */
554
- setStroke(ctx) {
555
- ctx.lineWidth = this.lineWidth;
556
- ctx.lineCap = this.lineCap;
557
- ctx.lineJoin = this.lineJoin;
558
- ctx.miterLimit = this.miterLimit;
559
- return ctx;
560
- }
561
- }
562
-
563
- Object.assign(Stroke.prototype, {
564
- /**
565
- * The line width
566
- * @name module:AWT.Stroke#lineWidth
567
- * @type {number} */
568
- lineWidth: 1.0,
569
- /**
570
- * The line ending type (`butt`, `round` or `square`)
571
- * @name module:AWT.Stroke#lineCap
572
- * @type {string} */
573
- lineCap: 'butt',
574
- /**
575
- * The drawing used when two lines join (`round`, `bevel` or `miter`)
576
- * @name module:AWT.Stroke#lineJoin
577
- * @type {string} */
578
- lineJoin: 'miter',
579
- /**
580
- * Ratio between the miter length and half `lineWidth`
581
- * @name module:AWT.Stroke#miterLimit
582
- * @type {number} */
583
- miterLimit: 10.0,
584
- });
585
-
586
- /**
587
- * Contains the `x` andy `y` coordinates of a point, and provides some useful methods.
588
- */
589
- export class Point {
590
- /**
591
- * Point constructor
592
- * @param {number|Point} x - When `x` is an `Point` object, a clone of it will be created.
593
- * @param {number} [y] - Not used when `x` is an `Point`
594
- */
595
- constructor(x, y) {
596
- if (x instanceof Point) {
597
- // Special case: constructor passing another point as unique parameter
598
- this.x = x.x;
599
- this.y = x.y;
600
- } else {
601
- this.x = x || 0;
602
- this.y = y || 0;
603
- }
604
- }
605
-
606
- /**
607
- * Reads the properties of this Point from an XML element
608
- * @param {external:jQuery} $xml - The xml element to be parsed
609
- * @returns {module:AWT.Point}
610
- */
611
- setProperties($xml) {
612
- this.x = Number($xml.attr('x'));
613
- this.y = Number($xml.attr('y'));
614
- return this;
615
- }
616
-
617
- /**
618
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
619
- * parent references, constants and also attributes retaining the default value.
620
- * The resulting object is commonly usued to serialize elements in JSON format.
621
- * @returns {object} - The resulting object, with minimal attrributes
622
- */
623
- getAttributes() {
624
- return getAttr(this, ['x', 'y']);
625
- }
626
-
627
- /**
628
- * Reads the properties of this Point from a data object
629
- * @param {object} data - The data object to be parsed
630
- * @returns {module:AWT.Point}
631
- */
632
- setAttributes(data) {
633
- return setAttr(this, data, ['x', 'y']);
634
- }
635
-
636
- /**
637
- * Moves this Point to a new position, by a specified displacement
638
- * @param {Point|Dimension} delta - The amount to move
639
- * @returns {module:AWT.Point}
640
- */
641
- moveBy(delta) {
642
- this.x += delta.x || delta.width || 0;
643
- this.y += delta.y || delta.height || 0;
644
- return this;
645
- }
646
-
647
- /**
648
- * Moves this Point to a new position
649
- * @param {number|Point} newPos - The new position, or a x coordinate
650
- * @param {number} [y] - `null` or `undefined` when `newPos` is a Point
651
- * @returns {module:AWT.Point}
652
- */
653
- moveTo(newPos, y) {
654
- if (typeof newPos === 'number') {
655
- this.x = newPos;
656
- this.y = y;
657
- } else {
658
- this.x = newPos.x;
659
- this.y = newPos.y;
660
- }
661
- return this;
662
- }
663
-
664
- /**
665
- * Multiplies the `x` and `y` coordinates by a specified `delta`
666
- * @param {Point|Dimension} delta - The amount to multiply by.
667
- * @returns {module:AWT.Point}
668
- */
669
- multBy(delta) {
670
- this.x *= delta.x || delta.width || 0;
671
- this.y *= delta.y || delta.height || 0;
672
- return this;
673
- }
674
-
675
- /**
676
- * Checks if two points are at the same place
677
- * @param {module:AWT.Point} p - The Point to check against to
678
- * @returns {boolean}
679
- */
680
- equals(p) {
681
- return this.x === p.x && this.y === p.y;
682
- }
683
-
684
- /**
685
- * Calculates the distance between two points
686
- * @param {module:AWT.Point} point - The Point to calculate the distance against to
687
- * @returns {number} - The distance between the two points.
688
- */
689
- distanceTo(point) {
690
- return Math.sqrt(Math.pow(this.x - point.x, 2), Math.pow(this.y - point.y, 2));
691
- }
692
-
693
- /**
694
- * Clones this point
695
- * @returns {module:AWT.Point}
696
- */
697
- clone() {
698
- return new Point(this);
699
- }
700
- }
701
-
702
- Object.assign(Point.prototype, {
703
- /**
704
- * @name module:AWT.Point#x
705
- * @type {number} */
706
- x: 0,
707
- /**
708
- * @name module:AWT.Point#y
709
- * @type {number} */
710
- y: 0,
711
- });
712
-
713
- /**
714
- * This class encapsulates `width` and `height` properties.
715
- */
716
- export class Dimension {
717
- /**
718
- * Dimension constructor
719
- * @param {number|Point} w - The width of this Dimension, or the upper-left vertex of a
720
- * virtual Rectangle
721
- * @param {number|Point} h - The height of this Dimension, or the bottom-right vertex of a
722
- * virtual Rectangle
723
- */
724
- constructor(w, h) {
725
- if (w instanceof Point && h instanceof Point) {
726
- this.width = h.x - w.x;
727
- this.height = h.y - w.y;
728
- } else {
729
- this.width = w || 0;
730
- this.height = h || 0;
731
- }
732
- }
733
-
734
- /**
735
- * Reads the properties of this Dimension from an XML element
736
- * @param {external:jQuery} $xml - The xml element to be parsed
737
- * @returns {module:AWT.Dimension}
738
- */
739
- setProperties($xml) {
740
- this.width = Number($xml.attr('width'));
741
- this.height = Number($xml.attr('height'));
742
- return this;
743
- }
744
-
745
- /**
746
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
747
- * parent references, constants and also attributes retaining the default value.
748
- * The resulting object is commonly usued to serialize elements in JSON format.
749
- * @returns {object} - The resulting object, with minimal attrributes
750
- */
751
- getAttributes() {
752
- return getAttr(this, ['width', 'height']);
753
- }
754
-
755
- /**
756
- * Reads the properties of this Dimension from a data object
757
- * @param {object} data - The data object to be parsed
758
- * @returns {module:AWT.Dimension}
759
- */
760
- setAttributes(data) {
761
- return setAttr(this, data, ['width', 'height']);
762
- }
763
-
764
- /**
765
- * Check if two dimensions are equivalent
766
- * @param {module:AWT.Dimension} d
767
- * @returns {boolean}
768
- */
769
- equals(d) {
770
- return this.width === d.width && this.height === d.height;
771
- }
772
-
773
- /**
774
- * Multiplies the `w` and `h` co-ordinates by a specified `delta`
775
- * @param {Point|Dimension} delta
776
- * @returns {module:AWT.Dimension}
777
- */
778
- multBy(delta) {
779
- this.width *= delta.x || delta.width || 0;
780
- this.height *= delta.y || delta.height || 0;
781
- return this;
782
- }
783
-
784
- /**
785
- * Sets new values for width and height.
786
- * `width` can be a number or another `Dimension` object
787
- * @param {number|Dimension} width - The new width, or a full Dimension to copy it from.
788
- * @param {number} [height] - Not used when `width` is a Dimension
789
- * @returns {module:AWT.Dimension}
790
- */
791
- setDimension(width, height) {
792
- if (width instanceof Dimension) {
793
- height = width.height;
794
- width = width.width;
795
- }
796
- this.width = width;
797
- this.height = height;
798
- return this;
799
- }
800
-
801
- /**
802
- * Calculates the area of a Rectangle with this dimension
803
- * @returns {number} The resulting area
804
- */
805
- getSurface() {
806
- return this.width * this.height;
807
- }
808
- }
809
-
810
- Object.assign(Dimension.prototype, {
811
- /**
812
- * @name module:AWT.Dimension#width
813
- * @type {number} */
814
- width: 0,
815
- /**
816
- * @name module:AWT.Dimension#height
817
- * @type {number} */
818
- height: 0,
819
- });
820
-
821
- /**
822
- * Shape is a generic abstract class for rectangles, ellipses and stroke-free shapes.
823
- * @abstract
824
- */
825
- export class Shape {
826
- /**
827
- * Shape constructor
828
- * @param {module:AWT.Point} pos - The top-left coordinates of this Shape
829
- */
830
- constructor(pos) {
831
- this.pos = pos || new Point();
832
- }
833
-
834
- /**
835
- * Shifts the shape a specified amount in horizontal and vertical directions
836
- * @param {Point|Dimension} delta - The amount to shift the Shape
837
- * @returns {module:AWT.Shape}
838
- */
839
- moveBy(delta) {
840
- this.pos.moveBy(delta);
841
- return this;
842
- }
843
-
844
- /**
845
- * Moves this shape to a new position
846
- * @param {module:AWT.Point} newPos - The new position of the shape
847
- * @returns {module:AWT.Shape}
848
- */
849
- moveTo(newPos) {
850
- this.pos.moveTo(newPos);
851
- return this;
852
- }
853
-
854
- /**
855
- * Gets the enclosing {@link module:AWT.Rectangle Rectangle} of this Shape.
856
- * @returns {module:AWT.Rectangle}
857
- */
858
- getBounds() {
859
- return new Rectangle(this.pos);
860
- }
861
-
862
- /**
863
- * Checks if two shapes are equivalent.
864
- * @param {module:AWT.Shape} p - The Shape to compare against
865
- * @returns {boolean}
866
- */
867
- equals(p) {
868
- return this.pos.equals(p.pos);
869
- }
870
-
871
- /**
872
- * Multiplies the dimension of the Shape by the specified `delta` amount.
873
- * @param {Point|Dimension} _delta - Object containing the X and Y ratio to be scaled.
874
- * @returns {module:AWT.Shape}
875
- */
876
- scaleBy(_delta) {
877
- // Nothing to scale in abstract shapes
878
- return this;
879
- }
880
-
881
- /**
882
- * Gets a clone of this shape moved to the `pos` component of the rectangle and scaled
883
- * by its `dim` value.
884
- * @param {module:AWT.Rectangle} rect - The rectangle to be taken as a base for moving and scaling
885
- * this shape.
886
- * @returns {module:AWT.Shape}
887
- */
888
- getShape(rect) {
889
- return this.clone().scaleBy(rect.dim).moveBy(rect.pos);
890
- }
891
-
892
- /**
893
- * Checks if the provided {@link module:AWT.Point} is inside this shape.
894
- * @param {module:AWT.Point} _p - The point to check
895
- * @returns {boolean}
896
- */
897
- contains(_p) {
898
- // Nothing to check in abstract shapes
899
- return false;
900
- }
901
-
902
- /**
903
- * Checks if the provided {@link module:AWT.Rectangle Rectangle} `r` intersects with this shape.
904
- * @param {module:AWT.Rectangle} _r
905
- * @returns {boolean}
906
- */
907
- intersects(_r) {
908
- // Nothing to check in abstract shapes
909
- return false;
910
- }
911
-
912
- /**
913
- * Fills the Shape with the current style in the provided HTML canvas context
914
- * @param {external:CanvasRenderingContext2D} ctx - The canvas 2D rendering context where to fill this shape.
915
- * @param {module:AWT.Rectangle} [dirtyRegion] - The context region to be updated. Used as clipping
916
- * region when drawing.
917
- * @returns {external:CanvasRenderingContext2D} - The provided rendering context
918
- */
919
- fill(ctx, dirtyRegion) {
920
- ctx.save();
921
- if (dirtyRegion && dirtyRegion.getSurface() > 0) {
922
- // Clip the dirty region
923
- ctx.beginPath();
924
- ctx.rect(dirtyRegion.pos.x, dirtyRegion.pos.y, dirtyRegion.dim.width, dirtyRegion.dim.height);
925
- ctx.clip();
926
- }
927
- // Prepare shape path and fill
928
- this.preparePath(ctx);
929
- ctx.fill();
930
- ctx.restore();
931
- return ctx;
932
- }
933
-
934
- /**
935
- * Draws this shape in the provided HTML canvas 2D rendering context.
936
- * @param {external:CanvasRenderingContext2D} ctx - The canvas 2D rendering context where to draw the shape.
937
- * @returns {external:CanvasRenderingContext2D} - The provided rendering context
938
- */
939
- stroke(ctx) {
940
- this.preparePath(ctx);
941
- ctx.stroke();
942
- return ctx;
943
- }
944
-
945
- /**
946
- * Prepares an HTML canvas 2D rendering context with a path that can be used to stroke a line,
947
- * to fill a surface or to define a clipping region.
948
- * @param {external:CanvasRenderingContext2D} ctx
949
- * @returns {external:CanvasRenderingContext2D} - The provided rendering context
950
- */
951
- preparePath(ctx) {
952
- // Nothing to do in abstract shapes
953
- return ctx;
954
- }
955
-
956
- /**
957
- * Creates a clipping region on the specified HTML canvas 2D rendering context
958
- * @param {external:CanvasRenderingContext2D} ctx - The rendering context
959
- * @param {string} [fillRule='nonzero'] - Can be 'nonzero' (default when not set) or 'evenodd'
960
- * @returns {external:CanvasRenderingContext2D} - The provided rendering context
961
- */
962
- clip(ctx, fillRule) {
963
- this.preparePath(ctx);
964
- ctx.clip(fillRule || 'nonzero');
965
- return ctx;
966
- }
967
-
968
- /**
969
- * Shorthand method for determining if a Shape is an {@link module:AWT.Rectangle Rectangle}
970
- * @returns {boolean}
971
- */
972
- isRect() {
973
- return false;
974
- }
975
-
976
- /**
977
- * Overwrites the original 'Object.toString' method with a more descriptive text
978
- * @returns {string}
979
- */
980
- toString() {
981
- return `Shape enclosed in ${this.getBounds().getCoords()}`;
982
- }
983
-
984
- /**
985
- * Reads the properties of this Shape from a data object
986
- * @param {object} data - The data object to be parsed
987
- * @returns {module:AWT.Shape}
988
- */
989
- setAttributes(data) {
990
- return Shape.buildShape(data);
991
- /*
992
- return setAttr(this, data, [
993
- 'type',
994
- { key: 'pos', fn: Point },
995
- ]);
996
- */
997
- }
998
-
999
- /**
1000
- * Builds a shape based on the provided `data` object.
1001
- * Data should contain a 'type' member, specifying the type of shape requested ('rect', 'ellipse', 'rectangle' or 'path')
1002
- * @param {object} data - Specific data for this shape
1003
- * @returns {module:AWT.Shape}
1004
- */
1005
- static buildShape(data) {
1006
- const shapeType = (data.type === 'rect' && Rectangle) || (data.type === 'ellipse' && Ellipse) || (data.type === 'path' && Path) || null;
1007
- if (!shapeType) {
1008
- console.log('unknown shape:', data);
1009
- } else
1010
- return (new shapeType()).setAttributes(data);
1011
- }
1012
- }
1013
-
1014
- Object.assign(Shape.prototype, {
1015
- /**
1016
- * Shape type id
1017
- * @name module:AWT.Shape#type
1018
- * @type {string} */
1019
- type: 'shape',
1020
- /**
1021
- * The current position of the shape
1022
- * @name module:AWT.Shape#pos
1023
- * @type {module:AWT.Point} */
1024
- pos: new Point(),
1025
- /**
1026
- * The type of shape (Rectangle, ellipse, path...)
1027
- * @name module:AWT.Shape#type
1028
- * @type {string} */
1029
- type: 'shape',
1030
- });
1031
-
1032
- /**
1033
- * The rectangular {@link module:AWT.Shape} accepts five different sets of parameters:
1034
- * @example
1035
- * // Calling Rectangle() with different sets of parameters
1036
- * // A Point and a Dimension:
1037
- * new Rectangle(pos, dim)
1038
- * // Another Rectangle, to be cloned:
1039
- * new Rectangle(rect)
1040
- * // Two Point objects containing the coordinates of upper-left and lower-right vertexs:
1041
- * new Rectangle(p0, p1)
1042
- * // An array of four numbers with the coordinates of the same vertexs:
1043
- * new Rectangle([x0, y0, x1, y1])
1044
- * // Four single numbers, meaning the same coordinates as above:
1045
- * new Rectangle(x0, y0, x1, y1)
1046
- * @extends module:AWT.Shape
1047
- */
1048
- export class Rectangle extends Shape {
1049
- /**
1050
- * Rectangle constructor
1051
- * @param {Point|Rectangle|number|number[]} pos
1052
- * @param {Dimension|number} [dim]
1053
- * @param {number} [w]
1054
- * @param {number} [h]
1055
- */
1056
- constructor(pos, dim, w, h) {
1057
- let p = pos, d = dim;
1058
- // Special case: constructor with a Rectangle as a unique parameter
1059
- if (pos instanceof Rectangle) {
1060
- d = new Dimension(pos.dim.width, pos.dim.height);
1061
- p = new Point(pos.pos.x, pos.pos.y);
1062
- } else if (pos instanceof Point) {
1063
- p = new Point(pos.x, pos.y);
1064
- if (dim instanceof Dimension)
1065
- d = new Dimension(dim.width, dim.height);
1066
- } else if (pos instanceof Array) {
1067
- // Assume `pos` is an array of numbers indicating: x0, y0, x1, y1
1068
- p = new Point(pos[0], pos[1]);
1069
- d = new Dimension(pos[2] - pos[0], pos[3] - pos[1]);
1070
- } else if (typeof w === 'number' && typeof h === 'number') {
1071
- // width and height passed. Treat all parameters as co-ordinates:
1072
- p = new Point(pos, dim);
1073
- d = new Dimension(w, h);
1074
- }
1075
- super(p);
1076
-
1077
- if (d instanceof Dimension)
1078
- this.dim = d;
1079
- else if (d instanceof Point)
1080
- this.dim = new Dimension(d.x - this.pos.x, d.y - this.pos.y);
1081
- else
1082
- this.dim = new Dimension();
1083
-
1084
- this.type = 'rect';
1085
- }
1086
-
1087
- /**
1088
- * Gets the enclosing {@link module:AWT.Rectangle Rectangle} of this Shape.
1089
- * @returns {module:AWT.Rectangle}
1090
- */
1091
- getBounds() {
1092
- return this;
1093
- }
1094
-
1095
- /**
1096
- * Sets this Rectangle the position and dimension of another one
1097
- * @param {module:AWT.Rectangle} rect
1098
- * @returns {module:AWT.Rectangle}
1099
- */
1100
- setBounds(rect) {
1101
- if (!rect)
1102
- rect = new Rectangle();
1103
- this.pos.x = rect.pos.x;
1104
- this.pos.y = rect.pos.y;
1105
- this.dim.width = rect.dim.width;
1106
- this.dim.height = rect.dim.height;
1107
- return this;
1108
- }
1109
-
1110
- /**
1111
- * Checks if two shapes are equivalent.
1112
- * @param {module:AWT.Shape} r - The Shape to compare against
1113
- * @returns {boolean}
1114
- */
1115
- equals(r) {
1116
- return r instanceof Rectangle && this.pos.equals(r.pos) && this.dim.equals(r.dim);
1117
- }
1118
-
1119
- /**
1120
- * Clones this Rectangle
1121
- * @returns {module:AWT.Rectangle}
1122
- */
1123
- clone() {
1124
- return new Rectangle(this);
1125
- }
1126
-
1127
- /**
1128
- * Multiplies the dimension of the Shape by the specified `delta` amount.
1129
- * @param {Point|Dimension} delta - Object containing the X and Y ratio to be scaled.
1130
- * @returns {module:AWT.Rectangle}
1131
- */
1132
- scaleBy(delta) {
1133
- this.pos.multBy(delta);
1134
- this.dim.multBy(delta);
1135
- return this;
1136
- }
1137
-
1138
- /**
1139
- * Expands the boundaries of this shape. This affects the current position and dimension.
1140
- * @param {number} dx - The amount to grow (or decrease) in horizontal direction
1141
- * @param {number} dy - The amount to grow (or decrease) in vertical direction
1142
- * @returns {module:AWT.Rectangle}
1143
- */
1144
- grow(dx, dy) {
1145
- this.pos.x -= dx;
1146
- this.pos.y -= dy;
1147
- this.dim.width += 2 * dx;
1148
- this.dim.height += 2 * dy;
1149
- return this;
1150
- }
1151
-
1152
- /**
1153
- * Gets the {@link module:AWT.Point} corresponding to the lower-right vertex of the Rectangle.
1154
- * @returns {module:AWT.Point}
1155
- */
1156
- getOppositeVertex() {
1157
- return new Point(this.pos.x + this.dim.width, this.pos.y + this.dim.height);
1158
- }
1159
-
1160
- /**
1161
- * Adds the boundaries of another shape to the current one
1162
- * @param {module:AWT.Shape} shape - The {@link module:AWT.Shape} to be added
1163
- * @returns {module:AWT.Rectangle}
1164
- */
1165
- add(shape) {
1166
- const
1167
- myP2 = this.getOppositeVertex(),
1168
- rectP2 = shape.getBounds().getOppositeVertex();
1169
-
1170
- this.pos.moveTo(
1171
- Math.min(this.pos.x, shape.getBounds().pos.x),
1172
- Math.min(this.pos.y, shape.getBounds().pos.y));
1173
- this.dim.setDimension(
1174
- Math.max(myP2.x, rectP2.x) - this.pos.x,
1175
- Math.max(myP2.y, rectP2.y) - this.pos.y);
1176
- return this;
1177
- }
1178
-
1179
- //
1180
- // Inherits the documentation of `contains` in Shape
1181
- contains(p) {
1182
- const p2 = this.getOppositeVertex();
1183
- return p.x >= this.pos.x && p.x <= p2.x && p.y >= this.pos.y && p.y <= p2.y;
1184
- }
1185
-
1186
- //
1187
- // Inherits the documentation of `intersects` in Shape
1188
- intersects(r) {
1189
- const
1190
- p1 = this.pos, p2 = this.getOppositeVertex(),
1191
- r1 = r.pos, r2 = r.getOppositeVertex();
1192
- return r2.x >= p1.x && r1.x <= p2.x && r2.y >= p1.y && r1.y <= p2.y;
1193
- }
1194
-
1195
- //
1196
- // Inherits the documentation of `preparePath` in Shape
1197
- preparePath(ctx) {
1198
- ctx.beginPath();
1199
- ctx.rect(this.pos.x, this.pos.y, this.dim.width, this.dim.height);
1200
- return ctx;
1201
- }
1202
-
1203
- //
1204
- // Inherits the documentation of `getSurface` in Shape
1205
- getSurface() {
1206
- return this.dim.getSurface();
1207
- }
1208
-
1209
- //
1210
- // Inherits the documentation of `isEmpty` in Shape
1211
- isEmpty() {
1212
- return this.getSurface() === 0;
1213
- }
1214
-
1215
- //
1216
- // Inherits the documentation of `isRect` in Shape
1217
- isRect() {
1218
- return true;
1219
- }
1220
-
1221
- //
1222
- // Inherits the documentation of `toString` in Shape
1223
- toString() {
1224
- return `Rectangle ${this.getCoords()}`;
1225
- }
1226
-
1227
- /**
1228
- * Gets a string with the co-ordinates of the upper-left and lower-right vertexs of this rectangle,
1229
- * (with values rounded to int)
1230
- * @returns {string}
1231
- */
1232
- getCoords() {
1233
- return `[${Math.round(this.pos.x)},${Math.round(this.pos.y)},${Math.round(this.pos.x + this.dim.width)},${Math.round(this.pos.y + this.dim.height)}]`;
1234
- }
1235
-
1236
- /**
1237
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
1238
- * parent references, constants and also attributes retaining the default value.
1239
- * The resulting object is commonly usued to serialize elements in JSON format.
1240
- * @returns {object} - The resulting object, with minimal attrributes
1241
- */
1242
- getAttributes() {
1243
- return getAttr(this, ['type', 'pos', 'dim']);
1244
- }
1245
-
1246
- /**
1247
- * Reads the properties of this Rectangle from a data object
1248
- * @param {object} data - The data object to be parsed
1249
- * @returns {module:AWT.Rectangle}
1250
- */
1251
- setAttributes(data) {
1252
- return setAttr(this, data, [
1253
- 'type',
1254
- { key: 'pos', fn: Point },
1255
- { key: 'dim', fn: Dimension },
1256
- ]);
1257
- }
1258
- }
1259
-
1260
- Object.assign(Rectangle.prototype, {
1261
- /**
1262
- * Shape type id
1263
- * @name module:AWT.Rectangle#type
1264
- * @type {string} */
1265
- type: 'rect',
1266
- /**
1267
- * The {@link module:AWT.Dimension Dimension} of the Rectangle
1268
- * @name module:AWT.Rectangle#dim
1269
- * @type {module:AWT.Dimension} */
1270
- dim: new Dimension(),
1271
- });
1272
-
1273
- /**
1274
- * The Ellipse shape has the same constructor options as {@link module:AWT.Rectangle Rectangle}
1275
- * @extends module:AWT.Rectangle
1276
- */
1277
- export class Ellipse extends Rectangle {
1278
- /**
1279
- * Ellipse constructor
1280
- * @param {Point|Rectangle|number|number[]} pos
1281
- * @param {Dimension|number} [dim]
1282
- * @param {number} [w]
1283
- * @param {number} [h]
1284
- */
1285
- constructor(pos, dim, w, h) {
1286
- super(pos, dim, w, h);
1287
- }
1288
-
1289
- //
1290
- // Inherits the documentation of `preparePath` in Rectangle
1291
- preparePath(ctx) {
1292
-
1293
- // Using the solution 'drawEllipseWithBezier' proposed by Steve Tranby in:
1294
- // [http://jsbin.com/sosugenegi/1/edit] as a response to:
1295
- // [http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas]
1296
- // Thanks Steve!!
1297
-
1298
- const kappa = 0.5522848,
1299
- ox = kappa * this.dim.width / 2, // control point offset horizontal
1300
- oy = kappa * this.dim.height / 2, // control point offset vertical
1301
- xe = this.pos.x + this.dim.width, // x-end
1302
- ye = this.pos.y + this.dim.height, // y-end
1303
- xm = this.pos.x + this.dim.width / 2, // x-middle
1304
- ym = this.pos.y + this.dim.height / 2; // y-middle
1305
-
1306
- ctx.beginPath();
1307
- ctx.moveTo(this.pos.x, ym);
1308
- ctx.bezierCurveTo(this.pos.x, ym - oy, xm - ox, this.pos.y, xm, this.pos.y);
1309
- ctx.bezierCurveTo(xm + ox, this.pos.y, xe, ym - oy, xe, ym);
1310
- ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
1311
- ctx.bezierCurveTo(xm - ox, ye, this.pos.x, ym + oy, this.pos.x, ym);
1312
- ctx.closePath();
1313
- return ctx;
1314
- }
1315
-
1316
- //
1317
- // Inherits the documentation of `contains` in Shape
1318
- contains(p) {
1319
- // First check if the point is inside the enclosing rectangle
1320
- let result = super.contains(p);
1321
- if (result) {
1322
- const
1323
- rx = this.dim.width / 2,
1324
- ry = this.dim.height / 2,
1325
- cx = this.pos.x + rx,
1326
- cy = this.pos.y + ry;
1327
- // Apply the general equation of an ellipse
1328
- // See: [http://math.stackexchange.com/questions/76457/check-if-a-point-is-within-an-ellipse]
1329
- // rx and ry are > 0 because we are inside the enclosing rect,
1330
- // so don't care about division by zero
1331
- result = Math.pow(p.x - cx, 2) / Math.pow(rx, 2) + Math.pow(p.y - cy, 2) / Math.pow(ry, 2) <= 1;
1332
- }
1333
- return result;
1334
- }
1335
-
1336
- //
1337
- // Inherits the documentation of `getSurface` in Rectangle
1338
- getSurface() {
1339
- return Math.PI * this.dim.width / 2 * this.dim.height / 2;
1340
- }
1341
-
1342
- //
1343
- // Inherits the documentation of `equals` in Rectangle
1344
- equals(e) {
1345
- return e instanceof Ellipse && super.equals(e);
1346
- }
1347
-
1348
- //
1349
- // Inherits the documentation of `clone` in Rectangle
1350
- clone() {
1351
- return new Ellipse(this.pos, this.dim);
1352
- }
1353
-
1354
- //
1355
- // Inherits the documentation of `isRect` in Rectangle
1356
- isRect() {
1357
- return false;
1358
- }
1359
-
1360
- //
1361
- // Inherits the documentation of `toString` in Shape
1362
- toString() {
1363
- return `Ellipse enclosed in ${this.getCoords()}`;
1364
- }
1365
- }
1366
-
1367
- Object.assign(Ellipse.prototype, {
1368
- /**
1369
- * Shape type id
1370
- * @name module:AWT.Ellipse#type
1371
- * @type {string} */
1372
- type: 'ellipse',
1373
- });
1374
-
1375
- /**
1376
- * A `Path` is a {@link module:AWT.Shape} formed by a serie of strokes, represented by
1377
- * {@link module:AWT.PathStroke} objects
1378
- * @extends module:AWT.Shape
1379
- */
1380
- export class Path extends Shape {
1381
- /**
1382
- * Path constructor
1383
- * @param {module:AWT.PathStroke[]} strokes - The array of {@link module:AWT.PathStroke} objects defining this Path.
1384
- */
1385
- constructor(strokes) {
1386
- super();
1387
- // Deep copy of the array of strokes
1388
- if (strokes)
1389
- this.setStrokes(strokes);
1390
- }
1391
-
1392
- setStrokes(strokes) {
1393
- this.strokes = [];
1394
- // In [Shaper](Shaper.html) objects, strokes have `action` instead of `type` and `data` instead of `points`
1395
- strokes.forEach(str => this.strokes.push(new PathStroke(str.type || str.action, str.points || str.data)));
1396
- // Calculate the enclosing rectangle
1397
- this.enclosing = new Rectangle();
1398
- this.enclosingPoints = [];
1399
- this.calcEnclosingRect();
1400
- this.pos = this.enclosing.pos;
1401
- return this;
1402
- }
1403
-
1404
- //
1405
- // Inherits the documentation of `clone` in Shape
1406
- clone() {
1407
- return new Path(this.strokes.map(str => str.clone()));
1408
- }
1409
-
1410
- /**
1411
- * Adds a {@link module:AWT.PathStroke} to `strokes`
1412
- * @param {module:AWT.PathStroke} stroke
1413
- */
1414
- addStroke(stroke) {
1415
- this.strokes.push(stroke);
1416
- return this;
1417
- }
1418
-
1419
- /**
1420
- * Calculates the polygon and the rectangle that (approximately) encloses this shape
1421
- * @returns {module:AWT.Rectangle}
1422
- */
1423
- calcEnclosingRect() {
1424
- this.enclosingPoints = [];
1425
- let last = new Point();
1426
- this.strokes.forEach(str => {
1427
- str.getEnclosingPoints(last).forEach(pt => {
1428
- last = new Point(pt);
1429
- this.enclosingPoints.push(last);
1430
- });
1431
- });
1432
-
1433
- let l = this.enclosingPoints.length;
1434
- if (l > 1 && this.enclosingPoints[0].equals(this.enclosingPoints[l - 1])) {
1435
- this.enclosingPoints.pop();
1436
- l--;
1437
- }
1438
- const
1439
- p0 = new Point(this.enclosingPoints[0]),
1440
- p1 = new Point(this.enclosingPoints[0]);
1441
-
1442
- for (let k = 1; k < l; k++) {
1443
- const p = this.enclosingPoints[k];
1444
- // Check if `p` is at left or above `p0`
1445
- p0.x = Math.min(p.x, p0.x);
1446
- p0.y = Math.min(p.y, p0.y);
1447
- // Check if `p` is at right or below `p1`
1448
- p1.x = Math.max(p.x, p1.x);
1449
- p1.y = Math.max(p.y, p1.y);
1450
- }
1451
- this.enclosing.setBounds(new Rectangle(p0, new Dimension(p0, p1)));
1452
- return this.enclosing;
1453
- }
1454
-
1455
- //
1456
- // Inherits the documentation of `getBounds` in Shape
1457
- getBounds() {
1458
- return this.enclosing;
1459
- }
1460
-
1461
- //
1462
- // Inherits the documentation of `moveBy` in Shape
1463
- moveBy(delta) {
1464
- this.strokes.forEach(str => str.moveBy(delta));
1465
- this.enclosingPoints.forEach(pt => pt.moveBy(delta));
1466
- this.enclosing.moveBy(delta);
1467
- return this;
1468
- }
1469
-
1470
- //
1471
- // Inherits the documentation of `moveTo` in Shape
1472
- moveTo(newPos) {
1473
- return this.moveBy(new Dimension(newPos.x - this.pos.x, newPos.y - this.pos.y));
1474
- }
1475
-
1476
- //
1477
- // Inherits the documentation of `equals` in Shape
1478
- // TODO: Implement comparision of complex paths
1479
- equals(_p) {
1480
- return false;
1481
- }
1482
-
1483
- //
1484
- // Inherits the documentation of `scaleBy` in Shape
1485
- scaleBy(delta) {
1486
- this.strokes.forEach(str => str.multBy(delta));
1487
- this.enclosingPoints.forEach(pt => pt.multBy(delta));
1488
- this.enclosing.scaleBy(delta);
1489
- return this;
1490
- }
1491
-
1492
- //
1493
- // Inherits the documentation of `contains` in Shape
1494
- contains(p) {
1495
- let result = this.enclosing.contains(p);
1496
- if (result) {
1497
- // Let's see if the point really lies inside the polygon formed by enclosingPoints
1498
- // Using the "Ray casting algorithm" described in [https://en.wikipedia.org/wiki/Point_in_polygon]
1499
- const N = this.enclosingPoints.length;
1500
- let
1501
- xinters = 0,
1502
- counter = 0,
1503
- p1 = this.enclosingPoints[0];
1504
-
1505
- for (let i = 1; i <= N; i++) {
1506
- const p2 = this.enclosingPoints[i % N];
1507
- if (p.y > Math.min(p1.y, p2.y)) {
1508
- if (p.y <= Math.max(p1.y, p2.y)) {
1509
- if (p.x <= Math.max(p1.x, p2.x)) {
1510
- if (p1.y !== p2.y) {
1511
- xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
1512
- if (p1.x === p2.x || p.x <= xinters)
1513
- counter++;
1514
- }
1515
- }
1516
- }
1517
- }
1518
- p1 = p2;
1519
- }
1520
- if (counter % 2 === 0)
1521
- result = false;
1522
- }
1523
- return result;
1524
- }
1525
-
1526
- //
1527
- // Inherits the documentation of `intersects` in Shape
1528
- // TODO: Implement a check algorithm based on the real shape
1529
- intersects(r) {
1530
- return this.enclosing.intersects(r);
1531
- }
1532
-
1533
- //
1534
- // Inherits the documentation of `preparePath` in Shape
1535
- preparePath(ctx) {
1536
- // TODO: Implement filling paths
1537
- ctx.beginPath();
1538
- this.strokes.forEach(str => str.stroke(ctx));
1539
- return ctx;
1540
- }
1541
-
1542
- /**
1543
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
1544
- * parent references, constants and also attributes retaining the default value.
1545
- * The resulting object is commonly usued to serialize elements in JSON format.
1546
- * @returns {object} - The resulting object, with minimal attrributes
1547
- */
1548
- getAttributes() {
1549
- return {
1550
- type: this.type,
1551
- strokes: this.strokes.map(s => s.getAttributes()).join('|'),
1552
- };
1553
- }
1554
-
1555
- /**
1556
- * Reads the properties of this Path from a data object
1557
- * @param {object} data - The data object to be parsed
1558
- * @returns {module:AWT.Path}
1559
- */
1560
- setAttributes(data) {
1561
- const strData = data.strokes.split('|');
1562
- const strokes = strData.map(s => {
1563
- const [type, points] = s.split(':');
1564
- return new PathStroke(type, points ? points.split(',') : []);
1565
- });
1566
- return this.setStrokes(strokes);
1567
- }
1568
- }
1569
-
1570
- Object.assign(Path.prototype, {
1571
- /**
1572
- * Shape type id
1573
- * @name module:AWT.Path#type
1574
- * @type {string} */
1575
- type: 'path',
1576
- /**
1577
- * The strokes forming this Path.
1578
- * @name module:AWT.Path#strokes
1579
- * @type {module:AWT.PathStroke[]} */
1580
- strokes: [],
1581
- /**
1582
- * The {@link module:AWT.Rectangle Rectangle} enclosing this Path (when drawing, this Rectangle don't include border width!)
1583
- * @name module:AWT.Path#enclosing
1584
- * @type {module:AWT.Rectangle} */
1585
- enclosing: new Rectangle(),
1586
- /**
1587
- * Set of vertexs of a polygon close to the real path of this shape
1588
- * @name module:AWT.Path#enclosingPoints
1589
- * @type {module:AWT.Point[]} */
1590
- enclosingPoints: [],
1591
- });
1592
-
1593
- /**
1594
- * PathStroke is the basic component of {@link module:AWT.Path} objects
1595
- */
1596
- export class PathStroke {
1597
- /**
1598
- * PathStroke constructor
1599
- * @param {string} type - The type of stroke. Possible values are: `M` (move to), `L` (line to),
1600
- * `Q` (quadratic to), `B` (bezier to) and `X` (close path).
1601
- * @param {module:AWT.Point[]} points - The array of {@link module:AWT.Point} objects used in this Stroke.
1602
- */
1603
- constructor(type, points) {
1604
- this.type = type;
1605
- // Points are deep cloned, to avoid change the original values
1606
- if (points && points.length > 0) {
1607
- // Check if 'points' is an array of objects of type 'Point'
1608
- if (points[0] instanceof Point)
1609
- this.points = points.map(p => new Point(p));
1610
- // otherwise assume that 'points' contains just numbers
1611
- // to be readed in pairs of x and y co-ordinates
1612
- else {
1613
- this.points = [];
1614
- for (let i = 0; i < points.length; i += 2)
1615
- this.points.push(new Point(points[i], points[i + 1]));
1616
- }
1617
- }
1618
- }
1619
-
1620
- /**
1621
- * Calculates some of the points included in a quadratic Bézier curve
1622
- * The number of points being calculated is defined in Utils.settings.BEZIER_POINTS
1623
- * @see {@link https://en.wikipedia.org/wiki/B%C3%A9zier_curve}
1624
- * @see {@link https://www.jasondavies.com/animated-bezier/}
1625
- *
1626
- * @param {module:AWT.Point} p0 - Starting point of the quadratic Bézier curve
1627
- * @param {module:AWT.Point} p1 - Control point
1628
- * @param {module:AWT.Point} p2 - Ending point
1629
- * @param {number} [numPoints] - The number of intermediate points to calculate. When not defined,
1630
- * the value will be obtained from {@link module:Utils.settings.BEZIER_POINTS}.
1631
- * @returns {module:AWT.Point[]} - Array with some intermediate points from the resulting Bézier curve
1632
- */
1633
- static getQuadraticPoints(p0, p1, p2, numPoints) {
1634
- if (!numPoints)
1635
- numPoints = settings.BEZIER_POINTS;
1636
- const
1637
- result = [],
1638
- pxa = new Point(),
1639
- pxb = new Point();
1640
- for (let i = 0; i < numPoints; i++) {
1641
- const n = (i + 1) / (numPoints + 1);
1642
- pxa.x = p0.x + (p1.x - p0.x) * n;
1643
- pxa.y = p0.y - (p0.y - p1.y) * n;
1644
- pxb.x = p1.x + (p2.x - p1.x) * n;
1645
- pxb.y = p1.y + (p2.y - p1.y) * n;
1646
- result.push(new Point(pxa.x + (pxb.x - pxa.x) * n, pxa.y - (pxa.y - pxb.y) * n));
1647
- }
1648
- return result;
1649
- }
1650
-
1651
- /**
1652
- * Calculates some of the points included in a cubic Bézier (curve with two control points)
1653
- * The number of points being calculated is defined in Utils.settings.BEZIER_POINTS
1654
- * @param {module:AWT.Point} p0 - Starting point of the cubic Bézier curve
1655
- * @param {module:AWT.Point} p1 - First control point
1656
- * @param {module:AWT.Point} p2 - Second control point
1657
- * @param {module:AWT.Point} p3 - Ending point
1658
- * @param {number} [numPoints] - The number of intermediate points to calculate. When not defined,
1659
- * the value will be obtained from {@link module:Utils.settings.BEZIER_POINTS}.
1660
- * @returns {module:AWT.Point[]} - Array with some intermediate points from the resulting Bézier curve
1661
- */
1662
- static getCubicPoints(p0, p1, p2, p3, numPoints) {
1663
- const result = [];
1664
- if (!numPoints)
1665
- numPoints = settings.BEZIER_POINTS;
1666
- const pr = PathStroke.getQuadraticPoints(p0, p1, p2, numPoints);
1667
- const pq = PathStroke.getQuadraticPoints(p1, p2, p3, numPoints);
1668
- for (let i = 0; i < numPoints; i++) {
1669
- const n = (i + 1) / (numPoints + 1);
1670
- result.push(new Point(pr[i].x + (pq[i].x - pr[i].x) * n, pr[i].y - (pr[0].y - pq[0].y) * n));
1671
- }
1672
- return result;
1673
- }
1674
-
1675
- /**
1676
- * Clones this PathStroke
1677
- * @returns {module:AWT.PathStroke}
1678
- */
1679
- clone() {
1680
- // The constructors of PathStroke always make a deep copy of the `points` array
1681
- return new PathStroke(this.type, this.points);
1682
- }
1683
-
1684
- /**
1685
- * Increments or decrements by `delta` the x and y coordinates of all points
1686
- * @param {Point|Dimension} delta - The amount to add to the `x` and `y`
1687
- * coordinates of each point.
1688
- */
1689
- moveBy(delta) {
1690
- if (this.points)
1691
- this.points.forEach(pt => pt.moveBy(delta));
1692
- return this;
1693
- }
1694
-
1695
- /**
1696
- * Multiplies each point coordinates by the `x` and `y` (or `w` and `h`) values of the
1697
- * passed {@link module:AWT.Point} or {@link module:AWT.Dimension Dimension}.
1698
- * @param {Point|Dimension} delta
1699
- */
1700
- multBy(delta) {
1701
- if (this.points)
1702
- this.points.forEach(pt => pt.multBy(delta));
1703
- return this;
1704
- }
1705
-
1706
- /**
1707
- * Draws this PathStroke in the provided HTML canvas context
1708
- * @param {external:CanvasRenderingContext2D} ctx - The HTML canvas 2D rendering context
1709
- */
1710
- stroke(ctx) {
1711
- switch (this.type) {
1712
- case 'M':
1713
- ctx.moveTo(this.points[0].x, this.points[0].y);
1714
- break;
1715
- case 'L':
1716
- ctx.lineTo(this.points[0].x, this.points[0].y);
1717
- break;
1718
- case 'Q':
1719
- ctx.quadraticCurveTo(
1720
- this.points[0].x, this.points[0].y,
1721
- this.points[1].x, this.points[1].y);
1722
- break;
1723
- case 'B':
1724
- ctx.bezierCurveTo(
1725
- this.points[0].x, this.points[0].y,
1726
- this.points[1].x, this.points[1].y,
1727
- this.points[2].x, this.points[2].y);
1728
- break;
1729
- case 'X':
1730
- ctx.closePath();
1731
- break;
1732
- }
1733
- return ctx;
1734
- }
1735
-
1736
- /**
1737
- * Gets the set of points that will be included as a vertexs on the owner's shape
1738
- * enclosing polygon.
1739
- * @param {module:AWT.Point} from - The starting point for this stroke
1740
- * @returns {module:AWT.Point[]}
1741
- */
1742
- getEnclosingPoints(from) {
1743
- let result = [];
1744
- switch (this.type) {
1745
- case 'M':
1746
- case 'L':
1747
- result.push(this.points[0]);
1748
- break;
1749
- case 'Q':
1750
- result = PathStroke.getQuadraticPoints(from, this.points[0], this.points[1]);
1751
- result.push(this.points[1]);
1752
- break;
1753
- case 'B':
1754
- result = PathStroke.getCubicPoints(from, this.points[0], this.points[1], this.points[2]);
1755
- result.push(this.points[2]);
1756
- break;
1757
- }
1758
- return result;
1759
- }
1760
-
1761
- /**
1762
- * Gets a object with the basic attributes needed to rebuild this instance excluding functions,
1763
- * parent references, constants and also attributes retaining the default value.
1764
- * The resulting object is commonly usued to serialize elements in JSON format.
1765
- * @returns {object} - The resulting object, with minimal attrributes
1766
- */
1767
- getAttributes() {
1768
- return `${this.type}:${this.points ? this.points.map(p => `${fx(p.x)},${fx(p.y)}`).join(',') : ''}`;
1769
- }
1770
- }
1771
-
1772
- Object.assign(PathStroke.prototype, {
1773
- /**
1774
- * The Stroke type. Possible values are: `M` (move to), `L` (line to), `Q` (quadratic to),
1775
- * `B` (bezier to) and `X` (close path).
1776
- * @name module:AWT.PathStroke#type
1777
- * @type {string} */
1778
- type: 'X',
1779
- /**
1780
- * The array of points used by this stroke. Can be `null`.
1781
- * @name module:AWT.PathStroke#points
1782
- * @type {module:AWT.Point[]} */
1783
- points: null,
1784
- });
1785
-
1786
- /**
1787
- * This class encapsulates actions that can be linked to buttons, menus and other active objects
1788
- */
1789
- export class Action {
1790
- /**
1791
- * Action constructor
1792
- * @param {string} name - The name of this Action
1793
- * @param {function} actionPerformed - The callback to be triggered by this Action
1794
- */
1795
- constructor(name, actionPerformed) {
1796
- this.name = name;
1797
- this.actionPerformed = actionPerformed;
1798
- this._statusListeners = [];
1799
- }
1800
-
1801
- /**
1802
- * Here is where subclasses must define the callback to be triggered when
1803
- * this Action object is called
1804
- * @param {module:AWT.Action} _thisAction - Pointer to this Action object
1805
- * @param {object} _event - The original action event that has originated this action
1806
- */
1807
- actionPerformed(_thisAction, _event) {
1808
- return this;
1809
- }
1810
-
1811
- /**
1812
- * This is the method to be passed to DOM event triggers
1813
- * @example
1814
- * const myFunc = () => { alert('Hello!') }
1815
- * const myAction = new Action('hello', myFunc)
1816
- * $( "#foo" ).on( "click", myAction.processEvent)
1817
- * @param {object} event - The event object passed by the DOM event trigger
1818
- */
1819
- processEvent(event) {
1820
- return this.actionPerformed(this, event);
1821
- }
1822
-
1823
- /**
1824
- * Adds a status listener
1825
- * @param {function} listener - The callback method to be called when the status of this
1826
- * Action changes
1827
- */
1828
- addStatusListener(listener) {
1829
- this._statusListeners.push(listener);
1830
- }
1831
-
1832
- /**
1833
- * Removes a previously registered status listener
1834
- * @param {function} listener - The listener to be removed
1835
- */
1836
- removeStatusListener(listener) {
1837
- this._statusListeners = this._statusListeners.map(l => l !== listener);
1838
- }
1839
-
1840
- /**
1841
- * Enables or disables this action
1842
- * @param {boolean} enabled
1843
- */
1844
- setEnabled(enabled) {
1845
- this.enabled = enabled;
1846
- this._statusListeners.forEach(listener => listener.call(this, this));
1847
- return this;
1848
- }
1849
- }
1850
-
1851
- Object.assign(Action.prototype, {
1852
- /**
1853
- * The action's name
1854
- * @name module:AWT.Action#name
1855
- * @type {string} */
1856
- name: null,
1857
- /**
1858
- * An optional description
1859
- * @name module:AWT.Action#description
1860
- * @type {string} */
1861
- description: null,
1862
- /**
1863
- * Action status. `true` means enabled, `false` disabled
1864
- * @name module:AWT.Action#enabled
1865
- * @type {boolean} */
1866
- enabled: false,
1867
- /**
1868
- * Array of callback functions to be triggered when the `enabled` flag changes
1869
- * @name module:AWT.Action#_statusListeners
1870
- * @private
1871
- * @type {function[]} */
1872
- _statusListeners: null,
1873
- });
1874
-
1875
- /**
1876
- * This class provides a timer that will launch a function at specific intervals
1877
- */
1878
- export class Timer {
1879
- /**
1880
- * Timer constructor
1881
- * @param {function} actionPerformed - The function to be triggered when the timer is enabled.
1882
- * @param {number} interval - The interval between action calls, specified in milliseconds.
1883
- * @param {boolean} [enabled=false] - Flag to indicate if the timer will be initially enabled.
1884
- */
1885
- constructor(actionPerformed, interval, enabled) {
1886
- this.actionPerformed = actionPerformed;
1887
- this.interval = interval;
1888
- this.setEnabled(enabled === true);
1889
- }
1890
-
1891
- /**
1892
- * Here is where subclasses must define the function to be performed when this timer ticks.
1893
- * @param {module:AWT.Timer} _thisTimer
1894
- */
1895
- actionPerformed(_thisTimer) {
1896
- return this;
1897
- }
1898
-
1899
- /**
1900
- * This is the method called by `window.setInterval`
1901
- * @param {external:Event} _event
1902
- */
1903
- processTimer(_event) {
1904
- this.ticks++;
1905
- if (!this.repeats)
1906
- this.stop();
1907
- return this.actionPerformed.call(this);
1908
- }
1909
-
1910
- /**
1911
- * Enables or disables this timer
1912
- * @param {boolean} enabled - Indicates if the timer should be enabled or disabled
1913
- * @param {boolean} [retainCounter=false] - When `true`, the ticks counter will not be cleared
1914
- */
1915
- setEnabled(enabled, retainCounter) {
1916
- if (!retainCounter)
1917
- this.ticks = 0;
1918
- if (enabled && this.timer !== null) {
1919
- // Timer already running
1920
- return;
1921
- }
1922
-
1923
- if (enabled) {
1924
- this.timer = window.setInterval(() => this.processTimer(null), this.interval);
1925
- } else {
1926
- if (this.timer !== null) {
1927
- window.clearInterval(this.timer);
1928
- this.timer = null;
1929
- }
1930
- }
1931
- return this;
1932
- }
1933
-
1934
- /**
1935
- * Checks if this timer is running
1936
- * @returns {boolean}
1937
- */
1938
- isRunning() {
1939
- return this.timer !== null;
1940
- }
1941
-
1942
- /**
1943
- * Starts this timer
1944
- * @param {boolean} [retainCounter=false] - When `true`, the ticks counter will not be cleared
1945
- */
1946
- start(retainCounter) {
1947
- return this.setEnabled(true, retainCounter);
1948
- }
1949
-
1950
- /**
1951
- * Stops this timer
1952
- * @param {boolean} [retainCounter=false] - When `true`, the ticks counter will not be cleared
1953
- */
1954
- stop(retainCounter) {
1955
- return this.setEnabled(false, retainCounter);
1956
- }
1957
- }
1958
-
1959
- Object.assign(Timer.prototype, {
1960
- /**
1961
- * The timer interval, in milliseconds
1962
- * @name module:AWT.Timer#interval
1963
- * @type {number} */
1964
- interval: 0,
1965
- /**
1966
- * The ticks counter
1967
- * @name module:AWT.Timer#ticks
1968
- * @type {number} */
1969
- ticks: 0,
1970
- /**
1971
- * The object returned by `window.setInterval`
1972
- * @name module:AWT.Timer#timer
1973
- * @type {object} */
1974
- timer: null,
1975
- /**
1976
- * When `true`, the timer should repeat until `stop` is called
1977
- * @name module:AWT.Timer#repeats
1978
- * @type {boolean} */
1979
- repeats: true,
1980
- });
1981
-
1982
- /**
1983
- * Logic object that takes care of an "invalidated" rectangle that will be repainted
1984
- * at the next update of a 2D object, usually an HTML Canvas.
1985
- * Container has the same constructor options as {@link module:AWT.Rectangle Rectangle}
1986
- * @extends module:AWT.Rectangle
1987
- */
1988
- export class Container extends Rectangle {
1989
- /**
1990
- * Container constructor
1991
- * @param {Point|Rectangle|number|number[]} pos
1992
- * @param {Dimension|number} [dim]
1993
- * @param {number} [w]
1994
- * @param {number} [h]
1995
- */
1996
- constructor(pos, dim, w, h) {
1997
- super(pos, dim, w, h);
1998
- }
1999
-
2000
- /**
2001
- * Adds the provided rectangle to the invalidated area.
2002
- * @param {module:AWT.Rectangle} rect
2003
- */
2004
- invalidate(rect) {
2005
- if (rect) {
2006
- if (this.invalidatedRect === null)
2007
- this.invalidatedRect = rect.clone();
2008
- else
2009
- this.invalidatedRect.add(rect);
2010
- } else
2011
- this.invalidatedRect = null;
2012
- return this;
2013
- }
2014
-
2015
- /**
2016
- * Updates the invalidated area
2017
- */
2018
- update() {
2019
- this.updateContent(this.invalidatedRect);
2020
- this.invalidatedRect = null;
2021
- return this;
2022
- }
2023
-
2024
- /**
2025
- * Containers should implement this method to update its graphic contents. It should
2026
- * be called from {@link module:AWT.Container#update}
2027
- * @param {module:AWT.Shape} _dirtyRegion - Specifies the area to be updated. When `null`, it's the whole
2028
- * Container.
2029
- */
2030
- updateContent(_dirtyRegion) {
2031
- // To be overrided by subclasses. Here does nothing.
2032
- return this;
2033
- }
2034
- }
2035
-
2036
- Object.assign(Container.prototype, {
2037
- /**
2038
- * The currently "invalidated" area
2039
- * @name module:AWT.Container#invalidatedRect
2040
- * @type {module:AWT.Rectangle} */
2041
- invalidatedRect: null,
2042
- });
2043
-
2044
- /**
2045
- * This object contains utility clases for painting graphics and images,
2046
- * as found in the Java [Abstract Window Toolkit](http://docs.oracle.com/javase/7/docs/api/java/awt/package-summary.html)
2047
- *
2048
- * The objects defined here are: {@link module:AWT.Font Font}, {@link module:AWT.Gradient Gradient}, {@link module:AWT.Stroke Stroke},
2049
- * {@link module:AWT.Point Point}, {@link module:AWT.Dimension Dimension}, {@link module:AWT.Shape Shape}, {@link module:AWT.Rectangle Rectangle},
2050
- * {@link module:AWT.Ellipse Ellipse}, {@link module:AWT.Path Path}, {@link module:AWT.PathStroke PathStroke}, {@link module:AWT.Action Action},
2051
- * {@link module:AWT.Timer Timer} and {@link module:AWT.Container Container}.
2052
- */
2053
- export default {
2054
- Font,
2055
- Gradient,
2056
- Stroke,
2057
- Point,
2058
- Dimension,
2059
- Shape,
2060
- Rectangle,
2061
- Ellipse,
2062
- Path,
2063
- PathStroke,
2064
- Action,
2065
- Timer,
2066
- Container
2067
- };