wgge 0.0.1

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 (107) hide show
  1. package/core/ActivatedTreeNode.js +187 -0
  2. package/core/Collection.js +156 -0
  3. package/core/Dictionary.js +83 -0
  4. package/core/animation/3d/AnimatedVector3.js +35 -0
  5. package/core/animation/3d/AnimationHelper.js +103 -0
  6. package/core/animation/3d/ProgressVector3.js +26 -0
  7. package/core/animation/AnimatedRotation.js +44 -0
  8. package/core/animation/AnimatedValue.js +33 -0
  9. package/core/animation/AnimatedVector2.js +35 -0
  10. package/core/animation/ProgressRotation.js +40 -0
  11. package/core/animation/ProgressValue.js +27 -0
  12. package/core/animation/ProgressVector2.js +23 -0
  13. package/core/audio/AudioModel.js +36 -0
  14. package/core/audio/AudioRenderer.js +120 -0
  15. package/core/audio/global/GlobalAudioModel.js +88 -0
  16. package/core/audio/global/GlobalAudioRenderer.js +108 -0
  17. package/core/controller/AnimationController.js +37 -0
  18. package/core/controller/AnimationFloatController.js +39 -0
  19. package/core/controller/AnimationVector2Controller.js +39 -0
  20. package/core/controller/CollectionController.js +47 -0
  21. package/core/controller/ConditionalNodeController.js +51 -0
  22. package/core/controller/ControllerBase.js +64 -0
  23. package/core/controller/ControllerWithSaveGame.js +27 -0
  24. package/core/controller/ControllerWithTimeout.js +10 -0
  25. package/core/controller/NullableNodeController.js +35 -0
  26. package/core/controller/TimeoutController.js +26 -0
  27. package/core/helper/ArrayHelper.js +19 -0
  28. package/core/helper/DOMHelper.js +167 -0
  29. package/core/helper/GUIHelper.js +53 -0
  30. package/core/helper/HttpHelper.js +33 -0
  31. package/core/helper/ImageHelper.js +36 -0
  32. package/core/helper/NumberHelper.js +28 -0
  33. package/core/helper/ObjectHelper.js +10 -0
  34. package/core/helper/StringHelper.js +59 -0
  35. package/core/model/ModelBase.js +91 -0
  36. package/core/model/ObjectModel.js +97 -0
  37. package/core/model/collection/ModelNodeCollection.js +232 -0
  38. package/core/model/collection/table/IdentifiedModelNode.js +24 -0
  39. package/core/model/collection/table/ModelNodeTable.js +53 -0
  40. package/core/model/event/ChangeEvent.js +24 -0
  41. package/core/model/event/EventManager.js +44 -0
  42. package/core/model/event/NodeWithEvents.js +32 -0
  43. package/core/model/template/TemplateNode.js +45 -0
  44. package/core/model/value/BoolValue.js +17 -0
  45. package/core/model/value/CapacityValue.js +49 -0
  46. package/core/model/value/DirtyValue.js +66 -0
  47. package/core/model/value/FloatValue.js +8 -0
  48. package/core/model/value/IntValue.js +20 -0
  49. package/core/model/value/NullableNode.js +66 -0
  50. package/core/model/value/StringValue.js +21 -0
  51. package/core/model/vector/Quaternion.js +82 -0
  52. package/core/model/vector/Rotation.js +61 -0
  53. package/core/model/vector/Vector2.js +177 -0
  54. package/core/model/vector/Vector3.js +98 -0
  55. package/core/renderer/RendererBase.js +46 -0
  56. package/core/renderer/canvas/CanvasRenderer.js +60 -0
  57. package/core/renderer/canvas/ImageToCanvasRenderer.js +94 -0
  58. package/core/renderer/dom/DirtyValueRenderer.js +36 -0
  59. package/core/renderer/dom/DomRenderer.js +32 -0
  60. package/core/renderer/dom/ImageRenderer.js +41 -0
  61. package/core/renderer/dom/Vector2Renderer.js +37 -0
  62. package/core/renderer/generic/CollectionRenderer.js +50 -0
  63. package/core/renderer/generic/ConditionalNodeRenderer.js +57 -0
  64. package/core/renderer/generic/NullableNodeRenderer.js +43 -0
  65. package/core/renderer/svg/SvgRenderer.js +130 -0
  66. package/editor/EditorController.js +135 -0
  67. package/editor/EditorModel.js +38 -0
  68. package/editor/EditorRenderer.js +116 -0
  69. package/editor/form/NodeFormRenderer.js +180 -0
  70. package/editor/material/MaterialPreviewRenderer.js +136 -0
  71. package/editor/table/TableRenderer.js +123 -0
  72. package/editor/table/lookup/TableLookupRenderer.js +120 -0
  73. package/editor/table/row/TableRowRenderer.js +97 -0
  74. package/game/GameController.js +144 -0
  75. package/game/GameModel.js +73 -0
  76. package/game/GameRenderer.js +108 -0
  77. package/game/controls/ControlsController.js +104 -0
  78. package/game/controls/ControlsModel.js +60 -0
  79. package/game/menu/MenuModel.js +25 -0
  80. package/game/menu/MenuRenderer.js +37 -0
  81. package/game/menu/item/MenuItemModel.js +31 -0
  82. package/game/menu/item/MenuItemRenderer.js +40 -0
  83. package/game/resources/3d/3DModel.js +30 -0
  84. package/game/resources/ResourcesModel.js +26 -0
  85. package/game/resources/assets/AssetCache.js +193 -0
  86. package/game/resources/assets/loader/AssetLoader.js +56 -0
  87. package/game/resources/assets/loader/AudioLoader.js +15 -0
  88. package/game/resources/assets/loader/GlbLoader.js +16 -0
  89. package/game/resources/assets/loader/ImageLoader.js +15 -0
  90. package/game/resources/assets/loader/MaterialLoader.js +49 -0
  91. package/game/resources/assets/loader/Model3dLoader.js +21 -0
  92. package/game/resources/assets/loader/SpriteLoader.js +40 -0
  93. package/game/resources/image/DualImageModel.js +34 -0
  94. package/game/resources/image/ImageModel.js +43 -0
  95. package/game/resources/material/MaterialModel.js +69 -0
  96. package/game/save/SaveGameController.js +21 -0
  97. package/game/save/SaveGameModel.js +5 -0
  98. package/game/save/SaveGameRenderer.js +17 -0
  99. package/game/save/editor/SaveGameEditorRenderer.js +17 -0
  100. package/index.html +42 -0
  101. package/index.js +73 -0
  102. package/package.json +35 -0
  103. package/style/basic/editor.less +290 -0
  104. package/style/basic/game.less +149 -0
  105. package/style/basic/utilities.less +215 -0
  106. package/style/style.less +3 -0
  107. package/webpack.config.js +29 -0
