melonjs 10.8.0 → 10.9.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.
@@ -52,6 +52,103 @@ class RoundRect extends Rect {
52
52
  this._radius = value;
53
53
  }
54
54
 
55
+ /**
56
+ * copy the position, size and radius of the given rounded rectangle into this one
57
+ * @name copy
58
+ * @memberof RoundRect.prototype
59
+ * @function
60
+ * @param {RoundRect} rrect source rounded rectangle
61
+ * @returns {RoundRect} new rectangle
62
+ */
63
+ copy(rrect) {
64
+ super.setShape(rrect.pos.x, rrect.pos.y, rrect.width, rrect.height);
65
+ this.radius = rrect.radius;
66
+ return this;
67
+ }
68
+
69
+ /**
70
+ * Returns true if the rounded rectangle contains the given point
71
+ * @name contains
72
+ * @memberof RoundRect.prototype
73
+ * @function
74
+ * @param {number} x x coordinate
75
+ * @param {number} y y coordinate
76
+ * @returns {boolean} true if contains
77
+ */
78
+
79
+ /**
80
+ * Returns true if the rounded rectangle contains the given point
81
+ * @name contains
82
+ * @memberof RoundRect.prototype
83
+ * @function
84
+ * @param {Vector2d} point
85
+ * @returns {boolean} true if contains
86
+ */
87
+ contains() {
88
+ var arg0 = arguments[0];
89
+ var _x, _y;
90
+ if (arguments.length === 2) {
91
+ // x, y
92
+ _x = arg0;
93
+ _y = arguments[1];
94
+ } else {
95
+ if (arg0 instanceof Rect) {
96
+ // good enough
97
+ return super.contains(arg0);
98
+ } else {
99
+ // vector
100
+ _x = arg0.x;
101
+ _y = arg0.y;
102
+ }
103
+ }
104
+
105
+ // check whether point is outside the bounding box
106
+ if (_x < this.left || _x >= this.right || _y < this.top || _y >= this.bottom) {
107
+ return false; // outside bounding box
108
+ }
109
+
110
+ // check whether point is within the bounding box minus radius
111
+ if ((_x >= this.left + this.radius && _x <= this.right - this.radius) || (_y >= this.top + this.radius && _y <= this.bottom - this.radius)) {
112
+ return true;
113
+ }
114
+
115
+ // check whether point is in one of the rounded corner areas
116
+ var tx, ty;
117
+ var radiusX = Math.max(0, Math.min(this.radius, this.width / 2));
118
+ var radiusY = Math.max(0, Math.min(this.radius, this.height / 2));
119
+
120
+ if (_x < this.left + radiusX && _y < this.top + radiusY) {
121
+ tx = _x - this.left - radiusX;
122
+ ty = _y - this.top - radiusY;
123
+ } else if (_x > this.right - radiusX && _y < this.top + radiusY) {
124
+ tx = _x - this.right + radiusX;
125
+ ty = _y - this.top - radiusY;
126
+ } else if (_x > this.right - radiusX && _y > this.bottom - radiusY) {
127
+ tx = _x - this.right + radiusX;
128
+ ty = _y - this.bottom + radiusY;
129
+ } else if (_x < this.left + radiusX && _y > this.bottom - radiusY) {
130
+ tx = _x - this.left - radiusX;
131
+ ty = _y - this.bottom + radiusY;
132
+ } else {
133
+ return false; // inside and not within the rounded corner area
134
+ }
135
+
136
+ // Pythagorean theorem.
137
+ return ((tx * tx) + (ty * ty) <= (radiusX * radiusY));
138
+ }
139
+
140
+ /**
141
+ * check if this RoundRect is identical to the specified one
142
+ * @name equals
143
+ * @memberof RoundRect.prototype
144
+ * @function
145
+ * @param {RoundRect} rrect
146
+ * @returns {boolean} true if equals
147
+ */
148
+ equals(rrect) {
149
+ return super.equals(rrect) && this.radius === rrect.radius;
150
+ }
151
+
55
152
  /**
56
153
  * clone this RoundRect
57
154
  * @name clone
@@ -60,7 +157,7 @@ class RoundRect extends Rect {
60
157
  * @returns {RoundRect} new RoundRect
61
158
  */
