q5 3.5.0 → 3.5.2
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/deno.json +1 -1
- package/package.json +1 -1
- package/q5.d.ts +18 -71
- package/q5.js +166 -101
- package/q5.min.js +1 -1
package/deno.json
CHANGED
package/package.json
CHANGED
package/q5.d.ts
CHANGED
|
@@ -458,10 +458,6 @@ createCanvas(200);
|
|
|
458
458
|
createCanvas(200, 100);
|
|
459
459
|
circle(100, 50, 80);
|
|
460
460
|
* @example
|
|
461
|
-
await Q5.WebGPU();
|
|
462
|
-
createCanvas(200, 100);
|
|
463
|
-
circle(0, 0, 80);
|
|
464
|
-
* @example
|
|
465
461
|
createCanvas(200, 200, { alpha: true });
|
|
466
462
|
|
|
467
463
|
function draw() {
|
|
@@ -971,15 +967,15 @@ function draw() {
|
|
|
971
967
|
c.g = (c.g + 1) % 256;
|
|
972
968
|
}
|
|
973
969
|
* @example
|
|
974
|
-
|
|
970
|
+
createCanvas(200);
|
|
975
971
|
|
|
976
|
-
// (r,
|
|
977
|
-
let c = color(0,
|
|
972
|
+
// (r, g, b, a)
|
|
973
|
+
let c = color(0, 255, 255, 50);
|
|
978
974
|
|
|
979
|
-
|
|
975
|
+
function draw() {
|
|
980
976
|
fill(c);
|
|
981
977
|
circle(mouseX, mouseY, 50);
|
|
982
|
-
}
|
|
978
|
+
}
|
|
983
979
|
*/
|
|
984
980
|
function color(c0: string | number | Color | number[], c1?: number, c2?: number, c3?: number): Color;
|
|
985
981
|
|
|
@@ -1274,12 +1270,10 @@ circle(25, 12.5, 16);
|
|
|
1274
1270
|
createCanvas(200, 100);
|
|
1275
1271
|
background('crimson');
|
|
1276
1272
|
* @example
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
q.draw = () => {
|
|
1280
|
-
background(0.5, 0.4);
|
|
1273
|
+
function draw() {
|
|
1274
|
+
background(128, 110);
|
|
1281
1275
|
circle(mouseX, mouseY, 20);
|
|
1282
|
-
}
|
|
1276
|
+
}
|
|
1283
1277
|
*/
|
|
1284
1278
|
function background(filler: Color | Q5.Image): void;
|
|
1285
1279
|
|
|
@@ -1395,12 +1389,10 @@ background(200);
|
|
|
1395
1389
|
strokeWeight(5);
|
|
1396
1390
|
capsule(40, 40, 160, 60, 10);
|
|
1397
1391
|
* @example
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
q.draw = () => {
|
|
1401
|
-
background(0.8);
|
|
1392
|
+
function draw() {
|
|
1393
|
+
background(200);
|
|
1402
1394
|
strokeWeight(10);
|
|
1403
|
-
capsule(
|
|
1395
|
+
capsule(100, 100, mouseX, mouseY, 20);
|
|
1404
1396
|
}
|
|
1405
1397
|
*/
|
|
1406
1398
|
function capsule(x1: number, y1: number, x2: number, y2: number, r: number): void;
|
|
@@ -1553,15 +1545,6 @@ ellipse(50, 25, 150, 75);
|
|
|
1553
1545
|
*
|
|
1554
1546
|
* Only takes effect in q5 WebGPU.
|
|
1555
1547
|
* @param {number} val curve detail level, default is 20
|
|
1556
|
-
* @example
|
|
1557
|
-
await Q5.WebGPU();
|
|
1558
|
-
|
|
1559
|
-
curveDetail(4);
|
|
1560
|
-
|
|
1561
|
-
strokeWeight(10);
|
|
1562
|
-
stroke(0, 1, 1);
|
|
1563
|
-
noFill();
|
|
1564
|
-
curve(-100, -200, -50, 0, 50, 0, 100, -200);
|
|
1565
1548
|
*/
|
|
1566
1549
|
function curveDetail(val: number): void;
|
|
1567
1550
|
|
|
@@ -1719,15 +1702,6 @@ let logo = loadImage('/q5js_logo.avif');
|
|
|
1719
1702
|
function draw() {
|
|
1720
1703
|
background(logo);
|
|
1721
1704
|
}
|
|
1722
|
-
* @example
|
|
1723
|
-
let q = await Q5.WebGPU();
|
|
1724
|
-
createCanvas(200);
|
|
1725
|
-
|
|
1726
|
-
let logo = loadImage('/q5js_logo.avif');
|
|
1727
|
-
|
|
1728
|
-
q.draw = () => {
|
|
1729
|
-
background(logo);
|
|
1730
|
-
};
|
|
1731
1705
|
*/
|
|
1732
1706
|
function loadImage(url: string): Q5.Image | Promise<Q5.Image>;
|
|
1733
1707
|
|
|
@@ -2212,13 +2186,12 @@ textFont('serif');
|
|
|
2212
2186
|
textSize(32);
|
|
2213
2187
|
text('Hello, world!', 15, 90);
|
|
2214
2188
|
* @example
|
|
2215
|
-
let q = await Q5.WebGPU();
|
|
2216
2189
|
createCanvas(200);
|
|
2217
|
-
background(
|
|
2190
|
+
background(200);
|
|
2218
2191
|
|
|
2219
2192
|
textFont('monospace');
|
|
2220
2193
|
|
|
2221
|
-
|
|
2194
|
+
function setup() {
|
|
2222
2195
|
text('Hello, world!', -65, 0);
|
|
2223
2196
|
}
|
|
2224
2197
|
*/
|
|
@@ -2385,23 +2358,21 @@ function draw() {
|
|
|
2385
2358
|
* @param {number} x x-coordinate where the image should be placed
|
|
2386
2359
|
* @param {number} y y-coordinate where the image should be placed
|
|
2387
2360
|
* @example
|
|
2388
|
-
let q = await Q5.WebGPU();
|
|
2389
2361
|
createCanvas(200);
|
|
2390
|
-
background(
|
|
2362
|
+
background(200);
|
|
2391
2363
|
textSize(96);
|
|
2392
2364
|
textAlign(CENTER, CENTER);
|
|
2393
2365
|
|
|
2394
|
-
textImage('🐶',
|
|
2366
|
+
textImage('🐶', 100,100);
|
|
2395
2367
|
* @example
|
|
2396
|
-
let q = await Q5.WebGPU();
|
|
2397
2368
|
createCanvas(200);
|
|
2398
2369
|
|
|
2399
2370
|
loadFont('/assets/Robotica.ttf');
|
|
2400
2371
|
|
|
2401
|
-
|
|
2402
|
-
background(
|
|
2372
|
+
function setup() {
|
|
2373
|
+
background(200);
|
|
2403
2374
|
textSize(66);
|
|
2404
|
-
textImage('Hello!',
|
|
2375
|
+
textImage('Hello!', 0, 0);
|
|
2405
2376
|
};
|
|
2406
2377
|
*/
|
|
2407
2378
|
function textImage(img: Q5.Image | String, x: number, y: number): void;
|
|
@@ -2896,19 +2867,6 @@ function draw() {
|
|
|
2896
2867
|
circle(x, 100, n * 40);
|
|
2897
2868
|
}
|
|
2898
2869
|
}
|
|
2899
|
-
* @example
|
|
2900
|
-
let q = await Q5.WebGPU();
|
|
2901
|
-
|
|
2902
|
-
q.draw = () => {
|
|
2903
|
-
noStroke();
|
|
2904
|
-
let t = millis() * 0.002;
|
|
2905
|
-
for (let x = -100; x < 100; x += 5) {
|
|
2906
|
-
for (let y = -100; y < 100; y += 5) {
|
|
2907
|
-
fill(noise(t, (mouseX + x) * .05, y * .05));
|
|
2908
|
-
square(x, y, 5);
|
|
2909
|
-
}
|
|
2910
|
-
}
|
|
2911
|
-
};
|
|
2912
2870
|
*/
|
|
2913
2871
|
function noise(x?: number, y?: number, z?: number): number;
|
|
2914
2872
|
|
|
@@ -3764,17 +3722,6 @@ await load('/assets/Robotica.ttf');
|
|
|
3764
3722
|
background(255);
|
|
3765
3723
|
textSize(24);
|
|
3766
3724
|
text('Hello, world!', 16, 100);
|
|
3767
|
-
* @example
|
|
3768
|
-
let q = await Q5.WebGPU();
|
|
3769
|
-
createCanvas(200);
|
|
3770
|
-
|
|
3771
|
-
let [jump, retro] = await load(
|
|
3772
|
-
'/assets/jump.wav', '/assets/retro.flac'
|
|
3773
|
-
);
|
|
3774
|
-
|
|
3775
|
-
q.mousePressed = () => {
|
|
3776
|
-
mouseButton == 'left' ? jump.play() : retro.play();
|
|
3777
|
-
};
|
|
3778
3725
|
*/
|
|
3779
3726
|
function load(...urls: string[]): Promise<any[]>;
|
|
3780
3727
|
|
package/q5.js
CHANGED
|
@@ -3104,8 +3104,8 @@ Q5.modules.dom = ($, q) => {
|
|
|
3104
3104
|
el.type = 'range';
|
|
3105
3105
|
el.min = min;
|
|
3106
3106
|
el.max = max;
|
|
3107
|
-
el.value = value;
|
|
3108
3107
|
el.step = step;
|
|
3108
|
+
el.value = value;
|
|
3109
3109
|
el.val = () => parseFloat(el.value);
|
|
3110
3110
|
return el;
|
|
3111
3111
|
};
|
|
@@ -3204,6 +3204,7 @@ Q5.modules.fes = ($) => {
|
|
|
3204
3204
|
|
|
3205
3205
|
let errFile = stackLines[idx].split(sep).at(-1);
|
|
3206
3206
|
if (errFile.startsWith('blob:')) errFile = errFile.slice(5);
|
|
3207
|
+
errFile = errFile.split(')')[0];
|
|
3207
3208
|
let parts = errFile.split(':');
|
|
3208
3209
|
let lineNum = parseInt(parts.at(-2));
|
|
3209
3210
|
parts[parts.length - 1] = parts.at(-1).split(')')[0];
|
|
@@ -3256,24 +3257,24 @@ Q5.modules.fes = ($) => {
|
|
|
3256
3257
|
});
|
|
3257
3258
|
}
|
|
3258
3259
|
}
|
|
3259
|
-
};
|
|
3260
3260
|
|
|
3261
|
-
if (typeof navigator != undefined && navigator.onLine) {
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3261
|
+
if (Q5.online != false && typeof navigator != undefined && navigator.onLine) {
|
|
3262
|
+
async function checkLatestVersion() {
|
|
3263
|
+
try {
|
|
3264
|
+
let response = await fetch('https://data.jsdelivr.com/v1/package/npm/q5');
|
|
3265
|
+
if (!response.ok) return;
|
|
3266
|
+
let data = await response.json();
|
|
3267
|
+
let l = data.tags.latest;
|
|
3268
|
+
l = l.slice(0, l.lastIndexOf('.'));
|
|
3269
|
+
if (l != Q5.version) {
|
|
3270
|
+
console.warn(`q5.js v${l} is now available! Consider updating from v${Q5.version}.`);
|
|
3271
|
+
}
|
|
3272
|
+
} catch (e) {}
|
|
3273
|
+
}
|
|
3274
3274
|
|
|
3275
|
-
|
|
3276
|
-
}
|
|
3275
|
+
checkLatestVersion();
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3277
3278
|
Q5.modules.input = ($, q) => {
|
|
3278
3279
|
if ($._isGraphics) return;
|
|
3279
3280
|
|
|
@@ -4581,7 +4582,7 @@ Q5.modules.util = ($, q) => {
|
|
|
4581
4582
|
return res.text();
|
|
4582
4583
|
})
|
|
4583
4584
|
.then((f) => {
|
|
4584
|
-
if (type == 'csv') f =
|
|
4585
|
+
if (type == 'csv') f = Q5.CSV.parse(f);
|
|
4585
4586
|
if (typeof f == 'string') ret.text = f;
|
|
4586
4587
|
else Object.assign(ret, f);
|
|
4587
4588
|
delete ret.promise;
|
|
@@ -4687,21 +4688,6 @@ Q5.modules.util = ($, q) => {
|
|
|
4687
4688
|
} else saveFile(a);
|
|
4688
4689
|
};
|
|
4689
4690
|
|
|
4690
|
-
$.CSV = {};
|
|
4691
|
-
$.CSV.parse = (csv, sep = ',', lineSep = '\n') => {
|
|
4692
|
-
if (!csv.length) return [];
|
|
4693
|
-
let a = [],
|
|
4694
|
-
lns = csv.split(lineSep),
|
|
4695
|
-
headers = lns[0].split(sep).map((h) => h.replaceAll('"', ''));
|
|
4696
|
-
for (let i = 1; i < lns.length; i++) {
|
|
4697
|
-
let o = {},
|
|
4698
|
-
ln = lns[i].split(sep);
|
|
4699
|
-
headers.forEach((h, i) => (o[h] = JSON.parse(ln[i])));
|
|
4700
|
-
a.push(o);
|
|
4701
|
-
}
|
|
4702
|
-
return a;
|
|
4703
|
-
};
|
|
4704
|
-
|
|
4705
4691
|
if ($.canvas && !Q5._createServerCanvas) {
|
|
4706
4692
|
$.canvas.save = $.saveCanvas = $.save;
|
|
4707
4693
|
}
|
|
@@ -4738,6 +4724,21 @@ Q5.modules.util = ($, q) => {
|
|
|
4738
4724
|
return a;
|
|
4739
4725
|
};
|
|
4740
4726
|
};
|
|
4727
|
+
|
|
4728
|
+
Q5.CSV = {};
|
|
4729
|
+
Q5.CSV.parse = (csv, sep = ',', lineSep = '\n') => {
|
|
4730
|
+
if (!csv.length) return [];
|
|
4731
|
+
let a = [],
|
|
4732
|
+
lns = csv.split(lineSep),
|
|
4733
|
+
headers = lns[0].split(sep).map((h) => h.replaceAll('"', ''));
|
|
4734
|
+
for (let i = 1; i < lns.length; i++) {
|
|
4735
|
+
let o = {},
|
|
4736
|
+
ln = lns[i].split(sep);
|
|
4737
|
+
headers.forEach((h, i) => (o[h] = JSON.parse(ln[i])));
|
|
4738
|
+
a.push(o);
|
|
4739
|
+
}
|
|
4740
|
+
return a;
|
|
4741
|
+
};
|
|
4741
4742
|
Q5.modules.vector = ($) => {
|
|
4742
4743
|
$.Vector = Q5.Vector;
|
|
4743
4744
|
$.createVector = (x, y, z) => new $.Vector(x, y, z, $);
|
|
@@ -5081,6 +5082,8 @@ struct Q5 {
|
|
|
5081
5082
|
frameLayout,
|
|
5082
5083
|
frameSampler,
|
|
5083
5084
|
frameBindGroup,
|
|
5085
|
+
frameBindGroupA,
|
|
5086
|
+
frameBindGroupB,
|
|
5084
5087
|
colorIndex = 2,
|
|
5085
5088
|
colorStackIndex = 12,
|
|
5086
5089
|
prevFramePL = 0,
|
|
@@ -5238,6 +5241,25 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5238
5241
|
|
|
5239
5242
|
// Create a pipeline for rendering frames
|
|
5240
5243
|
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
|
5244
|
+
|
|
5245
|
+
// Create persistent bind groups for both frame buffers
|
|
5246
|
+
frameBindGroupA = Q5.device.createBindGroup({
|
|
5247
|
+
layout: frameLayout,
|
|
5248
|
+
entries: [
|
|
5249
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5250
|
+
{ binding: 1, resource: frameSampler },
|
|
5251
|
+
{ binding: 2, resource: frameA.createView() }
|
|
5252
|
+
]
|
|
5253
|
+
});
|
|
5254
|
+
|
|
5255
|
+
frameBindGroupB = Q5.device.createBindGroup({
|
|
5256
|
+
layout: frameLayout,
|
|
5257
|
+
entries: [
|
|
5258
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5259
|
+
{ binding: 1, resource: frameSampler },
|
|
5260
|
+
{ binding: 2, resource: frameB.createView() }
|
|
5261
|
+
]
|
|
5262
|
+
});
|
|
5241
5263
|
};
|
|
5242
5264
|
|
|
5243
5265
|
$._createCanvas = (w, h, opt) => {
|
|
@@ -5618,6 +5640,9 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5618
5640
|
$.popStyles();
|
|
5619
5641
|
};
|
|
5620
5642
|
|
|
5643
|
+
// Reusable array for calcBox to avoid GC
|
|
5644
|
+
let boxCache = [0, 0, 0, 0];
|
|
5645
|
+
|
|
5621
5646
|
const calcBox = (x, y, w, h, mode) => {
|
|
5622
5647
|
// left, right, top, bottom
|
|
5623
5648
|
let l, r, t, b;
|
|
@@ -5641,7 +5666,11 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5641
5666
|
b = h;
|
|
5642
5667
|
}
|
|
5643
5668
|
|
|
5644
|
-
|
|
5669
|
+
boxCache[0] = l;
|
|
5670
|
+
boxCache[1] = r;
|
|
5671
|
+
boxCache[2] = t;
|
|
5672
|
+
boxCache[3] = b;
|
|
5673
|
+
return boxCache;
|
|
5645
5674
|
};
|
|
5646
5675
|
|
|
5647
5676
|
// prettier-ignore
|
|
@@ -5735,11 +5764,15 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5735
5764
|
};
|
|
5736
5765
|
|
|
5737
5766
|
$._beginRender = () => {
|
|
5738
|
-
// swap the frame textures
|
|
5767
|
+
// swap the frame textures and bind groups
|
|
5739
5768
|
const temp = frameA;
|
|
5740
5769
|
frameA = frameB;
|
|
5741
5770
|
frameB = temp;
|
|
5742
5771
|
|
|
5772
|
+
const tempBindGroup = frameBindGroupA;
|
|
5773
|
+
frameBindGroupA = frameBindGroupB;
|
|
5774
|
+
frameBindGroupB = tempBindGroup;
|
|
5775
|
+
|
|
5743
5776
|
encoder = Q5.device.createCommandEncoder();
|
|
5744
5777
|
|
|
5745
5778
|
$._pass = pass = encoder.beginRenderPass({
|
|
@@ -5755,14 +5788,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5755
5788
|
]
|
|
5756
5789
|
});
|
|
5757
5790
|
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
entries: [
|
|
5761
|
-
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5762
|
-
{ binding: 1, resource: frameSampler },
|
|
5763
|
-
{ binding: 2, resource: frameB.createView() }
|
|
5764
|
-
]
|
|
5765
|
-
});
|
|
5791
|
+
// Use pre-created bind group instead of creating new one
|
|
5792
|
+
frameBindGroup = frameBindGroupB;
|
|
5766
5793
|
|
|
5767
5794
|
if (!shouldClear) {
|
|
5768
5795
|
pass.setPipeline($._pipelines[prevFramePL]);
|
|
@@ -5773,6 +5800,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5773
5800
|
};
|
|
5774
5801
|
|
|
5775
5802
|
let transformsBuffer, colorsBuffer, shapesVertBuff, imgVertBuff, charBuffer, textBuffer;
|
|
5803
|
+
let mainBindGroup, lastTransformsBuffer, lastColorsBuffer;
|
|
5776
5804
|
|
|
5777
5805
|
$._render = () => {
|
|
5778
5806
|
let transformsSize = matrices.length * MATRIX_SIZE * 4; // 4 bytes per float
|
|
@@ -5797,32 +5825,36 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5797
5825
|
|
|
5798
5826
|
Q5.device.queue.writeBuffer(colorsBuffer, 0, colorStack.subarray(0, colorStackIndex));
|
|
5799
5827
|
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5828
|
+
// Reuse uniform array instead of creating new one each frame
|
|
5829
|
+
$._uniforms[0] = $.width;
|
|
5830
|
+
$._uniforms[1] = $.height;
|
|
5831
|
+
$._uniforms[2] = $.halfWidth;
|
|
5832
|
+
$._uniforms[3] = $.halfHeight;
|
|
5833
|
+
$._uniforms[4] = $._pixelDensity;
|
|
5834
|
+
$._uniforms[5] = $.frameCount;
|
|
5835
|
+
$._uniforms[6] = performance.now();
|
|
5836
|
+
$._uniforms[7] = $.deltaTime;
|
|
5837
|
+
$._uniforms[8] = $.mouseX;
|
|
5838
|
+
$._uniforms[9] = $.mouseY;
|
|
5839
|
+
$._uniforms[10] = $.mouseIsPressed ? 1 : 0;
|
|
5840
|
+
$._uniforms[11] = $.keyCode;
|
|
5841
|
+
$._uniforms[12] = $.keyIsPressed ? 1 : 0;
|
|
5842
|
+
|
|
5843
|
+
Q5.device.queue.writeBuffer(uniformBuffer, 0, $._uniforms);
|
|
5844
|
+
|
|
5845
|
+
// Only recreate bind group if buffers changed
|
|
5846
|
+
if (!mainBindGroup || lastTransformsBuffer !== transformsBuffer || lastColorsBuffer !== colorsBuffer) {
|
|
5847
|
+
mainBindGroup = Q5.device.createBindGroup({
|
|
5848
|
+
layout: mainLayout,
|
|
5849
|
+
entries: [
|
|
5850
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5851
|
+
{ binding: 1, resource: { buffer: transformsBuffer } },
|
|
5852
|
+
{ binding: 2, resource: { buffer: colorsBuffer } }
|
|
5853
|
+
]
|
|
5854
|
+
});
|
|
5855
|
+
lastTransformsBuffer = transformsBuffer;
|
|
5856
|
+
lastColorsBuffer = colorsBuffer;
|
|
5857
|
+
}
|
|
5826
5858
|
|
|
5827
5859
|
pass.setBindGroup(0, mainBindGroup);
|
|
5828
5860
|
|
|
@@ -5870,11 +5902,13 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5870
5902
|
// prepare to render text
|
|
5871
5903
|
|
|
5872
5904
|
if (charStack.length) {
|
|
5873
|
-
//
|
|
5874
|
-
let
|
|
5905
|
+
// Flatten char data into reusable buffer instead of creating new array
|
|
5906
|
+
let charOffset = 0;
|
|
5875
5907
|
for (let charsData of charStack) {
|
|
5876
|
-
|
|
5908
|
+
charDataBuffer.set(charsData, charOffset);
|
|
5909
|
+
charOffset += charsData.length;
|
|
5877
5910
|
}
|
|
5911
|
+
let totalTextSize = charOffset * 4;
|
|
5878
5912
|
|
|
5879
5913
|
if (!charBuffer || charBuffer.size < totalTextSize) {
|
|
5880
5914
|
if (charBuffer) charBuffer.destroy();
|
|
@@ -5884,10 +5918,16 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5884
5918
|
});
|
|
5885
5919
|
}
|
|
5886
5920
|
|
|
5887
|
-
Q5.device.queue.writeBuffer(charBuffer, 0,
|
|
5921
|
+
Q5.device.queue.writeBuffer(charBuffer, 0, charDataBuffer.buffer, 0, totalTextSize);
|
|
5922
|
+
|
|
5923
|
+
// Flatten text metadata into reusable buffer
|
|
5924
|
+
let textOffset = 0;
|
|
5925
|
+
for (let textData of textStack) {
|
|
5926
|
+
textDataBuffer.set(textData, textOffset);
|
|
5927
|
+
textOffset += textData.length;
|
|
5928
|
+
}
|
|
5929
|
+
let totalMetadataSize = textOffset * 4;
|
|
5888
5930
|
|
|
5889
|
-
// Calculate total buffer size for metadata
|
|
5890
|
-
let totalMetadataSize = textStack.length * 8 * 4;
|
|
5891
5931
|
if (!textBuffer || textBuffer.size < totalMetadataSize) {
|
|
5892
5932
|
if (textBuffer) textBuffer.destroy();
|
|
5893
5933
|
textBuffer = Q5.device.createBuffer({
|
|
@@ -5897,7 +5937,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5897
5937
|
});
|
|
5898
5938
|
}
|
|
5899
5939
|
|
|
5900
|
-
Q5.device.queue.writeBuffer(textBuffer, 0,
|
|
5940
|
+
Q5.device.queue.writeBuffer(textBuffer, 0, textDataBuffer.buffer, 0, totalMetadataSize);
|
|
5901
5941
|
|
|
5902
5942
|
// create a single bind group for the text buffer and metadata buffer
|
|
5903
5943
|
$._textBindGroup = Q5.device.createBindGroup({
|
|
@@ -6015,14 +6055,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6015
6055
|
]
|
|
6016
6056
|
});
|
|
6017
6057
|
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
entries: [
|
|
6021
|
-
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
6022
|
-
{ binding: 1, resource: frameSampler },
|
|
6023
|
-
{ binding: 2, resource: frameA.createView() }
|
|
6024
|
-
]
|
|
6025
|
-
});
|
|
6058
|
+
// Use pre-created bind group instead of creating new one
|
|
6059
|
+
frameBindGroup = frameBindGroupA;
|
|
6026
6060
|
|
|
6027
6061
|
pass.setPipeline($._pipelines[framePL]);
|
|
6028
6062
|
pass.setBindGroup(0, frameBindGroup);
|
|
@@ -6034,11 +6068,11 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6034
6068
|
$._pass = pass = encoder = null;
|
|
6035
6069
|
|
|
6036
6070
|
// clear the stacks for the next frame
|
|
6037
|
-
drawStack.
|
|
6071
|
+
drawStack.length = 0; // faster than splice for clearing
|
|
6038
6072
|
colorIndex = 2;
|
|
6039
6073
|
colorStackIndex = 12;
|
|
6040
|
-
matrices =
|
|
6041
|
-
matricesIdxStack =
|
|
6074
|
+
matrices.length = 1; // keep first matrix, clear rest
|
|
6075
|
+
matricesIdxStack.length = 0;
|
|
6042
6076
|
|
|
6043
6077
|
// frameA can now be saved when saveCanvas is run
|
|
6044
6078
|
$._texture = frameA;
|
|
@@ -6046,13 +6080,15 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6046
6080
|
// reset
|
|
6047
6081
|
shapesVertIdx = 0;
|
|
6048
6082
|
imgVertIdx = 0;
|
|
6049
|
-
|
|
6083
|
+
// Remove video frames without creating new array
|
|
6084
|
+
if (vidFrames > 0) {
|
|
6085
|
+
$._textureBindGroups.length = tIdx;
|
|
6086
|
+
}
|
|
6050
6087
|
vidFrames = 0;
|
|
6051
|
-
charStack =
|
|
6052
|
-
textStack =
|
|
6053
|
-
|
|
6088
|
+
charStack.length = 0;
|
|
6089
|
+
textStack.length = 0;
|
|
6090
|
+
// Don't create new typed arrays - just reset index
|
|
6054
6091
|
rectStackIdx = 0;
|
|
6055
|
-
ellipseStack = new Float32Array(Q5.MAX_ELLIPSES * 16);
|
|
6056
6092
|
ellipseStackIdx = 0;
|
|
6057
6093
|
|
|
6058
6094
|
// destroy buffers
|
|
@@ -6624,6 +6660,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6624
6660
|
$.rectMode = (x) => (_rectMode = x);
|
|
6625
6661
|
$._getRectMode = () => _rectMode;
|
|
6626
6662
|
|
|
6663
|
+
// Reusable array for rect mode calculations
|
|
6664
|
+
let rectModeCache = [0, 0, 0, 0];
|
|
6665
|
+
|
|
6627
6666
|
function applyRectMode(x, y, w, h) {
|
|
6628
6667
|
let hw = w / 2,
|
|
6629
6668
|
hh = h / 2;
|
|
@@ -6641,7 +6680,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6641
6680
|
y += hh;
|
|
6642
6681
|
}
|
|
6643
6682
|
}
|
|
6644
|
-
|
|
6683
|
+
rectModeCache[0] = x;
|
|
6684
|
+
rectModeCache[1] = y;
|
|
6685
|
+
rectModeCache[2] = hw;
|
|
6686
|
+
rectModeCache[3] = hh;
|
|
6687
|
+
return rectModeCache;
|
|
6645
6688
|
}
|
|
6646
6689
|
|
|
6647
6690
|
$.rect = (x, y, w, h, rr = 0) => {
|
|
@@ -6919,6 +6962,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6919
6962
|
$.ellipseMode = (x) => (_ellipseMode = x);
|
|
6920
6963
|
$._getEllipseMode = () => _ellipseMode;
|
|
6921
6964
|
|
|
6965
|
+
// Reusable array for ellipse mode calculations
|
|
6966
|
+
let ellipseModeCache = [0, 0, 0, 0];
|
|
6967
|
+
|
|
6922
6968
|
function applyEllipseMode(x, y, w, h) {
|
|
6923
6969
|
h ??= w;
|
|
6924
6970
|
let a, b;
|
|
@@ -6939,7 +6985,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6939
6985
|
a = (w - x) / 2;
|
|
6940
6986
|
b = (h - y) / 2;
|
|
6941
6987
|
}
|
|
6942
|
-
|
|
6988
|
+
ellipseModeCache[0] = x;
|
|
6989
|
+
ellipseModeCache[1] = y;
|
|
6990
|
+
ellipseModeCache[2] = a;
|
|
6991
|
+
ellipseModeCache[3] = b;
|
|
6992
|
+
return ellipseModeCache;
|
|
6943
6993
|
}
|
|
6944
6994
|
|
|
6945
6995
|
$.ellipse = (x, y, w, h) => {
|
|
@@ -7336,6 +7386,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7336
7386
|
$.imageMode = (x) => (_imageMode = x);
|
|
7337
7387
|
$._getImageMode = () => _imageMode;
|
|
7338
7388
|
|
|
7389
|
+
// Reusable uniform buffer array to avoid GC
|
|
7390
|
+
$._uniforms = new Float32Array(13);
|
|
7391
|
+
|
|
7339
7392
|
const addImgVert = (x, y, u, v, ci, ti, ia) => {
|
|
7340
7393
|
let s = imgVertStack,
|
|
7341
7394
|
i = imgVertIdx;
|
|
@@ -7350,6 +7403,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7350
7403
|
};
|
|
7351
7404
|
|
|
7352
7405
|
$.image = (img, dx = 0, dy = 0, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
7406
|
+
if (!img) return;
|
|
7353
7407
|
let isVideo;
|
|
7354
7408
|
if (img._texture == undefined) {
|
|
7355
7409
|
isVideo = img.tagName == 'VIDEO';
|
|
@@ -7679,8 +7733,8 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7679
7733
|
// chars and kernings can be stored as csv strings, making the file
|
|
7680
7734
|
// size smaller, but they need to be parsed into arrays of objects
|
|
7681
7735
|
if (typeof atlas.chars == 'string') {
|
|
7682
|
-
atlas.chars =
|
|
7683
|
-
atlas.kernings =
|
|
7736
|
+
atlas.chars = Q5.CSV.parse(atlas.chars, ' ');
|
|
7737
|
+
atlas.kernings = Q5.CSV.parse(atlas.kernings, ' ');
|
|
7684
7738
|
}
|
|
7685
7739
|
|
|
7686
7740
|
let charCount = atlas.chars.length;
|
|
@@ -7764,7 +7818,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7764
7818
|
$._loadDefaultFont = (fontName, cb) => {
|
|
7765
7819
|
fonts[fontName] = null;
|
|
7766
7820
|
let url = `https://q5js.org/fonts/${fontName}-msdf.json`;
|
|
7767
|
-
if (!navigator.onLine) {
|
|
7821
|
+
if (Q5.online == false || !navigator.onLine) {
|
|
7768
7822
|
url = `/node_modules/q5/builtinFonts/${fontName}-msdf.json`;
|
|
7769
7823
|
}
|
|
7770
7824
|
return $.loadFont(url, cb);
|
|
@@ -7837,13 +7891,20 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7837
7891
|
let charStack = [],
|
|
7838
7892
|
textStack = [];
|
|
7839
7893
|
|
|
7894
|
+
// Reusable array for line widths to avoid GC
|
|
7895
|
+
let lineWidthsCache = new Array(100);
|
|
7896
|
+
|
|
7897
|
+
// Reusable buffers for text data to avoid creating new arrays
|
|
7898
|
+
let charDataBuffer = new Float32Array(100000); // reusable buffer for char data
|
|
7899
|
+
let textDataBuffer = new Float32Array(10000); // reusable buffer for text metadata
|
|
7900
|
+
|
|
7840
7901
|
let measureText = (font, text, charCallback) => {
|
|
7841
7902
|
let maxWidth = 0,
|
|
7842
7903
|
offsetX = 0,
|
|
7843
7904
|
offsetY = 0,
|
|
7844
7905
|
line = 0,
|
|
7845
7906
|
printedCharCount = 0,
|
|
7846
|
-
lineWidths =
|
|
7907
|
+
lineWidths = lineWidthsCache, // reuse array
|
|
7847
7908
|
nextCharCode = text.charCodeAt(0);
|
|
7848
7909
|
|
|
7849
7910
|
for (let i = 0; i < text.length; ++i) {
|
|
@@ -7874,12 +7935,14 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7874
7935
|
printedCharCount++;
|
|
7875
7936
|
}
|
|
7876
7937
|
}
|
|
7877
|
-
lineWidths
|
|
7938
|
+
lineWidths[line] = offsetX;
|
|
7878
7939
|
maxWidth = Math.max(maxWidth, offsetX);
|
|
7940
|
+
let lineCount = line + 1;
|
|
7879
7941
|
return {
|
|
7880
7942
|
width: maxWidth,
|
|
7881
|
-
height:
|
|
7943
|
+
height: lineCount * font.lineHeight * leadPercent,
|
|
7882
7944
|
lineWidths,
|
|
7945
|
+
lineCount,
|
|
7883
7946
|
printedCharCount
|
|
7884
7947
|
};
|
|
7885
7948
|
};
|
|
@@ -7888,9 +7951,11 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7888
7951
|
if (!$._font) {
|
|
7889
7952
|
// if the default font hasn't been loaded yet, try to load it
|
|
7890
7953
|
if ($._font !== null) $.textFont('sans-serif');
|
|
7891
|
-
return;
|
|
7954
|
+
if (_textSize >= 1) return $.textImage(str, x, y, w, h);
|
|
7892
7955
|
}
|
|
7893
7956
|
|
|
7957
|
+
if (_textSize < 1) return;
|
|
7958
|
+
|
|
7894
7959
|
let type = typeof str;
|
|
7895
7960
|
if (type != 'string') {
|
|
7896
7961
|
if (type == 'object') str = str.toString();
|