@@ -0,0 +1,20 @@
1
+ import DirtyValue from "./DirtyValue";
2
+
3
+ export default class IntValue extends DirtyValue {
4
+
5
+ set(value) {
6
+ if (value === null || value === undefined || value === '') {
7
+ super.set(Number.NaN);
8
+ } else {
9
+ super.set(Number(value));
10
+ }
11
+ }
12
+
13
+ isEmpty() {
14
+ return super.isEmpty() || Number.isNaN(this.value);
15
+ }
16
+
17
+ increase(num) {
18
+ this.set(this.get() + num);
19
+ }
20
+ }
@@ -0,0 +1,66 @@
1
+ import DirtyValue from "./DirtyValue";
2
+
3
+ export default class NullableNode extends DirtyValue {
4
+ nodeFactory;
5
+
6
+ /**
7
+ * @type {ObjectModel}
8
+ */
9
+ value;
10
+
11
+ constructor(nodeFactory = null, persistent = true) {
12
+ super(null, persistent);
13
+
14
+ this.nodeFactory = nodeFactory;
15
+ }
16
+
17
+ /**
18
+ *
19
+ * @param {ObjectModel} value
20
+ */
21
+ set(value) {
22
+ if (this.isSet()) {
23
+ this.unsubscribeFromOnDirtyEvent(this.value);
24
+ }
25
+
26
+ super.set(value);
27
+
28
+ if (this.isSet()) {
29
+ this.subscribeToOnDirtyEvent(this.value);
30
+ }
31
+ }
32
+
33
+ getStateInternal() {
34
+ if (this.isSet()) {
35
+ return this.value.getState();
36
+ }
37
+ return null;
38
+ }
39
+
40
+ restoreStateInternal(state) {
41
+ this.set(null);
42
+ if (state && this.isPersistent) {
43
+ if (!this.nodeFactory) {
44
+ console.log('No nodeFactory to restore NullableNode');
45
+ return;
46
+ }
47
+ const node = this.nodeFactory();
48
+ node.restoreState(state);
49
+ this.set(node);
50
+ }
51
+ }
52
+
53
+ clean() {
54
+ if (super.clean()) {
55
+ if (this.value && typeof this.value.clean === 'function') this.value.clean();
56
+ return true;
57
+ }
58
+ }
59
+
60
+ getResourcesForPreload() {
61
+ if (this.isSet()) {
62
+ return this.value.getResourcesForPreload();
63
+ }
64
+ return [];
65
+ }
66
+ }
@@ -0,0 +1,21 @@
1
+ import DirtyValue from "./DirtyValue";
2
+
3
+ export default class StringValue extends DirtyValue {
4
+
5
+ set(value) {
6
+ if (typeof value === 'string') {
7
+ super.set(value);
8
+ } else {
9
+ if (value && typeof value === 'object') {
10
+ super.set(value.toString());
11
+ } else {
12
+ super.set('');
13
+ }
14
+ }
15
+ }
16
+
17
+ isEmpty() {
18
+ return super.isEmpty() || this.value === '';
19
+ }
20
+
21
+ }
@@ -0,0 +1,82 @@
1
+ import ObjectModel from "../ObjectModel";
2
+
3
+ export default class Quaternion extends ObjectModel {
4
+ x;
5
+ y;
6
+ z;
7
+ w;
8
+
9
+ constructor(x, y, z, w) {
10
+ super();
11
+
12
+ this.x = 0;
13
+ this.y = 0;
14
+ this.z = 0;
15
+ this.w = 0;
16
+
17
+ if (y === undefined && typeof x === 'object') {
18
+ if (x.length === 4) {
19
+ this.setFromArray(x);
20
+ } else {
21
+ this.set(x);
22
+ }
23
+ } else if (x !== undefined && w !== undefined) {
24
+ this.set(x, y, z, w);
25
+ }
26
+ }
27
+
28
+ equalsTo(v) {
29
+ return (v) ? this.x === v.x && this.y === v.y && this.z === v.z && this.w === v.w : false;
30
+ }
31
+
32
+ set(x, y, z, w) {
33
+ if (y === undefined && typeof x === 'object') {
34
+ this.set(x.x, x.y, x.z, x.w);
35
+ return;
36
+ }
37
+
38
+ x = Number(x);
39
+ y = Number(y);
40
+ z = Number(z);
41
+ w = Number(w);
42
+
43
+ if ((this.x !== x || this.y !== y || this.z !== z || this.w !== w)) {
44
+ const old = this.clone();
45
+ this.x = x;
46
+ this.y = y;
47
+ this.z = z;
48
+ this.w = w;
49
+ this.makeDirty();
50
+ this.triggerEvent('change', {oldValue: old, newValue: this});
51
+ }
52
+ }
53
+
54
+ toArray() {
55
+ return [this.x, this.y, this.z, this.w];
56
+ }
57
+
58
+ setFromArray(arr) {
59
+ if (typeof arr === 'object' && arr.length === 4) {
60
+ this.set(arr[0], arr[1], arr[2], arr[3]);
61
+ }
62
+ }
63
+
64
+ static fromArray(arr) {
65
+ const v = new Quaternion();
66
+ v.setFromArray(arr);
67
+ return v;
68
+ }
69
+
70
+ clone() {
71
+ return new Quaternion(this);
72
+ }
73
+
74
+ getStateInternal() {
75
+ return this.toArray();
76
+ }
77
+
78
+ restoreStateInternal(state) {
79
+ this.setFromArray(state);
80
+ }
81
+
82
+ }
@@ -0,0 +1,61 @@
1
+ import DirtyValue from "../value/DirtyValue";
2
+
3
+ const ROTATION_RANGE = 2 * Math.PI;
4
+
5
+ /**
6
+ * Keeps track of rotation in radians that is always in interval (-pi, +pi> which equals to (-180, 180> degrees
7
+ */
8
+ export default class Rotation extends DirtyValue {
9
+
10
+ static normalizeValue(rads) {
11
+ let result = rads % ROTATION_RANGE;
12
+ if (result > (ROTATION_RANGE / 2)) {
13
+ result = result - ROTATION_RANGE;
14
+ }
15
+ if (result <= (-ROTATION_RANGE / 2)) {
16
+ result = result + ROTATION_RANGE;
17
+ }
18
+ return result;
19
+ }
20
+
21
+ static diff(a, b) {
22
+ return Rotation.normalizeValue(Rotation.normalizeValue(a) - Rotation.normalizeValue(b));
23
+ }
24
+
25
+ static radToDeg(rads) {
26
+ return rads * 180 / Math.PI;
27
+ }
28
+
29
+ static degToRad(degs) {
30
+ return degs * Math.PI / 180;
31
+ }
32
+
33
+ set(value) {
34
+ super.set(Rotation.normalizeValue(value));
35
+ }
36
+
37
+ add(value) {
38
+ return new Rotation(this.get() + value);
39
+ }
40
+
41
+ subtract(value) {
42
+ return new Rotation(Rotation.diff(this.get(), value));
43
+ }
44
+
45
+ equalsTo(value) {
46
+ return (this.value === Rotation.normalizeValue(value));
47
+ }
48
+
49
+ clone() {
50
+ return new Rotation(this.value);
51
+ }
52
+
53
+ setDegrees(value) {
54
+ this.set(Rotation.degToRad(value));
55
+ }
56
+
57
+ getDegrees() {
58
+ return Rotation.radToDeg(this.get());
59
+ }
60
+
61
+ }
@@ -0,0 +1,177 @@
1
+ import ObjectModel from "../ObjectModel";
2
+ import Rotation from "./Rotation";
3
+
4
+ export default class Vector2 extends ObjectModel {
5
+ x;
6
+ y;
7
+
8
+ constructor(x, y, persistent) {
9
+ super(persistent);
10
+
11
+ this.x = 0;
12
+ this.y = 0;
13
+
14
+ if (y === undefined && typeof x === 'object') {
15
+ if (x.length === 2) {
16
+ this.setFromArray(x);
17
+ } else {
18
+ this.set(x);
19
+ }
20
+ } else if (x !== undefined) {
21
+ this.set(x, y);
22
+ }
23
+ }
24
+
25
+ distanceTo(v) {
26
+ return Math.sqrt(Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2));
27
+ }
28
+
29
+ equalsTo(v) {
30
+ return (v) ? this.x === v.x && this.y === v.y : false;
31
+ }
32
+
33
+ /**
34
+ *
35
+ * @param x Number|Vector2
36
+ * @param y Number|undefined
37
+ */
38
+ set(x, y = undefined) {
39
+ if (y === undefined && typeof x === 'object') {
40
+ this.set(x.x, x.y);
41
+ return;
42
+ }
43
+
44
+ x = Number(x);
45
+ y = Number(y);
46
+
47
+ if (this.x !== x || this.y !== y) {
48
+ const old = this.clone();
49
+ this.x = x;
50
+ this.y = y;
51
+ this.makeDirty();
52
+ this.triggerEvent('change', {oldValue: old, newValue: this});
53
+ }
54
+ }
55
+
56
+ size() {
57
+ return this.distanceTo(new Vector2(0, 0));
58
+ }
59
+
60
+ setSize(size) {
61
+ const currentSize = this.size();
62
+ if (currentSize !== 0) {
63
+ const ratio = size / currentSize;
64
+ this.set(this.x * ratio, this.y * ratio);
65
+ }
66
+ return this;
67
+ }
68
+
69
+ round() {
70
+ return new Vector2(Math.round(this.x), Math.round(this.y));
71
+ }
72
+
73
+ add(v) {
74
+ return new Vector2(this.x + v.x, this.y + v.y);
75
+ }
76
+
77
+ multiply(s) {
78
+ return new Vector2(this.x * s, this.y * s);
79
+ }
80
+
81
+ subtract(v) {
82
+ return new Vector2(this.x - v.x, this.y - v.y);
83
+ }
84
+
85
+ sub(v) {
86
+ return this.subtract(v);
87
+ }
88
+
89
+ toArray() {
90
+ return [this.x, this.y];
91
+ }
92
+
93
+ setFromArray(arr) {
94
+ if (typeof arr === 'object' && arr.length === 2) {
95
+ this.set(arr[0], arr[1]);
96
+ }
97
+ }
98
+
99
+ clone() {
100
+ return new Vector2(this.x, this.y);
101
+ }
102
+
103
+ getStateInternal() {
104
+ return this.toArray();
105
+ }
106
+
107
+ restoreStateInternal(state) {
108
+ this.setFromArray(state);
109
+ }
110
+
111
+ /***
112
+ * Return angle between AB and Y axis in radians
113
+ * @param {Vector2} b
114
+ * @returns {number}
115
+ */
116
+ getAngleToYAxis(b) {
117
+ const diff = b.subtract(this);
118
+ const down = diff.y < 0;
119
+ const sinX = diff.x / diff.size();
120
+ const angle = Math.asin(sinX);
121
+ const result = down ?
122
+ Math.PI - angle :
123
+ angle;
124
+ return result || 0;
125
+ }
126
+
127
+ getNeighborPositions(size = 1, includeCenter = false) {
128
+ const neighbors = [];
129
+ const maxX = this.x + size;
130
+ for (let x = this.x - size; x <= maxX; x++) {
131
+ const maxY = this.y + size;
132
+ for (let y = this.y - size; y <= maxY; y++) {
133
+ const n = new Vector2(x, y);
134
+ if (includeCenter || !this.equalsTo(n)) {
135
+ neighbors.push(n);
136
+ }
137
+ }
138
+ }
139
+ return neighbors;
140
+ }
141
+
142
+ getClosest(positions) {
143
+ if ((!positions) || positions.length === 0) return null;
144
+ if (positions.length === 1) return positions[0];
145
+ let closest = positions[0];
146
+ let distance = this.distanceTo(closest);
147
+ for (let i = 1, max = positions.length; i < max; i++) {
148
+ const d = this.distanceTo(positions[i]);
149
+ if (d < distance) {
150
+ closest = positions[i];
151
+ distance = d;
152
+ }
153
+ }
154
+ return closest;
155
+ }
156
+
157
+ /**
158
+ * If you are standing at this vector and looking at target vector, this will be rotation that you have to Y axis.
159
+ * @param {Vector2} target
160
+ * @returns {Rotation}
161
+ */
162
+ getRotationFromYAxis(target) {
163
+ return new Rotation(this.getAngleToYAxis(target));
164
+ }
165
+
166
+ addOnChangeListener(eventHandler) {
167
+ this.addEventListener('change', eventHandler);
168
+ }
169
+
170
+ removeOnChangeListener(eventHandler) {
171
+ this.removeEventListener('change', eventHandler);
172
+ }
173
+
174
+ toString() {
175
+ return `[${this.x},${this.y}]`;
176
+ }
177
+ }
@@ -0,0 +1,98 @@
1
+ import ObjectModel from "../ObjectModel";
2
+ import {Vector3 as ThreeVector3} from "three";
3
+
4
+ export default class Vector3 extends ObjectModel {
5
+ x;
6
+ y;
7
+ z;
8
+
9
+ constructor(x, y, z) {
10
+ super();
11
+
12
+ this.x = 0;
13
+ this.y = 0;
14
+ this.z = 0;
15
+
16
+ if (y === undefined && typeof x === 'object') {
17
+ if (x.length === 3) {
18
+ this.setFromArray(x);
19
+ } else {
20
+ this.set(x);
21
+ }
22
+ } else if (x !== undefined && z !== undefined) {
23
+ this.set(x, y, z);
24
+ }
25
+ }
26
+
27
+ equalsTo(v) {
28
+ return (v) ? this.x === v.x && this.y === v.y && this.z === v.z : false;
29
+ }
30
+
31
+ set(x, y, z) {
32
+ if (y === undefined && typeof x === 'object') {
33
+ this.set(x.x, x.y, x.z);
34
+ return;
35
+ }
36
+
37
+ x = Number(x);
38
+ y = Number(y);
39
+ z = Number(z);
40
+
41
+ if ((this.x !== x || this.y !== y || this.z !== z)) {
42
+ const old = this.clone();
43
+ this.x = x;
44
+ this.y = y;
45
+ this.z = z;
46
+ this.makeDirty();
47
+ this.triggerEvent('change', {oldValue: old, newValue: this});
48
+ }
49
+ }
50
+
51
+ add(v) {
52
+ return new Vector3(this.x + v.x, this.y + v.y, this.z + v.z);
53
+ }
54
+
55
+ multiply(s) {
56
+ return new Vector3(this.x * s, this.y * s, this.z * s);
57
+ }
58
+
59
+ subtract(v) {
60
+ return new Vector3(this.x - v.x, this.y - v.y, this.z - v.z);
61
+ }
62
+
63
+ toArray() {
64
+ return [this.x, this.y, this.z];
65
+ }
66
+
67
+ setFromArray(arr) {
68
+ if (typeof arr === 'object' && arr.length === 3) {
69
+ this.set(arr[0], arr[1], arr[2]);
70
+ }
71
+ }
72
+
73
+ static fromArray(arr) {
74
+ const v = new Vector3();
75
+ v.setFromArray(arr);
76
+ return v;
77
+ }
78
+
79
+ clone() {
80
+ return new Vector3(this);
81
+ }
82
+
83
+ getStateInternal() {
84
+ return this.toArray();
85
+ }
86
+
87
+ restoreStateInternal(state) {
88
+ this.setFromArray(state);
89
+ }
90
+
91
+ getThree() {
92
+ return new ThreeVector3(this.x, this.y, this.z);
93
+ }
94
+
95
+ asRgbColor() {
96
+ return `rgb(${Math.round(this.x)}, ${Math.round(this.y)}, ${Math.round(this.z)})`;
97
+ }
98
+ }
@@ -0,0 +1,46 @@
1
+ import ActivatedTreeNode from "../ActivatedTreeNode";
2
+
3
+ export default class RendererBase extends ActivatedTreeNode {
4
+
5
+ /**
6
+ * @type GameModel
7
+ */
8
+ game;
9
+
10
+ /**
11
+ * @type ModelBase
12
+ */
13
+ model;
14
+
15
+ /**
16
+ * @param {GameModel} game
17
+ * @param {ModelBase} model
18
+ */
19
+ constructor(game, model) {
20
+ super();
21
+ this.game = game;
22
+ this.model = model;
23
+
24
+ }
25
+
26
+ render() {
27
+ if (!this.model.isDirty) {
28
+ return;
29
+ }
30
+ if (!this.isActivated) {
31
+ return;
32
+ }
33
+ this.renderInternal();
34
+ this.children.forEach((c) => c.render());
35
+ if (this.isRoot()) {
36
+ this.model.clean();
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Override this
42
+ */
43
+ renderInternal() {
44
+ }
45
+
46
+ }
@@ -0,0 +1,60 @@
1
+ import RendererBase from "../RendererBase";
2
+
3
+ export default class CanvasRenderer extends RendererBase {
4
+
5
+ /**
6
+ * @type HTMLCanvasElement
7
+ */
8
+ canvas;
9
+
10
+ /**
11
+ * @type CanvasRenderingContext2D
12
+ */
13
+ context2d;
14
+
15
+ constructor(game, model, canvas) {
16
+ super(game, model);
17
+
18
+ this.canvas = canvas;
19
+
20
+ }
21
+
22
+ activateInternal() {
23
+ this.context2d = this.canvas.getContext('2d');
24
+ }
25
+
26
+ deactivateInternal() {
27
+ this.context2d = null;
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @param {Vector2} start
33
+ * @param {Vector2} size
34
+ * @param {CanvasGradient|string} fill
35
+ */
36
+ drawRect(start, size, fill = 'black') {
37
+ const end = start.add(size);
38
+ this.context2d.fillStyle = fill;
39
+ this.context2d.fillRect(start.x, start.y, end.x, end.y);
40
+ }
41
+
42
+ /**
43
+ *
44
+ * @param {Vector2} gradientStart
45
+ * @param {Vector2} gradientSize
46
+ * @param {Vector2} start
47
+ * @param {Vector2} size
48
+ * @param {[[Number, string|color]]} stops
49
+ */
50
+ drawGradient(start, size, gradientStart, gradientSize, stops) {
51
+ const gradientEnd = gradientStart.add(gradientSize);
52
+ const grd = this.context2d.createLinearGradient(gradientStart.x, gradientStart.y, gradientEnd.x, gradientEnd.y);
53
+ stops.forEach((s) => {
54
+ grd.addColorStop(s[0], s[1]);
55
+ });
56
+ this.drawRect(start, size, grd);
57
+ }
58
+
59
+
60
+ }