q5 2.9.21 → 2.9.23
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.
- package/.vscode/launch.json +26 -0
- package/bun.lockb +0 -0
- package/p5-tests/js/chai_helpers.js +20 -0
- package/p5-tests/js/mocha_setup.js +2 -0
- package/p5-tests/js/modernizr.js +5 -0
- package/p5-tests/js/p5_helpers.js +135 -0
- package/p5-tests/js/sinon.js +5949 -0
- package/p5-tests/mocha.css +289 -0
- package/p5-tests/test.html +71 -0
- package/p5-tests/unit/color/color_conversion.js +68 -0
- package/p5-tests/unit/color/creating_reading.js +217 -0
- package/p5-tests/unit/color/p5.Color.js +1000 -0
- package/p5-tests/unit/color/setting.js +289 -0
- package/p5-tests/unit/core/2d_primitives.js +490 -0
- package/p5-tests/unit/core/attributes.js +115 -0
- package/p5-tests/unit/core/curves.js +139 -0
- package/p5-tests/unit/core/environment.js +248 -0
- package/p5-tests/unit/core/error_helpers.js +1158 -0
- package/p5-tests/unit/core/main.js +340 -0
- package/p5-tests/unit/core/p5.Element.js +773 -0
- package/p5-tests/unit/core/p5.Graphics.js +179 -0
- package/p5-tests/unit/core/preload.js +285 -0
- package/p5-tests/unit/core/rendering.js +116 -0
- package/p5-tests/unit/core/structure.js +293 -0
- package/p5-tests/unit/core/transform.js +144 -0
- package/p5-tests/unit/core/version.js +28 -0
- package/p5-tests/unit/core/vertex.js +137 -0
- package/p5-tests/unit/dom/dom.js +2146 -0
- package/p5-tests/unit/events/acceleration.js +213 -0
- package/p5-tests/unit/events/keyboard.js +179 -0
- package/p5-tests/unit/events/mouse.js +487 -0
- package/p5-tests/unit/events/touch.js +180 -0
- package/p5-tests/unit/image/downloading.js +379 -0
- package/p5-tests/unit/image/filters.js +92 -0
- package/p5-tests/unit/image/loading.js +413 -0
- package/p5-tests/unit/image/p5.Image.js +201 -0
- package/p5-tests/unit/image/pixels.js +234 -0
- package/p5-tests/unit/io/files.js +378 -0
- package/p5-tests/unit/io/loadBytes.js +149 -0
- package/p5-tests/unit/io/loadImage.js +123 -0
- package/p5-tests/unit/io/loadJSON.js +185 -0
- package/p5-tests/unit/io/loadModel.js +215 -0
- package/p5-tests/unit/io/loadShader.js +176 -0
- package/p5-tests/unit/io/loadStrings.js +140 -0
- package/p5-tests/unit/io/loadTable.js +183 -0
- package/p5-tests/unit/io/loadXML.js +127 -0
- package/p5-tests/unit/io/saveModel.js +113 -0
- package/p5-tests/unit/io/saveTable.js +142 -0
- package/p5-tests/unit/math/calculation.js +452 -0
- package/p5-tests/unit/math/noise.js +66 -0
- package/p5-tests/unit/math/p5.Vector.js +1886 -0
- package/p5-tests/unit/math/random.js +177 -0
- package/p5-tests/unit/math/trigonometry.js +144 -0
- package/p5-tests/unit/spec.js +50 -0
- package/p5-tests/unit/typography/attributes.js +120 -0
- package/p5-tests/unit/typography/loadFont.js +162 -0
- package/p5-tests/unit/typography/p5.Font.js +63 -0
- package/p5-tests/unit/utilities/conversion.js +329 -0
- package/p5-tests/unit/utilities/time_date.js +133 -0
- package/package.json +1 -1
- package/q5.js +158 -54
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +8 -2
- package/src/q5-2d-drawing.js +20 -7
- package/src/q5-2d-image.js +4 -1
- package/src/q5-2d-text.js +7 -0
- package/src/q5-canvas.js +6 -5
- package/src/q5-color.js +5 -0
- package/src/q5-core.js +3 -1
- package/src/q5-input.js +12 -0
- package/src/q5-math.js +11 -3
- package/src/q5-record.js +2 -0
- package/src/q5-vector.js +33 -0
- package/src/q5-webgpu-canvas.js +11 -7
- package/src/q5-webgpu-drawing.js +15 -12
- package/src/q5-webgpu-image.js +1 -1
- package/src/q5-webgpu-text.js +22 -15
package/src/q5-2d-text.js
CHANGED
|
@@ -36,6 +36,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
36
36
|
fontMod = true;
|
|
37
37
|
styleHash = -1;
|
|
38
38
|
};
|
|
39
|
+
|
|
39
40
|
$.textSize = (x) => {
|
|
40
41
|
if (x == undefined || x == $._textSize) return $._textSize;
|
|
41
42
|
if ($._da) x *= $._da;
|
|
@@ -47,12 +48,14 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
47
48
|
leadDiff = leading - x;
|
|
48
49
|
}
|
|
49
50
|
};
|
|
51
|
+
|
|
50
52
|
$.textStyle = (x) => {
|
|
51
53
|
if (!x || x == emphasis) return emphasis;
|
|
52
54
|
emphasis = x;
|
|
53
55
|
fontMod = true;
|
|
54
56
|
styleHash = -1;
|
|
55
57
|
};
|
|
58
|
+
|
|
56
59
|
$.textLeading = (x) => {
|
|
57
60
|
leadingSet = true;
|
|
58
61
|
if (x == undefined || x == leading) return leading;
|
|
@@ -61,6 +64,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
61
64
|
leadDiff = x - $._textSize;
|
|
62
65
|
styleHash = -1;
|
|
63
66
|
};
|
|
67
|
+
|
|
64
68
|
$.textAlign = (horiz, vert) => {
|
|
65
69
|
$.ctx.textAlign = $._textAlign = horiz;
|
|
66
70
|
if (vert) {
|
|
@@ -90,6 +94,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
90
94
|
if (enable !== undefined) useCache = enable;
|
|
91
95
|
return useCache;
|
|
92
96
|
};
|
|
97
|
+
|
|
93
98
|
$.createTextImage = (str, w, h) => {
|
|
94
99
|
genTextImage = true;
|
|
95
100
|
img = $.text(str, 0, 0, w, h);
|
|
@@ -98,6 +103,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
98
103
|
};
|
|
99
104
|
|
|
100
105
|
let lines = [];
|
|
106
|
+
|
|
101
107
|
$.text = (str, x, y, w, h) => {
|
|
102
108
|
if (str === undefined || (!$._doFill && !$._doStroke)) return;
|
|
103
109
|
str = str.toString();
|
|
@@ -223,6 +229,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
223
229
|
$.textImage(img, x, y);
|
|
224
230
|
}
|
|
225
231
|
};
|
|
232
|
+
|
|
226
233
|
$.textImage = (img, x, y) => {
|
|
227
234
|
if (typeof img == 'string') img = $.createTextImage(img);
|
|
228
235
|
|
package/src/q5-canvas.js
CHANGED
|
@@ -146,7 +146,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
146
146
|
return g;
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
async function saveFile(data, name, ext) {
|
|
150
150
|
name = name || 'untitled';
|
|
151
151
|
ext = ext || 'png';
|
|
152
152
|
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
@@ -174,18 +174,19 @@ Q5.modules.canvas = ($, q) => {
|
|
|
174
174
|
a.download = name + '.' + ext;
|
|
175
175
|
a.click();
|
|
176
176
|
URL.revokeObjectURL(a.href);
|
|
177
|
-
}
|
|
177
|
+
}
|
|
178
|
+
|
|
178
179
|
$.save = (a, b, c) => {
|
|
179
180
|
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
180
181
|
c = b;
|
|
181
182
|
b = a;
|
|
182
183
|
a = $.canvas;
|
|
183
184
|
}
|
|
184
|
-
if (c) return
|
|
185
|
+
if (c) return saveFile(a, b, c);
|
|
185
186
|
if (b) {
|
|
186
187
|
b = b.split('.');
|
|
187
|
-
|
|
188
|
-
} else
|
|
188
|
+
saveFile(a, b[0], b.at(-1));
|
|
189
|
+
} else saveFile(a);
|
|
189
190
|
};
|
|
190
191
|
|
|
191
192
|
$._setCanvasSize = (w, h) => {
|
package/src/q5-color.js
CHANGED
|
@@ -146,6 +146,7 @@ Q5.Color = class {
|
|
|
146
146
|
this._q5Color = true;
|
|
147
147
|
}
|
|
148
148
|
};
|
|
149
|
+
|
|
149
150
|
Q5.ColorOKLCH = class extends Q5.Color {
|
|
150
151
|
constructor(l, c, h, a) {
|
|
151
152
|
super();
|
|
@@ -158,6 +159,7 @@ Q5.ColorOKLCH = class extends Q5.Color {
|
|
|
158
159
|
return `oklch(${this.l} ${this.c} ${this.h} / ${this.a})`;
|
|
159
160
|
}
|
|
160
161
|
};
|
|
162
|
+
|
|
161
163
|
Q5.ColorRGBA = class extends Q5.Color {
|
|
162
164
|
constructor(r, g, b, a) {
|
|
163
165
|
super();
|
|
@@ -173,11 +175,13 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
173
175
|
return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
174
176
|
}
|
|
175
177
|
};
|
|
178
|
+
|
|
176
179
|
Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
|
|
177
180
|
toString() {
|
|
178
181
|
return `color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
179
182
|
}
|
|
180
183
|
};
|
|
184
|
+
|
|
181
185
|
// legacy 8-bit (0-255) integer color format
|
|
182
186
|
Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
|
|
183
187
|
constructor(r, g, b, a) {
|
|
@@ -202,6 +206,7 @@ Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
|
|
|
202
206
|
return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
|
|
203
207
|
}
|
|
204
208
|
};
|
|
209
|
+
|
|
205
210
|
// p3 10-bit color in integer color format, for backwards compatibility
|
|
206
211
|
Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
|
|
207
212
|
constructor(r, g, b, a) {
|
package/src/q5-core.js
CHANGED
|
@@ -118,7 +118,7 @@ function Q5(scope, parent, renderer) {
|
|
|
118
118
|
};
|
|
119
119
|
$.loop = () => {
|
|
120
120
|
$._loop = true;
|
|
121
|
-
if (looper == null) $._draw();
|
|
121
|
+
if ($._setupDone && looper == null) $._draw();
|
|
122
122
|
};
|
|
123
123
|
$.isLooping = () => $._loop;
|
|
124
124
|
$.redraw = (n = 1) => {
|
|
@@ -309,6 +309,8 @@ function createCanvas(w, h, opt) {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
Q5.version = Q5.VERSION = '2.9';
|
|
313
|
+
|
|
312
314
|
if (typeof document == 'object') {
|
|
313
315
|
document.addEventListener('DOMContentLoaded', () => {
|
|
314
316
|
if (!Q5._hasGlobal) new Q5('auto');
|
package/src/q5-input.js
CHANGED
|
@@ -59,6 +59,7 @@ Q5.modules.input = ($, q) => {
|
|
|
59
59
|
q.moveX = e.movementX;
|
|
60
60
|
q.moveY = e.movementY;
|
|
61
61
|
};
|
|
62
|
+
|
|
62
63
|
$._onmousedown = (e) => {
|
|
63
64
|
$._startAudio();
|
|
64
65
|
$._updateMouse(e);
|
|
@@ -66,22 +67,26 @@ Q5.modules.input = ($, q) => {
|
|
|
66
67
|
q.mouseButton = mouseBtns[e.button];
|
|
67
68
|
$.mousePressed(e);
|
|
68
69
|
};
|
|
70
|
+
|
|
69
71
|
$._onmousemove = (e) => {
|
|
70
72
|
$._updateMouse(e);
|
|
71
73
|
if ($.mouseIsPressed) $.mouseDragged(e);
|
|
72
74
|
else $.mouseMoved(e);
|
|
73
75
|
};
|
|
76
|
+
|
|
74
77
|
$._onmouseup = (e) => {
|
|
75
78
|
$._updateMouse(e);
|
|
76
79
|
q.mouseIsPressed = false;
|
|
77
80
|
$.mouseReleased(e);
|
|
78
81
|
};
|
|
82
|
+
|
|
79
83
|
$._onclick = (e) => {
|
|
80
84
|
$._updateMouse(e);
|
|
81
85
|
q.mouseIsPressed = true;
|
|
82
86
|
$.mouseClicked(e);
|
|
83
87
|
q.mouseIsPressed = false;
|
|
84
88
|
};
|
|
89
|
+
|
|
85
90
|
$._onwheel = (e) => {
|
|
86
91
|
$._updateMouse(e);
|
|
87
92
|
e.delta = e.deltaY;
|
|
@@ -99,9 +104,11 @@ Q5.modules.input = ($, q) => {
|
|
|
99
104
|
}
|
|
100
105
|
$.canvas.style.cursor = name + pfx;
|
|
101
106
|
};
|
|
107
|
+
|
|
102
108
|
$.noCursor = () => {
|
|
103
109
|
$.canvas.style.cursor = 'none';
|
|
104
110
|
};
|
|
111
|
+
|
|
105
112
|
if (window) {
|
|
106
113
|
$.requestPointerLock = document.body?.requestPointerLock;
|
|
107
114
|
$.exitPointerLock = document.exitPointerLock;
|
|
@@ -117,6 +124,7 @@ Q5.modules.input = ($, q) => {
|
|
|
117
124
|
$.keyPressed(e);
|
|
118
125
|
if (e.key.length == 1) $.keyTyped(e);
|
|
119
126
|
};
|
|
127
|
+
|
|
120
128
|
$._onkeyup = (e) => {
|
|
121
129
|
q.keyIsPressed = false;
|
|
122
130
|
q.key = e.key;
|
|
@@ -124,6 +132,7 @@ Q5.modules.input = ($, q) => {
|
|
|
124
132
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = false;
|
|
125
133
|
$.keyReleased(e);
|
|
126
134
|
};
|
|
135
|
+
|
|
127
136
|
$.keyIsDown = (v) => !!keysHeld[typeof v == 'string' ? v.toLowerCase() : v];
|
|
128
137
|
|
|
129
138
|
function getTouchInfo(touch) {
|
|
@@ -136,6 +145,7 @@ Q5.modules.input = ($, q) => {
|
|
|
136
145
|
id: touch.identifier
|
|
137
146
|
};
|
|
138
147
|
}
|
|
148
|
+
|
|
139
149
|
$._ontouchstart = (e) => {
|
|
140
150
|
$._startAudio();
|
|
141
151
|
q.touches = [...e.touches].map(getTouchInfo);
|
|
@@ -148,6 +158,7 @@ Q5.modules.input = ($, q) => {
|
|
|
148
158
|
}
|
|
149
159
|
if (!$.touchStarted(e)) e.preventDefault();
|
|
150
160
|
};
|
|
161
|
+
|
|
151
162
|
$._ontouchmove = (e) => {
|
|
152
163
|
q.touches = [...e.touches].map(getTouchInfo);
|
|
153
164
|
if (!$._isTouchAware) {
|
|
@@ -157,6 +168,7 @@ Q5.modules.input = ($, q) => {
|
|
|
157
168
|
}
|
|
158
169
|
if (!$.touchMoved(e)) e.preventDefault();
|
|
159
170
|
};
|
|
171
|
+
|
|
160
172
|
$._ontouchend = (e) => {
|
|
161
173
|
q.touches = [...e.touches].map(getTouchInfo);
|
|
162
174
|
if (!$._isTouchAware && !$.touches.length) {
|
package/src/q5-math.js
CHANGED
|
@@ -25,6 +25,7 @@ Q5.modules.math = ($, q) => {
|
|
|
25
25
|
|
|
26
26
|
$.angleMode = (mode) => {
|
|
27
27
|
angleMode = $._angleMode = mode == 0 || mode == 'radians' ? 0 : 1;
|
|
28
|
+
return !angleMode ? 'radians' : 'degrees';
|
|
28
29
|
};
|
|
29
30
|
let DEGTORAD = ($._DEGTORAD = Math.PI / 180);
|
|
30
31
|
let RADTODEG = ($._RADTODEG = 180 / Math.PI);
|
|
@@ -42,13 +43,15 @@ Q5.modules.math = ($, q) => {
|
|
|
42
43
|
return Math.min(Math.max(val, ostop), ostart);
|
|
43
44
|
}
|
|
44
45
|
};
|
|
45
|
-
|
|
46
|
-
$.constrain = (x, lo, hi) => Math.min(Math.max(x, lo), hi);
|
|
46
|
+
|
|
47
47
|
$.dist = function () {
|
|
48
48
|
let a = arguments;
|
|
49
49
|
if (a.length == 4) return Math.hypot(a[0] - a[2], a[1] - a[3]);
|
|
50
50
|
else return Math.hypot(a[0] - a[3], a[1] - a[4], a[2] - a[5]);
|
|
51
51
|
};
|
|
52
|
+
|
|
53
|
+
$.lerp = (a, b, t) => a * (1 - t) + b * t;
|
|
54
|
+
$.constrain = (x, lo, hi) => Math.min(Math.max(x, lo), hi);
|
|
52
55
|
$.norm = (value, start, stop) => $.map(value, start, stop, 0, 1);
|
|
53
56
|
$.sq = (x) => x * x;
|
|
54
57
|
$.fract = (x) => x - Math.floor(x);
|
|
@@ -69,7 +72,6 @@ Q5.modules.math = ($, q) => {
|
|
|
69
72
|
let a = Math.atan(x);
|
|
70
73
|
return !angleMode ? a : a * RADTODEG;
|
|
71
74
|
};
|
|
72
|
-
|
|
73
75
|
$.atan2 = (y, x) => {
|
|
74
76
|
let a = Math.atan2(y, x);
|
|
75
77
|
return !angleMode ? a : a * RADTODEG;
|
|
@@ -93,6 +95,7 @@ Q5.modules.math = ($, q) => {
|
|
|
93
95
|
}
|
|
94
96
|
};
|
|
95
97
|
}
|
|
98
|
+
|
|
96
99
|
function shr3() {
|
|
97
100
|
let jsr, seed;
|
|
98
101
|
let m = 4294967295;
|
|
@@ -111,6 +114,7 @@ Q5.modules.math = ($, q) => {
|
|
|
111
114
|
}
|
|
112
115
|
};
|
|
113
116
|
}
|
|
117
|
+
|
|
114
118
|
let rng1 = shr3();
|
|
115
119
|
rng1.setSeed();
|
|
116
120
|
|
|
@@ -127,6 +131,7 @@ Q5.modules.math = ($, q) => {
|
|
|
127
131
|
return a[Math.trunc(a.length * rng1.rand())];
|
|
128
132
|
}
|
|
129
133
|
};
|
|
134
|
+
|
|
130
135
|
$.randomGenerator = (method) => {
|
|
131
136
|
if (method == $.LCG) rng1 = lcg();
|
|
132
137
|
else if (method == $.SHR3) rng1 = shr3();
|
|
@@ -282,13 +287,16 @@ Q5.modules.math = ($, q) => {
|
|
|
282
287
|
q.Noise = Q5[mode[0].toUpperCase() + mode.slice(1) + 'Noise'];
|
|
283
288
|
_noise = null;
|
|
284
289
|
};
|
|
290
|
+
|
|
285
291
|
$.noiseSeed = (seed) => {
|
|
286
292
|
_noise = new $.Noise(seed);
|
|
287
293
|
};
|
|
294
|
+
|
|
288
295
|
$.noise = (x = 0, y = 0, z = 0) => {
|
|
289
296
|
_noise ??= new $.Noise();
|
|
290
297
|
return _noise.noise(x, y, z);
|
|
291
298
|
};
|
|
299
|
+
|
|
292
300
|
$.noiseDetail = (lod, falloff) => {
|
|
293
301
|
_noise ??= new $.Noise();
|
|
294
302
|
if (lod > 0) _noise.octaves = lod;
|
package/src/q5-record.js
ADDED
package/src/q5-vector.js
CHANGED
|
@@ -11,15 +11,18 @@ Q5.Vector = class {
|
|
|
11
11
|
this._cn = null;
|
|
12
12
|
this._cnsq = null;
|
|
13
13
|
}
|
|
14
|
+
|
|
14
15
|
set(x, y, z) {
|
|
15
16
|
this.x = x?.x || x || 0;
|
|
16
17
|
this.y = x?.y || y || 0;
|
|
17
18
|
this.z = x?.z || z || 0;
|
|
18
19
|
return this;
|
|
19
20
|
}
|
|
21
|
+
|
|
20
22
|
copy() {
|
|
21
23
|
return new Q5.Vector(this.x, this.y, this.z);
|
|
22
24
|
}
|
|
25
|
+
|
|
23
26
|
_arg2v(x, y, z) {
|
|
24
27
|
if (x?.x !== undefined) return x;
|
|
25
28
|
if (y !== undefined) {
|
|
@@ -27,10 +30,12 @@ Q5.Vector = class {
|
|
|
27
30
|
}
|
|
28
31
|
return { x: x, y: x, z: x };
|
|
29
32
|
}
|
|
33
|
+
|
|
30
34
|
_calcNorm() {
|
|
31
35
|
this._cnsq = this.x * this.x + this.y * this.y + this.z * this.z;
|
|
32
36
|
this._cn = Math.sqrt(this._cnsq);
|
|
33
37
|
}
|
|
38
|
+
|
|
34
39
|
add() {
|
|
35
40
|
let u = this._arg2v(...arguments);
|
|
36
41
|
this.x += u.x;
|
|
@@ -38,6 +43,7 @@ Q5.Vector = class {
|
|
|
38
43
|
this.z += u.z;
|
|
39
44
|
return this;
|
|
40
45
|
}
|
|
46
|
+
|
|
41
47
|
rem() {
|
|
42
48
|
let u = this._arg2v(...arguments);
|
|
43
49
|
this.x %= u.x;
|
|
@@ -45,6 +51,7 @@ Q5.Vector = class {
|
|
|
45
51
|
this.z %= u.z;
|
|
46
52
|
return this;
|
|
47
53
|
}
|
|
54
|
+
|
|
48
55
|
sub() {
|
|
49
56
|
let u = this._arg2v(...arguments);
|
|
50
57
|
this.x -= u.x;
|
|
@@ -52,6 +59,7 @@ Q5.Vector = class {
|
|
|
52
59
|
this.z -= u.z;
|
|
53
60
|
return this;
|
|
54
61
|
}
|
|
62
|
+
|
|
55
63
|
mult() {
|
|
56
64
|
let u = this._arg2v(...arguments);
|
|
57
65
|
this.x *= u.x;
|
|
@@ -59,6 +67,7 @@ Q5.Vector = class {
|
|
|
59
67
|
this.z *= u.z;
|
|
60
68
|
return this;
|
|
61
69
|
}
|
|
70
|
+
|
|
62
71
|
div() {
|
|
63
72
|
let u = this._arg2v(...arguments);
|
|
64
73
|
if (u.x) this.x /= u.x;
|
|
@@ -69,18 +78,22 @@ Q5.Vector = class {
|
|
|
69
78
|
else this.z = 0;
|
|
70
79
|
return this;
|
|
71
80
|
}
|
|
81
|
+
|
|
72
82
|
mag() {
|
|
73
83
|
this._calcNorm();
|
|
74
84
|
return this._cn;
|
|
75
85
|
}
|
|
86
|
+
|
|
76
87
|
magSq() {
|
|
77
88
|
this._calcNorm();
|
|
78
89
|
return this._cnsq;
|
|
79
90
|
}
|
|
91
|
+
|
|
80
92
|
dot() {
|
|
81
93
|
let u = this._arg2v(...arguments);
|
|
82
94
|
return this.x * u.x + this.y * u.y + this.z * u.z;
|
|
83
95
|
}
|
|
96
|
+
|
|
84
97
|
dist() {
|
|
85
98
|
let u = this._arg2v(...arguments);
|
|
86
99
|
let x = this.x - u.x;
|
|
@@ -88,6 +101,7 @@ Q5.Vector = class {
|
|
|
88
101
|
let z = this.z - u.z;
|
|
89
102
|
return Math.sqrt(x * x + y * y + z * z);
|
|
90
103
|
}
|
|
104
|
+
|
|
91
105
|
cross() {
|
|
92
106
|
let u = this._arg2v(...arguments);
|
|
93
107
|
let x = this.y * u.z - this.z * u.y;
|
|
@@ -98,6 +112,7 @@ Q5.Vector = class {
|
|
|
98
112
|
this.z = z;
|
|
99
113
|
return this;
|
|
100
114
|
}
|
|
115
|
+
|
|
101
116
|
normalize() {
|
|
102
117
|
this._calcNorm();
|
|
103
118
|
let n = this._cn;
|
|
@@ -110,6 +125,7 @@ Q5.Vector = class {
|
|
|
110
125
|
this._cnsq = 1;
|
|
111
126
|
return this;
|
|
112
127
|
}
|
|
128
|
+
|
|
113
129
|
limit(m) {
|
|
114
130
|
this._calcNorm();
|
|
115
131
|
let n = this._cn;
|
|
@@ -123,6 +139,7 @@ Q5.Vector = class {
|
|
|
123
139
|
}
|
|
124
140
|
return this;
|
|
125
141
|
}
|
|
142
|
+
|
|
126
143
|
setMag(m) {
|
|
127
144
|
this._calcNorm();
|
|
128
145
|
let n = this._cn;
|
|
@@ -134,15 +151,18 @@ Q5.Vector = class {
|
|
|
134
151
|
this._cnsq = m * m;
|
|
135
152
|
return this;
|
|
136
153
|
}
|
|
154
|
+
|
|
137
155
|
heading() {
|
|
138
156
|
return this._$.atan2(this.y, this.x);
|
|
139
157
|
}
|
|
158
|
+
|
|
140
159
|
setHeading(ang) {
|
|
141
160
|
let mag = this.mag();
|
|
142
161
|
this.x = mag * this._$.cos(ang);
|
|
143
162
|
this.y = mag * this._$.sin(ang);
|
|
144
163
|
return this;
|
|
145
164
|
}
|
|
165
|
+
|
|
146
166
|
rotate(ang) {
|
|
147
167
|
let costh = this._$.cos(ang);
|
|
148
168
|
let sinth = this._$.sin(ang);
|
|
@@ -152,12 +172,14 @@ Q5.Vector = class {
|
|
|
152
172
|
this.y = vy;
|
|
153
173
|
return this;
|
|
154
174
|
}
|
|
175
|
+
|
|
155
176
|
angleBetween() {
|
|
156
177
|
let u = this._arg2v(...arguments);
|
|
157
178
|
let o = Q5.Vector.cross(this, u);
|
|
158
179
|
let ang = this._$.atan2(o.mag(), this.dot(u));
|
|
159
180
|
return ang * Math.sign(o.z || 1);
|
|
160
181
|
}
|
|
182
|
+
|
|
161
183
|
lerp() {
|
|
162
184
|
let args = [...arguments];
|
|
163
185
|
let amt = args.at(-1);
|
|
@@ -168,6 +190,7 @@ Q5.Vector = class {
|
|
|
168
190
|
this.z += (u.z - this.z) * amt;
|
|
169
191
|
return this;
|
|
170
192
|
}
|
|
193
|
+
|
|
171
194
|
slerp() {
|
|
172
195
|
let args = [...arguments];
|
|
173
196
|
let amt = args.at(-1);
|
|
@@ -206,17 +229,21 @@ Q5.Vector = class {
|
|
|
206
229
|
this.z = this.z * cosMultiplier + ey.z * sinMultiplier;
|
|
207
230
|
return this;
|
|
208
231
|
}
|
|
232
|
+
|
|
209
233
|
reflect(n) {
|
|
210
234
|
n.normalize();
|
|
211
235
|
return this.sub(n.mult(2 * this.dot(n)));
|
|
212
236
|
}
|
|
237
|
+
|
|
213
238
|
array() {
|
|
214
239
|
return [this.x, this.y, this.z];
|
|
215
240
|
}
|
|
241
|
+
|
|
216
242
|
equals(u, epsilon) {
|
|
217
243
|
epsilon ??= Number.EPSILON || 0;
|
|
218
244
|
return Math.abs(u.x - this.x) < epsilon && Math.abs(u.y - this.y) < epsilon && Math.abs(u.z - this.z) < epsilon;
|
|
219
245
|
}
|
|
246
|
+
|
|
220
247
|
fromAngle(th, l) {
|
|
221
248
|
if (l === undefined) l = 1;
|
|
222
249
|
this._cn = l;
|
|
@@ -226,6 +253,7 @@ Q5.Vector = class {
|
|
|
226
253
|
this.z = 0;
|
|
227
254
|
return this;
|
|
228
255
|
}
|
|
256
|
+
|
|
229
257
|
fromAngles(th, ph, l) {
|
|
230
258
|
if (l === undefined) l = 1;
|
|
231
259
|
this._cn = l;
|
|
@@ -239,18 +267,22 @@ Q5.Vector = class {
|
|
|
239
267
|
this.z = l * sinth * cosph;
|
|
240
268
|
return this;
|
|
241
269
|
}
|
|
270
|
+
|
|
242
271
|
random2D() {
|
|
243
272
|
this._cn = this._cnsq = 1;
|
|
244
273
|
return this.fromAngle(Math.random() * Math.PI * 2);
|
|
245
274
|
}
|
|
275
|
+
|
|
246
276
|
random3D() {
|
|
247
277
|
this._cn = this._cnsq = 1;
|
|
248
278
|
return this.fromAngles(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
|
|
249
279
|
}
|
|
280
|
+
|
|
250
281
|
toString() {
|
|
251
282
|
return `[${this.x}, ${this.y}, ${this.z}]`;
|
|
252
283
|
}
|
|
253
284
|
};
|
|
285
|
+
|
|
254
286
|
Q5.Vector.add = (v, u) => v.copy().add(u);
|
|
255
287
|
Q5.Vector.cross = (v, u) => v.copy().cross(u);
|
|
256
288
|
Q5.Vector.dist = (v, u) => Math.hypot(v.x - u.x, v.y - u.y, v.z - u.z);
|
|
@@ -267,6 +299,7 @@ Q5.Vector.mult = (v, u) => v.copy().mult(u);
|
|
|
267
299
|
Q5.Vector.normalize = (v) => v.copy().normalize();
|
|
268
300
|
Q5.Vector.rem = (v, u) => v.copy().rem(u);
|
|
269
301
|
Q5.Vector.sub = (v, u) => v.copy().sub(u);
|
|
302
|
+
|
|
270
303
|
for (let k of ['fromAngle', 'fromAngles', 'random2D', 'random3D']) {
|
|
271
304
|
Q5.Vector[k] = (u, v, t) => new Q5.Vector()[k](u, v, t);
|
|
272
305
|
}
|
package/src/q5-webgpu-canvas.js
CHANGED
|
@@ -168,7 +168,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
168
168
|
$.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
|
|
169
169
|
|
|
170
170
|
$.resetMatrix = () => {
|
|
171
|
-
//
|
|
171
|
+
// initialize the transformation matrix as 4x4 identity matrix
|
|
172
172
|
|
|
173
173
|
// prettier-ignore
|
|
174
174
|
$._matrix = [
|
|
@@ -192,7 +192,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
192
192
|
|
|
193
193
|
$.translate = (x, y, z) => {
|
|
194
194
|
if (!x && !y && !z) return;
|
|
195
|
-
//
|
|
195
|
+
// update the translation values
|
|
196
196
|
$._matrix[12] += x;
|
|
197
197
|
$._matrix[13] -= y;
|
|
198
198
|
$._matrix[14] += z || 0;
|
|
@@ -291,34 +291,35 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
291
291
|
else m = args;
|
|
292
292
|
|
|
293
293
|
if (m.length == 9) {
|
|
294
|
-
//
|
|
294
|
+
// convert 3x3 matrix to 4x4 matrix
|
|
295
295
|
m = [m[0], m[1], 0, m[2], m[3], m[4], 0, m[5], 0, 0, 1, 0, m[6], m[7], 0, m[8]];
|
|
296
296
|
} else if (m.length != 16) {
|
|
297
297
|
throw new Error('Matrix must be a 3x3 or 4x4 array.');
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
//
|
|
300
|
+
// overwrite the current transformation matrix
|
|
301
301
|
$._matrix = m.slice();
|
|
302
302
|
$._matrixDirty = true;
|
|
303
303
|
};
|
|
304
304
|
|
|
305
|
-
//
|
|
305
|
+
// function to save the current matrix state if dirty
|
|
306
306
|
$._saveMatrix = () => {
|
|
307
307
|
transformStates.push($._matrix.slice());
|
|
308
308
|
$._transformIndex = transformStates.length - 1;
|
|
309
309
|
$._matrixDirty = false;
|
|
310
310
|
};
|
|
311
311
|
|
|
312
|
-
//
|
|
312
|
+
// push the current matrix index onto the stack
|
|
313
313
|
$.pushMatrix = () => {
|
|
314
314
|
if ($._matrixDirty) $._saveMatrix();
|
|
315
315
|
$._transformIndexStack.push($._transformIndex);
|
|
316
316
|
};
|
|
317
|
+
|
|
317
318
|
$.popMatrix = () => {
|
|
318
319
|
if (!$._transformIndexStack.length) {
|
|
319
320
|
return console.warn('Matrix index stack is empty!');
|
|
320
321
|
}
|
|
321
|
-
//
|
|
322
|
+
// pop the last matrix index and set it as the current matrix index
|
|
322
323
|
let idx = $._transformIndexStack.pop();
|
|
323
324
|
$._matrix = transformStates[idx].slice();
|
|
324
325
|
$._transformIndex = idx;
|
|
@@ -329,6 +330,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
329
330
|
$.pushMatrix();
|
|
330
331
|
$.pushStyles();
|
|
331
332
|
};
|
|
333
|
+
|
|
332
334
|
$.pop = () => {
|
|
333
335
|
$.popMatrix();
|
|
334
336
|
$.popStyles();
|
|
@@ -380,6 +382,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
380
382
|
'max' // 4
|
|
381
383
|
];
|
|
382
384
|
|
|
385
|
+
// other blend modes are not supported yet
|
|
383
386
|
const blendModes = {
|
|
384
387
|
normal: [2, 3, 0, 2, 3, 0],
|
|
385
388
|
// destination_over: [6, 1, 0, 6, 1, 0],
|
|
@@ -410,6 +413,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
410
413
|
}
|
|
411
414
|
|
|
412
415
|
$._blendMode = 'normal';
|
|
416
|
+
|
|
413
417
|
$.blendMode = (mode) => {
|
|
414
418
|
if (mode == $._blendMode) return;
|
|
415
419
|
if (mode == 'source-over') mode = 'normal';
|
package/src/q5-webgpu-drawing.js
CHANGED
|
@@ -282,19 +282,22 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
282
282
|
if ($._matrixDirty) $._saveMatrix();
|
|
283
283
|
let ti = $._transformIndex,
|
|
284
284
|
ci = $._stroke,
|
|
285
|
-
sw = $._strokeWeight
|
|
286
|
-
hsw = sw / 2;
|
|
285
|
+
sw = $._strokeWeight;
|
|
287
286
|
|
|
288
287
|
if (sw < 2) {
|
|
289
288
|
let [l, r, t, b] = $._calcBox(x, y, sw, sw, 'corner');
|
|
290
289
|
addRect(l, t, r, t, r, b, l, b, ci, ti);
|
|
291
290
|
} else {
|
|
292
291
|
let n = getArcSegments(sw);
|
|
293
|
-
|
|
292
|
+
sw /= 2;
|
|
293
|
+
addEllipse(x, y, sw, sw, n, ci, ti);
|
|
294
294
|
}
|
|
295
295
|
};
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
$.stokeJoin = (x) => {
|
|
298
|
+
$.log("q5 WebGPU doesn't support changing stroke join style.");
|
|
299
|
+
};
|
|
300
|
+
|
|
298
301
|
$.line = (x1, y1, x2, y2) => {
|
|
299
302
|
if ($._matrixDirty) $._saveMatrix();
|
|
300
303
|
let ti = $._transformIndex,
|
|
@@ -302,12 +305,12 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
302
305
|
sw = $._strokeWeight,
|
|
303
306
|
hsw = sw / 2;
|
|
304
307
|
|
|
305
|
-
//
|
|
308
|
+
// calculate the direction vector and length
|
|
306
309
|
let dx = x2 - x1,
|
|
307
310
|
dy = y2 - y1,
|
|
308
311
|
length = Math.hypot(dx, dy);
|
|
309
312
|
|
|
310
|
-
//
|
|
313
|
+
// calculate the perpendicular vector for line thickness
|
|
311
314
|
let px = -(dy / length) * hsw,
|
|
312
315
|
py = (dx / length) * hsw;
|
|
313
316
|
|
|
@@ -343,10 +346,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
343
346
|
|
|
344
347
|
$.endShape = (close) => {
|
|
345
348
|
if (curveVertices.length > 0) {
|
|
346
|
-
//
|
|
349
|
+
// duplicate start and end points if necessary
|
|
347
350
|
let points = [...curveVertices];
|
|
348
351
|
if (points.length < 4) {
|
|
349
|
-
//
|
|
352
|
+
// duplicate first and last points
|
|
350
353
|
while (points.length < 4) {
|
|
351
354
|
points.unshift(points[0]);
|
|
352
355
|
points.push(points[points.length - 1]);
|
|
@@ -387,7 +390,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
387
390
|
throw new Error('A shape must have at least 3 vertices.');
|
|
388
391
|
}
|
|
389
392
|
|
|
390
|
-
//
|
|
393
|
+
// close the shape if requested
|
|
391
394
|
if (close) {
|
|
392
395
|
let firstIndex = 0;
|
|
393
396
|
let lastIndex = (shapeVertCount - 1) * 4;
|
|
@@ -404,7 +407,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
404
407
|
}
|
|
405
408
|
|
|
406
409
|
if ($._doFill) {
|
|
407
|
-
//
|
|
410
|
+
// triangulate the shape
|
|
408
411
|
for (let i = 1; i < shapeVertCount - 1; i++) {
|
|
409
412
|
let v0 = 0;
|
|
410
413
|
let v1 = i * 4;
|
|
@@ -418,7 +421,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
418
421
|
}
|
|
419
422
|
|
|
420
423
|
if ($._doStroke) {
|
|
421
|
-
//
|
|
424
|
+
// draw lines between vertices
|
|
422
425
|
for (let i = 0; i < shapeVertCount - 1; i++) {
|
|
423
426
|
let v1 = i * 4;
|
|
424
427
|
let v2 = (i + 1) * 4;
|
|
@@ -431,7 +434,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
431
434
|
}
|
|
432
435
|
}
|
|
433
436
|
|
|
434
|
-
//
|
|
437
|
+
// reset for the next shape
|
|
435
438
|
shapeVertCount = 0;
|
|
436
439
|
sv = [];
|
|
437
440
|
curveVertices = [];
|
package/src/q5-webgpu-image.js
CHANGED
|
@@ -151,7 +151,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
151
151
|
|
|
152
152
|
tIdx = (tIdx + 1) % MAX_TEXTURES;
|
|
153
153
|
|
|
154
|
-
//
|
|
154
|
+
// if the texture array is full, destroy the oldest texture
|
|
155
155
|
if ($._textures[tIdx]) {
|
|
156
156
|
$._textures[tIdx].destroy();
|
|
157
157
|
delete $._textures[tIdx];
|