62
159
  clone() {
63
- return new RoundRect(this.pos.x, this.pos.y, this.width, this.height, this.radius);
160
+ return new RoundRect(this.pos.x, this.pos.y, this.width, this.height, radius);
64
161
  }
65
162
  };
66
163
 
@@ -57,8 +57,8 @@ var leadingZeroRE = /^0+/;
57
57
  function addMapping(id, mapping) {
58
58
  var expanded_id = id.replace(vendorProductRE, function (_, a, b) {
59
59
  return (
60
- "000".substr(a.length - 1) + a + "-" +
61
- "000".substr(b.length - 1) + b + "-"
60
+ "000".slice(a.length - 1) + a + "-" +
61
+ "000".slice(b.length - 1) + b + "-"
62
62
  );
63
63
  });
64
64
  var sparse_id = id.replace(vendorProductRE, function (_, a, b) {
@@ -5,3 +5,4 @@ import "core-js/proposals/global-this";
5
5
  import "./console.js";
6
6
  import "./performance.js";
7
7
  import "./requestAnimationFrame.js";
8
+ import "./roundrect.js";
@@ -0,0 +1,235 @@
1
+ /*
2
+ * based on https://www.npmjs.com/package/canvas-roundrect-polyfill
3
+ * @version 0.0.1
4
+ */
5
+ (() => {
6
+
7
+ "use strict";
8
+
9
+ /** @ignore */
10
+ function roundRect(x, y, w, h, radii) {
11
+
12
+ if (!([x, y, w, h].every((input) => Number.isFinite(input)))) {
13
+
14
+ return;
15
+
16
+ }
17
+
18
+ radii = parseRadiiArgument(radii);
19
+
20
+ let upperLeft, upperRight, lowerRight, lowerLeft;
21
+
22
+ if (radii.length === 4) {
23
+
24
+ upperLeft = toCornerPoint(radii[0]);
25
+ upperRight = toCornerPoint(radii[1]);
26
+ lowerRight = toCornerPoint(radii[2]);
27
+ lowerLeft = toCornerPoint(radii[3]);
28
+
29
+ } else if (radii.length === 3) {
30
+
31
+ upperLeft = toCornerPoint(radii[0]);
32
+ upperRight = toCornerPoint(radii[1]);
33
+ lowerLeft = toCornerPoint(radii[1]);
34
+ lowerRight = toCornerPoint(radii[2]);
35
+
36
+ } else if (radii.length === 2) {
37
+
38
+ upperLeft = toCornerPoint(radii[0]);
39
+ lowerRight = toCornerPoint(radii[0]);
40
+ upperRight = toCornerPoint(radii[1]);
41
+ lowerLeft = toCornerPoint(radii[1]);
42
+
43
+ } else if (radii.length === 1) {
44
+
45
+ upperLeft = toCornerPoint(radii[0]);
46
+ upperRight = toCornerPoint(radii[0]);
47
+ lowerRight = toCornerPoint(radii[0]);
48
+ lowerLeft = toCornerPoint(radii[0]);
49
+
50
+ } else {
51
+
52
+ throw new Error(radii.length + " is not a valid size for radii sequence.");
53
+
54
+ }
55
+
56
+ const corners = [upperLeft, upperRight, lowerRight, lowerLeft];
57
+ const negativeCorner = corners.find(({x, y}) => x < 0 || y < 0);
58
+ //const negativeValue = negativeCorner?.x < 0 ? negativeCorner.x : negativeCorner?.y
59
+
60
+ if (corners.some(({x, y}) => !Number.isFinite(x) || !Number.isFinite(y))) {
61
+
62
+ return;
63
+
64
+ }
65
+
66
+ if (negativeCorner) {
67
+
68
+ throw new Error("Radius value " + negativeCorner + " is negative.");
69
+
70
+ }
71
+
72
+ fixOverlappingCorners(corners);
73
+
74
+ if (w < 0 && h < 0) {
75
+
76
+ this.moveTo(x - upperLeft.x, y);
77
+ this.ellipse(x + w + upperRight.x, y - upperRight.y, upperRight.x, upperRight.y, 0, -Math.PI * 1.5, -Math.PI);
78
+ this.ellipse(x + w + lowerRight.x, y + h + lowerRight.y, lowerRight.x, lowerRight.y, 0, -Math.PI, -Math.PI / 2);
79
+ this.ellipse(x - lowerLeft.x, y + h + lowerLeft.y, lowerLeft.x, lowerLeft.y, 0, -Math.PI / 2, 0);
80
+ this.ellipse(x - upperLeft.x, y - upperLeft.y, upperLeft.x, upperLeft.y, 0, 0, -Math.PI / 2);
81
+
82
+ } else if (w < 0) {
83
+
84
+ this.moveTo(x - upperLeft.x, y);
85
+ this.ellipse(x + w + upperRight.x, y + upperRight.y, upperRight.x, upperRight.y, 0, -Math.PI / 2, -Math.PI, 1);
86
+ this.ellipse(x + w + lowerRight.x, y + h - lowerRight.y, lowerRight.x, lowerRight.y, 0, -Math.PI, -Math.PI * 1.5, 1);
87
+ this.ellipse(x - lowerLeft.x, y + h - lowerLeft.y, lowerLeft.x, lowerLeft.y, 0, Math.PI / 2, 0, 1);
88
+ this.ellipse(x - upperLeft.x, y + upperLeft.y, upperLeft.x, upperLeft.y, 0, 0, -Math.PI / 2, 1);
89
+
90
+ } else if (h < 0) {
91
+
92
+ this.moveTo(x + upperLeft.x, y);
93
+ this.ellipse(x + w - upperRight.x, y - upperRight.y, upperRight.x, upperRight.y, 0, Math.PI / 2, 0, 1);
94
+ this.ellipse(x + w - lowerRight.x, y + h + lowerRight.y, lowerRight.x, lowerRight.y, 0, 0, -Math.PI / 2, 1);
95
+ this.ellipse(x + lowerLeft.x, y + h + lowerLeft.y, lowerLeft.x, lowerLeft.y, 0, -Math.PI / 2, -Math.PI, 1);
96
+ this.ellipse(x + upperLeft.x, y - upperLeft.y, upperLeft.x, upperLeft.y, 0, -Math.PI, -Math.PI * 1.5, 1);
97
+
98
+ } else {
99
+
100
+ this.moveTo(x + upperLeft.x, y);
101
+ this.ellipse(x + w - upperRight.x, y + upperRight.y, upperRight.x, upperRight.y, 0, -Math.PI / 2, 0);
102
+ this.ellipse(x + w - lowerRight.x, y + h - lowerRight.y, lowerRight.x, lowerRight.y, 0, 0, Math.PI / 2);
103
+ this.ellipse(x + lowerLeft.x, y + h - lowerLeft.y, lowerLeft.x, lowerLeft.y, 0, Math.PI / 2, Math.PI);
104
+ this.ellipse(x + upperLeft.x, y + upperLeft.y, upperLeft.x, upperLeft.y, 0, Math.PI, Math.PI * 1.5);
105
+
106
+ }
107
+
108
+ this.closePath();
109
+ this.moveTo(x, y);
110
+
111
+ /** @ignore */
112
+ function toDOMPointInit(value) {
113
+
114
+ const {x, y, z, w} = value;
115
+ return {x, y, z, w};
116
+
117
+ }
118
+
119
+ /** @ignore */
120
+ function parseRadiiArgument(value) {
121
+
122
+ // https://webidl.spec.whatwg.org/#es-union
123
+ // with 'optional (unrestricted double or DOMPointInit
124
+ // or sequence<(unrestricted double or DOMPointInit)>) radii = 0'
125
+ const type = typeof value;
126
+
127
+ if (type === "undefined" || value === null) {
128
+
129
+ return [0];
130
+
131
+ }
132
+ if (type === "function") {
133
+
134
+ return [NaN];
135
+
136
+ }
137
+ if (type === "object") {
138
+
139
+ if (typeof value[Symbol.iterator] === "function") {
140
+
141
+ return [...value].map((elem) => {
142
+ // https://webidl.spec.whatwg.org/#es-union
143
+ // with '(unrestricted double or DOMPointInit)'
144
+ const elemType = typeof elem;
145
+ if (elemType === "undefined" || elem === null) {
146
+ return 0;
147
+ }
148
+ if (elemType === "function") {
149
+ return NaN;
150
+ }
151
+ if (elemType === "object") {
152
+ return toDOMPointInit(elem);
153
+ }
154
+ return toUnrestrictedNumber(elem);
155
+ });
156
+
157
+ }
158
+
159
+ return [toDOMPointInit(value)];
160
+
161
+ }
162
+
163
+ return [toUnrestrictedNumber(value)];
164
+
165
+ }
166
+
167
+ /** @ignore */
168
+ function toUnrestrictedNumber(value) {
169
+
170
+ return +value;
171
+
172
+ }
173
+
174
+ /** @ignore */
175
+ function toCornerPoint(value) {
176
+
177
+ const asNumber = toUnrestrictedNumber(value);
178
+ if (Number.isFinite(asNumber)) {
179
+
180
+ return {
181
+ x: asNumber,
182
+ y: asNumber
183
+ };
184
+
185
+ }
186
+ if (Object(value) === value) {
187
+
188
+ return {
189
+ x: toUnrestrictedNumber(value.x || 0),
190
+ y: toUnrestrictedNumber(value.y || 0)
191
+ };
192
+
193
+ }
194
+
195
+ return {
196
+ x: NaN,
197
+ y: NaN
198
+ };
199
+
200
+ }
201
+
202
+ /** @ignore */
203
+ function fixOverlappingCorners(corners) {
204
+ const [upperLeft, upperRight, lowerRight, lowerLeft] = corners;
205
+ const factors = [
206
+ Math.abs(w) / (upperLeft.x + upperRight.x),
207
+ Math.abs(h) / (upperRight.y + lowerRight.y),
208
+ Math.abs(w) / (lowerRight.x + lowerLeft.x),
209
+ Math.abs(h) / (upperLeft.y + lowerLeft.y)
210
+ ];
211
+ const minFactor = Math.min(...factors);
212
+ if (minFactor <= 1) {
213
+ corners.forEach((radii) => {
214
+ radii.x *= minFactor;
215
+ radii.y *= minFactor;
216
+ });
217
+ }
218
+ }
219
+ }
220
+
221
+ if (typeof Path2D.prototype.roundRect === "undefined") {
222
+ Path2D.prototype.roundRect = roundRect;
223
+ }
224
+ if (globalThis.CanvasRenderingContext2D) {
225
+ if (typeof globalThis.CanvasRenderingContext2D.prototype.roundRect === "undefined") {
226
+ globalThis.CanvasRenderingContext2D.prototype.roundRect = roundRect;
227
+ }
228
+ }
229
+ if (globalThis.OffscreenCanvasRenderingContext2D) {
230
+ if (typeof globalThis.OffscreenCanvasRenderingContext2D.prototype.roundRect === "undefined") {
231
+ globalThis.OffscreenCanvasRenderingContext2D.prototype.roundRect = roundRect;
232
+ }
233
+ }
234
+
235
+ })();
@@ -227,7 +227,7 @@ class Renderable extends Rect {
227
227
  * A mask limits rendering elements to the shape and position of the given mask object.
228
228
  * So, if the renderable is larger than the mask, only the intersecting part of the renderable will be visible.
229
229
  * @public
230
- * @type {Rect|Polygon|Line|Ellipse}
230
+ * @type {Rect|RoundRect|Polygon|Line|Ellipse}
231
231
  * @name mask
232
232
  * @default undefined
233
233
  * @memberof Renderable#
@@ -120,14 +120,14 @@ var utils = {
120
120
  // never cache if a url is passed as parameter
121
121
  var index = url.indexOf("#");
122
122
  if (index !== -1) {
123
- url = url.substr(index, url.length);
123
+ url = url.slice(index, url.length);
124
124
  } else {
125
125
  return hash;
126
126
  }
127
127
  }
128
128
 
129
129
  // parse the url
130
- url.substr(1).split("&").filter(function (value) {
130
+ url.slice(1).split("&").filter(function (value) {
131
131
  return (value !== "");
132
132
  }).forEach(function (value) {
133
133
  var kv = value.split("=");
@@ -2,6 +2,7 @@ import Color from "./../../math/color.js";
2
2
  import Renderer from "./../renderer.js";
3
3
  import TextureCache from "./../texture_cache.js";
4
4
  import Ellipse from "./../../geometries/ellipse.js";
5
+ import RoundRect from "./../../geometries/roundrect.js";
5
6
  import { createCanvas } from "./../video.js";
6
7
 
7
8
 
@@ -529,20 +530,7 @@ class CanvasRenderer extends Renderer {
529
530
  var context = this.getContext();
530
531
 
531
532
  context.beginPath();
532
- if (typeof context.roundRect === "function") {
533
- //https://developer.chrome.com/blog/canvas2d/#round-rect
534
- context.roundRect(x, y, width, height, radius);
535
- } else {
536
- context.moveTo(x + radius, y);
537
- context.lineTo(x + width - radius, y);
538
- context.arcTo(x + width, y, x + width, y + radius, radius);
539
- context.lineTo(x + width, y + height - radius);
540
- context.arcTo(x + width, y + height, x + width - radius, y + height, radius);
541
- context.lineTo(x + radius, y + height);
542
- context.arcTo(x, y + height, x, y + height - radius, radius);
543
- context.lineTo(x, y + radius);
544
- context.arcTo(x, y, x + radius, y, radius);
545
- }
533
+ context.roundRect(x, y, width, height, radius);
546
534
  context[fill === true ? "fill" : "stroke"]();
547
535
  }
548
536
 
@@ -775,7 +763,7 @@ class CanvasRenderer extends Renderer {
775
763
  * @name setMask
776
764
  * @memberof CanvasRenderer.prototype
777
765
  * @function
778
- * @param {Rect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
766
+ * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
779
767
  */
780
768
  setMask(mask) {
781
769
  var context = this.getContext();
@@ -783,6 +771,8 @@ class CanvasRenderer extends Renderer {
783
771
 
784
772
  context.save();
785
773
 
774
+ context.beginPath();
775
+
786
776
  // https://github.com/melonjs/melonJS/issues/648
787
777
  if (mask instanceof Ellipse) {
788
778
  var hw = mask.radiusV.x,
@@ -799,14 +789,14 @@ class CanvasRenderer extends Renderer {
799
789
  ymin = _y - ymagic,
800
790
  ymax = _y + ymagic;
801
791
 
802
- context.beginPath();
803
792
  context.moveTo(_x, ty);
804
793
  context.bezierCurveTo(xmax, ty, rx, ymin, rx, _y);
805
794
  context.bezierCurveTo(rx, ymax, xmax, by, _x, by);
806
795
  context.bezierCurveTo(xmin, by, lx, ymax, lx, _y);
807
796
  context.bezierCurveTo(lx, ymin, xmin, ty, _x, ty);
797
+ } else if (mask instanceof RoundRect) {
798
+ context.roundRect(_x, _y, mask.width, mask.height, mask.radius);
808
799
  } else {
809
- context.beginPath();
810
800
  context.moveTo(_x + mask.points[0].x, _y + mask.points[0].y);
811
801
  var point;
812
802
  for (var i = 1; i < mask.points.length; i++) {
@@ -826,7 +816,7 @@ class CanvasRenderer extends Renderer {
826
816
  * @function
827
817
  */
828
818
  clearMask() {
829
- this.backBufferContext2D.restore();
819
+ this.getContext().restore();
830
820
  }
831
821
 
832
822
  };
@@ -345,13 +345,19 @@ class Renderer {
345
345
  * @param {boolean} [fill=false] fill the shape with the current color if true
346
346
  */
347
347
  stroke(shape, fill) {
348
- if (shape instanceof RoundRect) {
349
- this.strokeRoundRect(shape.left, shape.top, shape.width, shape.height, shape.radius, fill);
350
- } else if (shape instanceof Rect || shape instanceof Bounds) {
348
+ if (shape instanceof Rect || shape instanceof Bounds) {
351
349
  this.strokeRect(shape.left, shape.top, shape.width, shape.height, fill);
352
- } else if (shape instanceof Line || shape instanceof Polygon) {
350
+ return;
351
+ }
352
+ if (shape instanceof Line || shape instanceof Polygon) {
353
353
  this.strokePolygon(shape, fill);
354
- } else if (shape instanceof Ellipse) {
354
+ return;
355
+ }
356
+ if (shape instanceof RoundRect) {
357
+ this.strokeRoundRect(shape.left, shape.top, shape.width, shape.height, shape.radius, fill);
358
+ return;
359
+ }
360
+ if (shape instanceof Ellipse) {
355
361
  this.strokeEllipse(
356
362
  shape.pos.x,
357
363
  shape.pos.y,
@@ -359,7 +365,9 @@ class Renderer {
359
365
  shape.radiusV.y,
360
366
  fill
361
367
  );
368
+ return;
362
369
  }
370
+ throw new Error("Invalid geometry for fill/stroke");
363
371
  }
364
372
 
365
373
  /**
@@ -367,7 +375,7 @@ class Renderer {
367
375
  * @name fill
368
376
  * @memberof Renderer.prototype
369
377
  * @function
370
- * @param {Rect|Polygon|Line|Ellipse} shape a shape object to fill
378
+ * @param {Rect|RoundRect|Polygon|Line|Ellipse} shape a shape object to fill
371
379
  */
372
380
  fill(shape) {
373
381
  this.stroke(shape, true);
@@ -409,7 +417,7 @@ class Renderer {
409
417
  * @name setMask
410
418
  * @memberof Renderer.prototype
411
419
  * @function
412
- * @param {Rect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
420
+ * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
413
421
  */
414
422
  // eslint-disable-next-line no-unused-vars
415
423
  setMask(mask) {}
@@ -72,7 +72,7 @@ export function extractUniforms(gl, shader) {
72
72
  */
73
73
  return function (val) {
74
74
  var fnv = fn;
75
- if (val.length && fn.substr(-1) !== "v") {
75
+ if (val.length && fn.slice(-1) !== "v") {
76
76
  fnv += "v";
77
77
  }
78
78
  gl[fnv](locations[name], val);
@@ -1097,7 +1097,7 @@ class WebGLRenderer extends Renderer {
1097
1097
  * @name setMask
1098
1098
  * @memberof WebGLRenderer.prototype
1099
1099
  * @function
1100
- * @param {Rect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
1100
+ * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] the shape defining the mask to be applied
1101
1101
  */
1102
1102
  setMask(mask) {
1103
1103
  var gl = this.gl;