q5 3.5.1 → 3.6.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.
- package/.prettierignore +1 -0
- package/README.md +1 -1
- package/defs/q5-c2d.d.ts +3950 -0
- package/deno.json +1 -1
- package/package.json +2 -1
- package/q5.d.ts +2649 -3027
- package/q5.js +334 -201
- package/q5.min.js +2 -2
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 3.
|
|
3
|
+
* @version 3.6
|
|
4
4
|
* @author quinton-ashley
|
|
5
5
|
* @contributors evanalulu, Tezumie, ormaq, Dukemz, LingDong-
|
|
6
6
|
* @license LGPL-3.0
|
|
@@ -79,15 +79,19 @@ function Q5(scope, parent, renderer) {
|
|
|
79
79
|
$.deviceOrientation = window.screen?.orientation?.type;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
$.
|
|
83
|
-
$.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
$._loaders = [];
|
|
83
|
+
$.loadAll = () => {
|
|
84
|
+
let loaders = $._loaders;
|
|
85
|
+
if ($._g) loaders = loaders.concat($._g._loaders);
|
|
86
|
+
return Promise.all(loaders);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
$.isPreloadSupported = () => true;
|
|
90
|
+
$.disablePreload = () => ($._disablePreload = true);
|
|
87
91
|
|
|
88
92
|
const resolvers = [];
|
|
89
93
|
$._incrementPreload = () => {
|
|
90
|
-
$.
|
|
94
|
+
$._loaders.push(new Promise((resolve) => resolvers.push(resolve)));
|
|
91
95
|
};
|
|
92
96
|
$._decrementPreload = () => {
|
|
93
97
|
if (resolvers.length) resolvers.pop()();
|
|
@@ -289,7 +293,7 @@ function Q5(scope, parent, renderer) {
|
|
|
289
293
|
for (let name of userFns) $[name] ??= () => {};
|
|
290
294
|
|
|
291
295
|
if ($._isGlobal) {
|
|
292
|
-
for (let name of ['setup', 'update', 'draw', ...userFns]) {
|
|
296
|
+
for (let name of ['setup', 'update', 'draw', 'drawFrame', ...userFns]) {
|
|
293
297
|
if (Q5[name]) $[name] = Q5[name];
|
|
294
298
|
else {
|
|
295
299
|
Object.defineProperty(Q5, name, {
|
|
@@ -341,16 +345,13 @@ function Q5(scope, parent, renderer) {
|
|
|
341
345
|
new Promise((resolve) => {
|
|
342
346
|
setTimeout(() => {
|
|
343
347
|
// if not loading
|
|
344
|
-
if (!$.
|
|
348
|
+
if (!$._loaders.length) resolve();
|
|
345
349
|
}, 500);
|
|
346
350
|
})
|
|
347
351
|
]);
|
|
348
352
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (t.setup?.constructor.name == 'AsyncFunction') {
|
|
353
|
-
$.usePromiseLoading();
|
|
353
|
+
if (!$._disablePreload) {
|
|
354
|
+
await $.loadAll();
|
|
354
355
|
}
|
|
355
356
|
|
|
356
357
|
$.setup ??= t.setup || (() => {});
|
|
@@ -374,7 +375,7 @@ function Q5(scope, parent, renderer) {
|
|
|
374
375
|
|
|
375
376
|
Q5.instances.push($);
|
|
376
377
|
|
|
377
|
-
if (autoLoaded) start();
|
|
378
|
+
if (autoLoaded || Q5._esm) start();
|
|
378
379
|
else setTimeout(start, 32);
|
|
379
380
|
}
|
|
380
381
|
|
|
@@ -400,14 +401,14 @@ Q5.hooks = {
|
|
|
400
401
|
remove: []
|
|
401
402
|
};
|
|
402
403
|
|
|
403
|
-
Q5.addHook = (
|
|
404
|
+
Q5.addHook = (lifecycle, fn) => Q5.hooks[lifecycle].push(fn);
|
|
404
405
|
|
|
405
406
|
// p5 v2 compat
|
|
406
407
|
Q5.registerAddon = (addon) => {
|
|
407
408
|
let lifecycles = {};
|
|
408
409
|
addon(Q5, Q5.prototype, lifecycles);
|
|
409
|
-
for (let
|
|
410
|
-
Q5.hooks[
|
|
410
|
+
for (let l in lifecycles) {
|
|
411
|
+
Q5.hooks[l].push(lifecycles[l]);
|
|
411
412
|
}
|
|
412
413
|
};
|
|
413
414
|
|
|
@@ -437,16 +438,16 @@ function createCanvas(w, h, opt) {
|
|
|
437
438
|
}
|
|
438
439
|
}
|
|
439
440
|
|
|
440
|
-
if (Q5._server) global.p5 ??= global.Q5 = Q5;
|
|
441
|
+
if (Q5._server) global.p5 ??= global.q5 = global.Q5 = Q5;
|
|
441
442
|
|
|
442
443
|
if (typeof window == 'object') {
|
|
443
|
-
window.p5 ??= window.Q5 = Q5;
|
|
444
|
+
window.p5 ??= window.q5 = window.Q5 = Q5;
|
|
444
445
|
window.createCanvas = createCanvas;
|
|
445
446
|
window.C2D = 'c2d';
|
|
446
447
|
window.WEBGPU = 'webgpu';
|
|
447
448
|
} else global.window = 0;
|
|
448
449
|
|
|
449
|
-
Q5.version = Q5.VERSION = '3.
|
|
450
|
+
Q5.version = Q5.VERSION = '3.6';
|
|
450
451
|
|
|
451
452
|
if (typeof document == 'object') {
|
|
452
453
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -1388,11 +1389,10 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1388
1389
|
let imgData = null,
|
|
1389
1390
|
pixels = null;
|
|
1390
1391
|
|
|
1391
|
-
$.createImage = (w, h, opt) => {
|
|
1392
|
-
opt ??=
|
|
1393
|
-
opt.
|
|
1394
|
-
|
|
1395
|
-
return new Q5.Image($, w, h, opt);
|
|
1392
|
+
$.createImage = (w, h, opt = {}) => {
|
|
1393
|
+
opt.colorSpace ??= $.canvas.colorSpace;
|
|
1394
|
+
opt.defaultImageScale ??= $._defaultImageScale;
|
|
1395
|
+
return new Q5.Image(w, h, opt);
|
|
1396
1396
|
};
|
|
1397
1397
|
|
|
1398
1398
|
$.loadImage = function (url, cb, opt) {
|
|
@@ -1406,7 +1406,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1406
1406
|
if (typeof last == 'object') {
|
|
1407
1407
|
opt = last;
|
|
1408
1408
|
cb = null;
|
|
1409
|
-
} else opt =
|
|
1409
|
+
} else opt = undefined;
|
|
1410
1410
|
|
|
1411
1411
|
let g = $.createImage(1, 1, opt);
|
|
1412
1412
|
let pd = g._pixelDensity;
|
|
@@ -1416,6 +1416,10 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1416
1416
|
|
|
1417
1417
|
g.promise = new Promise((resolve, reject) => {
|
|
1418
1418
|
img.onload = () => {
|
|
1419
|
+
delete g.promise;
|
|
1420
|
+
delete g.then;
|
|
1421
|
+
if (g._usedAwait) g = $.createImage(1, 1, opt);
|
|
1422
|
+
|
|
1419
1423
|
img._pixelDensity = pd;
|
|
1420
1424
|
g.defaultWidth = img.width * $._defaultImageScale;
|
|
1421
1425
|
g.defaultHeight = img.height * $._defaultImageScale;
|
|
@@ -1425,16 +1429,19 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1425
1429
|
|
|
1426
1430
|
g.ctx.drawImage(img, 0, 0);
|
|
1427
1431
|
if (cb) cb(g);
|
|
1428
|
-
delete g.promise;
|
|
1429
1432
|
resolve(g);
|
|
1430
1433
|
};
|
|
1431
1434
|
img.onerror = reject;
|
|
1432
1435
|
});
|
|
1433
|
-
$.
|
|
1436
|
+
$._loaders.push(g.promise);
|
|
1434
1437
|
|
|
1435
|
-
|
|
1438
|
+
// then only runs when the user awaits the instance
|
|
1439
|
+
g.then = (resolve, reject) => {
|
|
1440
|
+
g._usedAwait = true;
|
|
1441
|
+
return g.promise.then(resolve, reject);
|
|
1442
|
+
};
|
|
1436
1443
|
|
|
1437
|
-
|
|
1444
|
+
g.src = img.src = url;
|
|
1438
1445
|
return g;
|
|
1439
1446
|
};
|
|
1440
1447
|
|
|
@@ -1695,7 +1702,9 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1695
1702
|
};
|
|
1696
1703
|
|
|
1697
1704
|
Q5.Image = class {
|
|
1698
|
-
constructor(
|
|
1705
|
+
constructor(w, h, opt = {}) {
|
|
1706
|
+
opt.alpha ??= true;
|
|
1707
|
+
opt.colorSpace ??= Q5.canvasOptions.colorSpace;
|
|
1699
1708
|
let $ = this;
|
|
1700
1709
|
$._isImage = true;
|
|
1701
1710
|
$.canvas = $.ctx = $.drawingContext = null;
|
|
@@ -1706,7 +1715,7 @@ Q5.Image = class {
|
|
|
1706
1715
|
if (r[m]) r[m]($, $);
|
|
1707
1716
|
}
|
|
1708
1717
|
$._pixelDensity = opt.pixelDensity || 1;
|
|
1709
|
-
$._defaultImageScale = opt.defaultImageScale ||
|
|
1718
|
+
$._defaultImageScale = opt.defaultImageScale || 2;
|
|
1710
1719
|
$.createCanvas(w, h, opt);
|
|
1711
1720
|
let scale = $._pixelDensity * $._defaultImageScale;
|
|
1712
1721
|
$.defaultWidth = w * scale;
|
|
@@ -1878,13 +1887,11 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1878
1887
|
emphasis = 'normal',
|
|
1879
1888
|
weight = 'normal',
|
|
1880
1889
|
styleHash = 0,
|
|
1881
|
-
styleHashes = [],
|
|
1882
1890
|
genTextImage = false,
|
|
1883
1891
|
cacheSize = 0;
|
|
1884
1892
|
$._fontMod = false;
|
|
1885
1893
|
|
|
1886
1894
|
let cache = ($._textCache = {});
|
|
1887
|
-
$._textCacheMaxSize = 12000;
|
|
1888
1895
|
|
|
1889
1896
|
$.loadFont = (url, cb) => {
|
|
1890
1897
|
let f;
|
|
@@ -1894,25 +1901,29 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1894
1901
|
} else {
|
|
1895
1902
|
let name = url.split('/').pop().split('.')[0].replace(' ', '');
|
|
1896
1903
|
|
|
1897
|
-
f =
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1904
|
+
f = { family: name };
|
|
1905
|
+
let ff = new FontFace(name, `url(${encodeURI(url)})`);
|
|
1906
|
+
document.fonts.add(ff);
|
|
1907
|
+
|
|
1908
|
+
f.promise = new Promise((resolve, reject) => {
|
|
1909
|
+
ff.load()
|
|
1910
|
+
.then(() => {
|
|
1911
|
+
delete f.promise;
|
|
1912
|
+
delete f.then;
|
|
1913
|
+
if (cb) cb(ff);
|
|
1914
|
+
resolve(ff);
|
|
1915
|
+
})
|
|
1916
|
+
.catch((err) => {
|
|
1917
|
+
reject(err);
|
|
1918
|
+
});
|
|
1919
|
+
});
|
|
1911
1920
|
}
|
|
1912
|
-
|
|
1913
|
-
$._preloadPromises.push(f.promise);
|
|
1921
|
+
$._loaders.push(f.promise);
|
|
1914
1922
|
$.textFont(f.family);
|
|
1915
|
-
|
|
1923
|
+
f.then = (resolve, reject) => {
|
|
1924
|
+
f._usedAwait = true;
|
|
1925
|
+
return f.promise.then(resolve, reject);
|
|
1926
|
+
};
|
|
1916
1927
|
return f;
|
|
1917
1928
|
};
|
|
1918
1929
|
|
|
@@ -1980,8 +1991,13 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1980
1991
|
}
|
|
1981
1992
|
}
|
|
1982
1993
|
|
|
1994
|
+
if (f._usedAwait) {
|
|
1995
|
+
f = { family: fontFamily };
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1983
1998
|
f.faces = loadedFaces;
|
|
1984
1999
|
delete f.promise;
|
|
2000
|
+
delete f.then;
|
|
1985
2001
|
if (cb) cb(f);
|
|
1986
2002
|
return f;
|
|
1987
2003
|
} catch (e) {
|
|
@@ -2086,7 +2102,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2086
2102
|
if (str === undefined || (!$._doFill && !$._doStroke)) return;
|
|
2087
2103
|
str = str.toString();
|
|
2088
2104
|
let ctx = $.ctx;
|
|
2089
|
-
let img, colorStyle;
|
|
2105
|
+
let img, colorStyle, styleCache, colorCache, recycling;
|
|
2090
2106
|
|
|
2091
2107
|
if ($._fontMod) $._updateFont();
|
|
2092
2108
|
|
|
@@ -2094,14 +2110,23 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2094
2110
|
if (styleHash == -1) updateStyleHash();
|
|
2095
2111
|
colorStyle = $._fill + $._stroke + $._strokeWeight;
|
|
2096
2112
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2113
|
+
styleCache = cache[str];
|
|
2114
|
+
if (styleCache) colorCache = styleCache[styleHash];
|
|
2115
|
+
else styleCache = cache[str] = {};
|
|
2100
2116
|
|
|
2101
|
-
if (
|
|
2102
|
-
img =
|
|
2117
|
+
if (colorCache) {
|
|
2118
|
+
img = colorCache[colorStyle];
|
|
2103
2119
|
if (img) return img;
|
|
2104
|
-
|
|
2120
|
+
|
|
2121
|
+
if (colorCache.size >= 4) {
|
|
2122
|
+
for (let recycleKey in colorCache) {
|
|
2123
|
+
img = colorCache[recycleKey];
|
|
2124
|
+
delete colorCache[recycleKey];
|
|
2125
|
+
break;
|
|
2126
|
+
}
|
|
2127
|
+
recycling = true;
|
|
2128
|
+
}
|
|
2129
|
+
} else colorCache = styleCache[styleHash] = {};
|
|
2105
2130
|
}
|
|
2106
2131
|
|
|
2107
2132
|
if (str.indexOf('\n') == -1) lines[0] = str;
|
|
@@ -2169,6 +2194,8 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2169
2194
|
img._bottom = img._top + ascent + leading * (lines.length - 1);
|
|
2170
2195
|
img._leading = leading;
|
|
2171
2196
|
} else {
|
|
2197
|
+
let cnv = img.canvas;
|
|
2198
|
+
img.ctx.clearRect(0, 0, cnv.width, cnv.height);
|
|
2172
2199
|
img.modified = true;
|
|
2173
2200
|
}
|
|
2174
2201
|
|
|
@@ -2199,19 +2226,33 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2199
2226
|
if (!$._fillSet) ctx.fillStyle = ogFill;
|
|
2200
2227
|
|
|
2201
2228
|
if (genTextImage) {
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
s = cache[s];
|
|
2212
|
-
for (let h of hashes) delete s[h];
|
|
2229
|
+
colorCache[colorStyle] = img;
|
|
2230
|
+
|
|
2231
|
+
if (!recycling) {
|
|
2232
|
+
if (!colorCache.size) {
|
|
2233
|
+
Object.defineProperty(colorCache, 'size', {
|
|
2234
|
+
writable: true,
|
|
2235
|
+
enumerable: false
|
|
2236
|
+
});
|
|
2237
|
+
colorCache.size = 0;
|
|
2213
2238
|
}
|
|
2214
|
-
|
|
2239
|
+
colorCache.size++;
|
|
2240
|
+
cacheSize++;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
if (cacheSize > Q5.MAX_TEXT_IMAGES) {
|
|
2244
|
+
for (const str in cache) {
|
|
2245
|
+
styleCache = cache[str];
|
|
2246
|
+
for (const hash in styleCache) {
|
|
2247
|
+
colorCache = styleCache[hash];
|
|
2248
|
+
for (let c in colorCache) {
|
|
2249
|
+
let _img = colorCache[c];
|
|
2250
|
+
if (_img._texture) _img._texture.destroy();
|
|
2251
|
+
delete colorCache[c];
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
cacheSize = 0;
|
|
2215
2256
|
}
|
|
2216
2257
|
return img;
|
|
2217
2258
|
}
|
|
@@ -2239,6 +2280,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2239
2280
|
};
|
|
2240
2281
|
|
|
2241
2282
|
Q5.fonts = [];
|
|
2283
|
+
Q5.MAX_TEXT_IMAGES = 5000;
|
|
2242
2284
|
Q5.modules.color = ($, q) => {
|
|
2243
2285
|
$.RGB = $.RGBA = $.RGBHDR = $._colorMode = 'rgb';
|
|
2244
2286
|
$.HSL = 'hsl';
|
|
@@ -2275,6 +2317,7 @@ Q5.modules.color = ($, q) => {
|
|
|
2275
2317
|
black: [0, 0, 0],
|
|
2276
2318
|
blue: [0, 0, 255],
|
|
2277
2319
|
brown: [165, 42, 42],
|
|
2320
|
+
coral: [255, 127, 80],
|
|
2278
2321
|
crimson: [220, 20, 60],
|
|
2279
2322
|
cyan: [0, 255, 255],
|
|
2280
2323
|
darkviolet: [148, 0, 211],
|
|
@@ -3112,29 +3155,38 @@ Q5.modules.dom = ($, q) => {
|
|
|
3112
3155
|
|
|
3113
3156
|
$.createSpan = (content) => $.createEl('span', content);
|
|
3114
3157
|
|
|
3158
|
+
function initVideo(el) {
|
|
3159
|
+
el.width ||= el.videoWidth;
|
|
3160
|
+
el.height ||= el.videoHeight;
|
|
3161
|
+
el.defaultWidth = el.width * $._defaultImageScale;
|
|
3162
|
+
el.defaultHeight = el.height * $._defaultImageScale;
|
|
3163
|
+
el.ready = true;
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3115
3166
|
$.createVideo = (src) => {
|
|
3116
3167
|
let el = $.createEl('video');
|
|
3117
3168
|
el.crossOrigin = 'anonymous';
|
|
3118
3169
|
|
|
3119
|
-
el._load = () => {
|
|
3120
|
-
el.width ||= el.videoWidth;
|
|
3121
|
-
el.height ||= el.videoHeight;
|
|
3122
|
-
el.defaultWidth = el.width * $._defaultImageScale;
|
|
3123
|
-
el.defaultHeight = el.height * $._defaultImageScale;
|
|
3124
|
-
el.ready = true;
|
|
3125
|
-
};
|
|
3126
|
-
|
|
3127
3170
|
if (src) {
|
|
3128
3171
|
el.promise = new Promise((resolve) => {
|
|
3129
3172
|
el.addEventListener('loadeddata', () => {
|
|
3130
|
-
el.
|
|
3173
|
+
delete el.promise;
|
|
3174
|
+
delete el.then;
|
|
3175
|
+
if (el._usedAwait) {
|
|
3176
|
+
el = $.createEl('video');
|
|
3177
|
+
el.crossOrigin = 'anonymous';
|
|
3178
|
+
el.src = src;
|
|
3179
|
+
}
|
|
3180
|
+
initVideo(el);
|
|
3131
3181
|
resolve(el);
|
|
3132
3182
|
});
|
|
3133
3183
|
el.src = src;
|
|
3134
3184
|
});
|
|
3135
|
-
$.
|
|
3136
|
-
|
|
3137
|
-
|
|
3185
|
+
$._loaders.push(el.promise);
|
|
3186
|
+
el.then = (resolve, reject) => {
|
|
3187
|
+
el._usedAwait = true;
|
|
3188
|
+
return el.promise.then(resolve, reject);
|
|
3189
|
+
};
|
|
3138
3190
|
}
|
|
3139
3191
|
return el;
|
|
3140
3192
|
};
|
|
@@ -3149,18 +3201,22 @@ Q5.modules.dom = ($, q) => {
|
|
|
3149
3201
|
constraints.video.facingMode ??= 'user';
|
|
3150
3202
|
|
|
3151
3203
|
let vid = $.createVideo();
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
vid.
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3204
|
+
|
|
3205
|
+
function extendVideo(vid) {
|
|
3206
|
+
vid.playsinline = vid.autoplay = true;
|
|
3207
|
+
if (flipped) {
|
|
3208
|
+
vid.flipped = true;
|
|
3209
|
+
vid.style.transform = 'scale(-1, 1)';
|
|
3210
|
+
}
|
|
3211
|
+
vid.loadPixels = () => {
|
|
3212
|
+
let g = $.createGraphics(vid.videoWidth, vid.videoHeight, { renderer: 'c2d' });
|
|
3213
|
+
g.image(vid, 0, 0);
|
|
3214
|
+
g.loadPixels();
|
|
3215
|
+
vid.pixels = g.pixels;
|
|
3216
|
+
g.remove();
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
|
|
3164
3220
|
vid.promise = (async () => {
|
|
3165
3221
|
let stream;
|
|
3166
3222
|
try {
|
|
@@ -3169,16 +3225,26 @@ Q5.modules.dom = ($, q) => {
|
|
|
3169
3225
|
throw e;
|
|
3170
3226
|
}
|
|
3171
3227
|
|
|
3228
|
+
delete vid.promise;
|
|
3229
|
+
delete vid.then;
|
|
3230
|
+
if (vid._usedAwait) {
|
|
3231
|
+
vid = $.createVideo();
|
|
3232
|
+
}
|
|
3233
|
+
extendVideo(vid);
|
|
3234
|
+
|
|
3172
3235
|
vid.srcObject = stream;
|
|
3173
3236
|
await new Promise((resolve) => vid.addEventListener('loadeddata', resolve));
|
|
3174
3237
|
|
|
3175
|
-
vid
|
|
3238
|
+
initVideo(vid);
|
|
3176
3239
|
if (cb) cb(vid);
|
|
3177
3240
|
return vid;
|
|
3178
3241
|
})();
|
|
3179
|
-
$.
|
|
3242
|
+
$._loaders.push(vid.promise);
|
|
3180
3243
|
|
|
3181
|
-
|
|
3244
|
+
vid.then = (resolve, reject) => {
|
|
3245
|
+
vid._usedAwait = true;
|
|
3246
|
+
return vid.promise.then(resolve, reject);
|
|
3247
|
+
};
|
|
3182
3248
|
return vid;
|
|
3183
3249
|
};
|
|
3184
3250
|
|
|
@@ -3258,12 +3324,12 @@ Q5.modules.fes = ($) => {
|
|
|
3258
3324
|
}
|
|
3259
3325
|
}
|
|
3260
3326
|
|
|
3261
|
-
if (Q5.online != false && typeof navigator != undefined && navigator.onLine) {
|
|
3327
|
+
if ($._isGlobal && Q5.online != false && typeof navigator != undefined && navigator.onLine) {
|
|
3262
3328
|
async function checkLatestVersion() {
|
|
3263
3329
|
try {
|
|
3264
|
-
let
|
|
3265
|
-
if (!
|
|
3266
|
-
let data = await
|
|
3330
|
+
let res = await fetch('https://data.jsdelivr.com/v1/package/npm/q5');
|
|
3331
|
+
if (!res.ok) return;
|
|
3332
|
+
let data = await res.json();
|
|
3267
3333
|
let l = data.tags.latest;
|
|
3268
3334
|
l = l.slice(0, l.lastIndexOf('.'));
|
|
3269
3335
|
if (l != Q5.version) {
|
|
@@ -4352,6 +4418,11 @@ Q5.modules.sound = ($, q) => {
|
|
|
4352
4418
|
sounds.push(s);
|
|
4353
4419
|
|
|
4354
4420
|
s.promise = (async () => {
|
|
4421
|
+
if (s._usedAwait) {
|
|
4422
|
+
sounds.splice(sounds.indexOf(s), 1);
|
|
4423
|
+
s = new Q5.Sound();
|
|
4424
|
+
sounds.push(s);
|
|
4425
|
+
}
|
|
4355
4426
|
let err;
|
|
4356
4427
|
try {
|
|
4357
4428
|
await s.load(url);
|
|
@@ -4359,13 +4430,17 @@ Q5.modules.sound = ($, q) => {
|
|
|
4359
4430
|
err = e;
|
|
4360
4431
|
}
|
|
4361
4432
|
delete s.promise;
|
|
4433
|
+
delete s.then;
|
|
4362
4434
|
if (err) throw err;
|
|
4363
4435
|
if (cb) cb(s);
|
|
4364
4436
|
return s;
|
|
4365
4437
|
})();
|
|
4366
|
-
$.
|
|
4438
|
+
$._loaders.push(s.promise);
|
|
4367
4439
|
|
|
4368
|
-
|
|
4440
|
+
s.then = (resolve, reject) => {
|
|
4441
|
+
s._usedAwait = true;
|
|
4442
|
+
return s.promise.then(resolve, reject);
|
|
4443
|
+
};
|
|
4369
4444
|
return s;
|
|
4370
4445
|
};
|
|
4371
4446
|
|
|
@@ -4374,19 +4449,30 @@ Q5.modules.sound = ($, q) => {
|
|
|
4374
4449
|
a._isAudio = true;
|
|
4375
4450
|
a.crossOrigin = 'Anonymous';
|
|
4376
4451
|
a.promise = new Promise((resolve, reject) => {
|
|
4377
|
-
|
|
4452
|
+
function loaded() {
|
|
4378
4453
|
if (!a.loaded) {
|
|
4454
|
+
delete a.promise;
|
|
4455
|
+
delete a.then;
|
|
4456
|
+
if (a._usedAwait) {
|
|
4457
|
+
a = new Audio(url);
|
|
4458
|
+
a._isAudio = true;
|
|
4459
|
+
a.crossOrigin = 'Anonymous';
|
|
4460
|
+
}
|
|
4379
4461
|
a.loaded = true;
|
|
4380
4462
|
if (cb) cb(a);
|
|
4381
4463
|
resolve(a);
|
|
4382
4464
|
}
|
|
4383
|
-
}
|
|
4384
|
-
a.addEventListener('
|
|
4465
|
+
}
|
|
4466
|
+
a.addEventListener('canplay', loaded);
|
|
4467
|
+
a.addEventListener('suspend', loaded);
|
|
4385
4468
|
a.addEventListener('error', reject);
|
|
4386
4469
|
});
|
|
4387
|
-
$.
|
|
4470
|
+
$._loaders.push(a.promise);
|
|
4388
4471
|
|
|
4389
|
-
|
|
4472
|
+
a.then = (resolve, reject) => {
|
|
4473
|
+
a._usedAwait = true;
|
|
4474
|
+
return a.promise.then(resolve, reject);
|
|
4475
|
+
};
|
|
4390
4476
|
return a;
|
|
4391
4477
|
};
|
|
4392
4478
|
|
|
@@ -4571,27 +4657,30 @@ Q5.Sound = class {
|
|
|
4571
4657
|
Q5.modules.util = ($, q) => {
|
|
4572
4658
|
$._loadFile = (url, cb, type) => {
|
|
4573
4659
|
let ret = {};
|
|
4574
|
-
ret.promise =
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
$.
|
|
4594
|
-
|
|
4660
|
+
ret.promise = fetch(url)
|
|
4661
|
+
.then((res) => {
|
|
4662
|
+
if (!res.ok) {
|
|
4663
|
+
reject('error loading file');
|
|
4664
|
+
return null;
|
|
4665
|
+
}
|
|
4666
|
+
return type == 'json' ? res.json() : res.text();
|
|
4667
|
+
})
|
|
4668
|
+
.then((f) => {
|
|
4669
|
+
if (type == 'csv') f = Q5.CSV.parse(f);
|
|
4670
|
+
|
|
4671
|
+
if (typeof f == 'string') ret.text = f;
|
|
4672
|
+
else Object.assign(ret, f);
|
|
4673
|
+
|
|
4674
|
+
delete ret.promise;
|
|
4675
|
+
delete ret.then;
|
|
4676
|
+
if (cb) cb(f);
|
|
4677
|
+
return f;
|
|
4678
|
+
});
|
|
4679
|
+
$._loaders.push(ret.promise);
|
|
4680
|
+
|
|
4681
|
+
ret.then = (resolve, reject) => {
|
|
4682
|
+
return ret.promise.then(resolve, reject);
|
|
4683
|
+
};
|
|
4595
4684
|
return ret;
|
|
4596
4685
|
};
|
|
4597
4686
|
|
|
@@ -4607,11 +4696,15 @@ Q5.modules.util = ($, q) => {
|
|
|
4607
4696
|
let xml = new DOMParser().parseFromString(text, 'application/xml');
|
|
4608
4697
|
ret.DOM = xml;
|
|
4609
4698
|
delete ret.promise;
|
|
4699
|
+
delete ret.then;
|
|
4610
4700
|
if (cb) cb(xml);
|
|
4611
4701
|
return xml;
|
|
4612
4702
|
});
|
|
4613
|
-
$.
|
|
4614
|
-
|
|
4703
|
+
$._loaders.push(ret.promise);
|
|
4704
|
+
|
|
4705
|
+
ret.then = (resolve, reject) => {
|
|
4706
|
+
return ret.promise.then(resolve, reject);
|
|
4707
|
+
};
|
|
4615
4708
|
return ret;
|
|
4616
4709
|
};
|
|
4617
4710
|
|
|
@@ -4623,7 +4716,7 @@ Q5.modules.util = ($, q) => {
|
|
|
4623
4716
|
$.load = function (...urls) {
|
|
4624
4717
|
if (Array.isArray(urls[0])) urls = urls[0];
|
|
4625
4718
|
|
|
4626
|
-
let
|
|
4719
|
+
let thenables = [];
|
|
4627
4720
|
|
|
4628
4721
|
for (let url of urls) {
|
|
4629
4722
|
let ext = url.split('.').pop().toLowerCase();
|
|
@@ -4644,11 +4737,11 @@ Q5.modules.util = ($, q) => {
|
|
|
4644
4737
|
} else {
|
|
4645
4738
|
obj = $.loadText(url);
|
|
4646
4739
|
}
|
|
4647
|
-
|
|
4740
|
+
thenables.push(obj);
|
|
4648
4741
|
}
|
|
4649
4742
|
|
|
4650
|
-
if (urls.length == 1) return
|
|
4651
|
-
return Promise.all(
|
|
4743
|
+
if (urls.length == 1) return thenables[0];
|
|
4744
|
+
return Promise.all(thenables);
|
|
4652
4745
|
};
|
|
4653
4746
|
|
|
4654
4747
|
async function saveFile(data, name, ext) {
|
|
@@ -4688,21 +4781,6 @@ Q5.modules.util = ($, q) => {
|
|
|
4688
4781
|
} else saveFile(a);
|
|
4689
4782
|
};
|
|
4690
4783
|
|
|
4691
|
-
$.CSV = {};
|
|
4692
|
-
$.CSV.parse = (csv, sep = ',', lineSep = '\n') => {
|
|
4693
|
-
if (!csv.length) return [];
|
|
4694
|
-
let a = [],
|
|
4695
|
-
lns = csv.split(lineSep),
|
|
4696
|
-
headers = lns[0].split(sep).map((h) => h.replaceAll('"', ''));
|
|
4697
|
-
for (let i = 1; i < lns.length; i++) {
|
|
4698
|
-
let o = {},
|
|
4699
|
-
ln = lns[i].split(sep);
|
|
4700
|
-
headers.forEach((h, i) => (o[h] = JSON.parse(ln[i])));
|
|
4701
|
-
a.push(o);
|
|
4702
|
-
}
|
|
4703
|
-
return a;
|
|
4704
|
-
};
|
|
4705
|
-
|
|
4706
4784
|
if ($.canvas && !Q5._createServerCanvas) {
|
|
4707
4785
|
$.canvas.save = $.saveCanvas = $.save;
|
|
4708
4786
|
}
|
|
@@ -4739,6 +4817,21 @@ Q5.modules.util = ($, q) => {
|
|
|
4739
4817
|
return a;
|
|
4740
4818
|
};
|
|
4741
4819
|
};
|
|
4820
|
+
|
|
4821
|
+
Q5.CSV = {};
|
|
4822
|
+
Q5.CSV.parse = (csv, sep = ',', lineSep = '\n') => {
|
|
4823
|
+
if (!csv.length) return [];
|
|
4824
|
+
let a = [],
|
|
4825
|
+
lns = csv.split(lineSep),
|
|
4826
|
+
headers = lns[0].split(sep).map((h) => h.replaceAll('"', ''));
|
|
4827
|
+
for (let i = 1; i < lns.length; i++) {
|
|
4828
|
+
let o = {},
|
|
4829
|
+
ln = lns[i].split(sep);
|
|
4830
|
+
headers.forEach((h, i) => (o[h] = JSON.parse(ln[i])));
|
|
4831
|
+
a.push(o);
|
|
4832
|
+
}
|
|
4833
|
+
return a;
|
|
4834
|
+
};
|
|
4742
4835
|
Q5.modules.vector = ($) => {
|
|
4743
4836
|
$.Vector = Q5.Vector;
|
|
4744
4837
|
$.createVector = (x, y, z) => new $.Vector(x, y, z, $);
|
|
@@ -5532,8 +5625,18 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5532
5625
|
if (args.length == 1) m = args[0];
|
|
5533
5626
|
else m = args;
|
|
5534
5627
|
|
|
5535
|
-
if (m.length
|
|
5536
|
-
|
|
5628
|
+
if (m.length <= 6) {
|
|
5629
|
+
const a = m[0],
|
|
5630
|
+
b = m[1],
|
|
5631
|
+
c = m[2],
|
|
5632
|
+
d = m[3],
|
|
5633
|
+
e = m[4] || 0,
|
|
5634
|
+
f = m[5] || 0;
|
|
5635
|
+
// Convert Canvas2D [a,b,c,d,e,f] (column-major 3x3: [a,b,0, c,d,0, e,f,1])
|
|
5636
|
+
m = [a, b, 0, c, d, 0, e, f, 1];
|
|
5637
|
+
}
|
|
5638
|
+
if (m.length <= 9) {
|
|
5639
|
+
// convert 3x3 matrix to 4x4 layout used internally
|
|
5537
5640
|
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]];
|
|
5538
5641
|
} else if (m.length != 16) {
|
|
5539
5642
|
throw new Error('Matrix must be a 3x3 or 4x4 array.');
|
|
@@ -6674,10 +6777,10 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6674
6777
|
hw = w;
|
|
6675
6778
|
hh = h;
|
|
6676
6779
|
} else if (_rectMode == 'corners') {
|
|
6677
|
-
hw = (
|
|
6678
|
-
hh = (
|
|
6679
|
-
x
|
|
6680
|
-
y
|
|
6780
|
+
hw = Math.abs((w - x) / 2);
|
|
6781
|
+
hh = Math.abs((h - y) / 2);
|
|
6782
|
+
x = (x + w) / 2;
|
|
6783
|
+
y = (y + h) / 2;
|
|
6681
6784
|
}
|
|
6682
6785
|
}
|
|
6683
6786
|
rectModeCache[0] = x;
|
|
@@ -6696,7 +6799,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6696
6799
|
addRect(x, y, hw, hh, rr, doStroke ? sw : 0, doFill ? fillIdx : 0);
|
|
6697
6800
|
};
|
|
6698
6801
|
|
|
6699
|
-
$.square = (x, y, s) => $.rect(x, y, s, s);
|
|
6802
|
+
$.square = (x, y, s, rr) => $.rect(x, y, s, s, rr);
|
|
6700
6803
|
|
|
6701
6804
|
function addCapsule(x1, y1, x2, y2, r, strokeW, fillCapsule) {
|
|
6702
6805
|
let dx = x2 - x1,
|
|
@@ -6982,8 +7085,8 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6982
7085
|
} else if (_ellipseMode == 'corners') {
|
|
6983
7086
|
x = (x + w) / 2;
|
|
6984
7087
|
y = (y + h) / 2;
|
|
6985
|
-
a =
|
|
6986
|
-
b =
|
|
7088
|
+
a = w - x;
|
|
7089
|
+
b = h - y;
|
|
6987
7090
|
}
|
|
6988
7091
|
ellipseModeCache[0] = x;
|
|
6989
7092
|
ellipseModeCache[1] = y;
|
|
@@ -7338,9 +7441,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7338
7441
|
};
|
|
7339
7442
|
|
|
7340
7443
|
$.loadImage = (src, cb) => {
|
|
7341
|
-
let g = $._g.loadImage(src, () => {
|
|
7342
|
-
$._makeDrawable(
|
|
7343
|
-
if (cb) cb(
|
|
7444
|
+
let g = $._g.loadImage(src, (img) => {
|
|
7445
|
+
$._makeDrawable(img);
|
|
7446
|
+
if (cb) cb(img);
|
|
7344
7447
|
});
|
|
7345
7448
|
return g;
|
|
7346
7449
|
};
|
|
@@ -7707,18 +7810,27 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7707
7810
|
|
|
7708
7811
|
let fontsArr = [];
|
|
7709
7812
|
let fonts = {};
|
|
7813
|
+
let fontSet;
|
|
7710
7814
|
|
|
7711
|
-
async function createFont(
|
|
7712
|
-
let
|
|
7713
|
-
if (res.status == 404) return '';
|
|
7714
|
-
|
|
7715
|
-
let atlas = await res.json();
|
|
7815
|
+
async function createFont(url, fontName, cb) {
|
|
7816
|
+
let baseUrl = url.substring(0, url.lastIndexOf('-'));
|
|
7716
7817
|
|
|
7717
|
-
|
|
7718
|
-
let
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7818
|
+
// load atlas and image in parallel
|
|
7819
|
+
let atlas, img;
|
|
7820
|
+
try {
|
|
7821
|
+
[atlas, img] = await Promise.all([
|
|
7822
|
+
fetch(url).then((res) => {
|
|
7823
|
+
if (res.status == 404) throw new Error('404');
|
|
7824
|
+
return res.json();
|
|
7825
|
+
}),
|
|
7826
|
+
fetch(baseUrl + '.png')
|
|
7827
|
+
.then((res) => res.blob())
|
|
7828
|
+
.then((blob) => createImageBitmap(blob))
|
|
7829
|
+
]);
|
|
7830
|
+
} catch (error) {
|
|
7831
|
+
console.error('Error loading font:', error);
|
|
7832
|
+
return '';
|
|
7833
|
+
}
|
|
7722
7834
|
|
|
7723
7835
|
// convert image to texture
|
|
7724
7836
|
let imgSize = [img.width, img.height, 1];
|
|
@@ -7733,8 +7845,8 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7733
7845
|
// chars and kernings can be stored as csv strings, making the file
|
|
7734
7846
|
// size smaller, but they need to be parsed into arrays of objects
|
|
7735
7847
|
if (typeof atlas.chars == 'string') {
|
|
7736
|
-
atlas.chars =
|
|
7737
|
-
atlas.kernings =
|
|
7848
|
+
atlas.chars = Q5.CSV.parse(atlas.chars, ' ');
|
|
7849
|
+
atlas.kernings = Q5.CSV.parse(atlas.kernings, ' ');
|
|
7738
7850
|
}
|
|
7739
7851
|
|
|
7740
7852
|
let charCount = atlas.chars.length;
|
|
@@ -7786,44 +7898,54 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7786
7898
|
}
|
|
7787
7899
|
}
|
|
7788
7900
|
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7901
|
+
let _font = new MsdfFont(fontBindGroup, atlas.common.lineHeight, chars, kernings);
|
|
7902
|
+
_font.index = fontsArr.length;
|
|
7903
|
+
fontsArr.push(_font);
|
|
7904
|
+
fonts[fontName] = _font;
|
|
7905
|
+
$._font = _font;
|
|
7794
7906
|
|
|
7795
7907
|
if (cb) cb(fontName);
|
|
7908
|
+
return { family: fontName };
|
|
7796
7909
|
}
|
|
7797
7910
|
|
|
7798
|
-
$.loadFont = (url, cb) => {
|
|
7911
|
+
$.loadFont = (url = 'sans-serif', cb) => {
|
|
7912
|
+
fontSet = true;
|
|
7799
7913
|
if (url.startsWith('https://fonts.googleapis.com/css')) {
|
|
7800
7914
|
return $._g.loadFont(url, cb);
|
|
7801
7915
|
}
|
|
7802
7916
|
|
|
7803
7917
|
let ext = url.slice(url.lastIndexOf('.') + 1);
|
|
7804
|
-
|
|
7918
|
+
|
|
7919
|
+
// if not a url, assume it's one of q5's MSDF fonts
|
|
7920
|
+
if (url == ext) {
|
|
7921
|
+
let fontName = url;
|
|
7922
|
+
fonts[fontName] = null;
|
|
7923
|
+
url = `https://q5js.org/fonts/${fontName}-msdf.json`;
|
|
7924
|
+
if (Q5.online == false || !navigator.onLine) {
|
|
7925
|
+
url = `/node_modules/q5/builtinFonts/${fontName}-msdf.json`;
|
|
7926
|
+
}
|
|
7927
|
+
ext = 'json';
|
|
7928
|
+
}
|
|
7929
|
+
|
|
7805
7930
|
if (ext != 'json') return $._g.loadFont(url, cb);
|
|
7931
|
+
|
|
7806
7932
|
let fontName = url.slice(url.lastIndexOf('/') + 1, url.lastIndexOf('-'));
|
|
7807
7933
|
let f = { family: fontName };
|
|
7808
7934
|
f.promise = createFont(url, fontName, () => {
|
|
7809
7935
|
delete f.promise;
|
|
7936
|
+
delete f.then;
|
|
7937
|
+
if (f._usedAwait) f = { family: fontName };
|
|
7810
7938
|
if (cb) cb(f);
|
|
7811
7939
|
});
|
|
7812
|
-
$.
|
|
7940
|
+
$._loaders.push(f.promise);
|
|
7813
7941
|
|
|
7814
|
-
|
|
7942
|
+
f.then = (resolve, reject) => {
|
|
7943
|
+
f._usedAwait = true;
|
|
7944
|
+
return f.promise.then(resolve, reject);
|
|
7945
|
+
};
|
|
7815
7946
|
return f;
|
|
7816
7947
|
};
|
|
7817
7948
|
|
|
7818
|
-
$._loadDefaultFont = (fontName, cb) => {
|
|
7819
|
-
fonts[fontName] = null;
|
|
7820
|
-
let url = `https://q5js.org/fonts/${fontName}-msdf.json`;
|
|
7821
|
-
if (Q5.online == false || !navigator.onLine) {
|
|
7822
|
-
url = `/node_modules/q5/builtinFonts/${fontName}-msdf.json`;
|
|
7823
|
-
}
|
|
7824
|
-
return $.loadFont(url, cb);
|
|
7825
|
-
};
|
|
7826
|
-
|
|
7827
7949
|
let _textSize = 18,
|
|
7828
7950
|
_textAlign = 'left',
|
|
7829
7951
|
_textBaseline = 'alphabetic',
|
|
@@ -7832,12 +7954,16 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7832
7954
|
leadDiff = 4.5,
|
|
7833
7955
|
leadPercent = 1.25;
|
|
7834
7956
|
|
|
7957
|
+
let categories = ['serif', 'sans-serif', 'monospace', 'cursive', 'fantasy', 'system-ui'];
|
|
7958
|
+
|
|
7835
7959
|
$.textFont = (fontName) => {
|
|
7836
7960
|
if (!fontName) return $._font;
|
|
7961
|
+
fontSet = true;
|
|
7837
7962
|
if (typeof fontName != 'string') fontName = fontName.family;
|
|
7838
7963
|
let font = fonts[fontName];
|
|
7839
7964
|
if (font) $._font = font;
|
|
7840
|
-
|
|
7965
|
+
// if it's a font category or not a WebGPU font, set the Canvas2D font
|
|
7966
|
+
else if (categories[fontName] || font === undefined) $._g.textFont(fontName);
|
|
7841
7967
|
};
|
|
7842
7968
|
|
|
7843
7969
|
$.textSize = (size) => {
|
|
@@ -7895,8 +8021,8 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7895
8021
|
let lineWidthsCache = new Array(100);
|
|
7896
8022
|
|
|
7897
8023
|
// Reusable buffers for text data to avoid creating new arrays
|
|
7898
|
-
let charDataBuffer = new Float32Array(
|
|
7899
|
-
let textDataBuffer = new Float32Array(
|
|
8024
|
+
let charDataBuffer = new Float32Array(Q5.MAX_CHARS * 4); // reusable buffer for char data
|
|
8025
|
+
let textDataBuffer = new Float32Array(Q5.MAX_TEXTS * 8); // reusable buffer for text metadata
|
|
7900
8026
|
|
|
7901
8027
|
let measureText = (font, text, charCallback) => {
|
|
7902
8028
|
let maxWidth = 0,
|
|
@@ -7948,16 +8074,21 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7948
8074
|
};
|
|
7949
8075
|
|
|
7950
8076
|
$.text = (str, x, y, w, h) => {
|
|
7951
|
-
if (
|
|
7952
|
-
// if the default font hasn't been loaded yet, try to load it
|
|
7953
|
-
if ($._font !== null) $.textFont('sans-serif');
|
|
7954
|
-
return;
|
|
7955
|
-
}
|
|
8077
|
+
if (_textSize < 1) return;
|
|
7956
8078
|
|
|
7957
8079
|
let type = typeof str;
|
|
7958
8080
|
if (type != 'string') {
|
|
7959
8081
|
if (type == 'object') str = str.toString();
|
|
7960
8082
|
else str = str + '';
|
|
8083
|
+
} else if (!str.length) return;
|
|
8084
|
+
|
|
8085
|
+
// if not using an MSDF font
|
|
8086
|
+
if (!$._font) {
|
|
8087
|
+
// if no font is set, lazy load the default MSDF font
|
|
8088
|
+
if (!fontSet) $.loadFont();
|
|
8089
|
+
// use Canvas2D text rendering
|
|
8090
|
+
let img = $.createTextImage(str, w, h);
|
|
8091
|
+
return $.textImage(img, x, y);
|
|
7961
8092
|
}
|
|
7962
8093
|
|
|
7963
8094
|
if (str.length > w) {
|
|
@@ -8194,6 +8325,8 @@ Q5.BLUR = 8;
|
|
|
8194
8325
|
Q5.MAX_TRANSFORMS = 1e7;
|
|
8195
8326
|
Q5.MAX_RECTS = 200200;
|
|
8196
8327
|
Q5.MAX_ELLIPSES = 200200;
|
|
8328
|
+
Q5.MAX_CHARS = 100000;
|
|
8329
|
+
Q5.MAX_TEXTS = 10000;
|
|
8197
8330
|
|
|
8198
8331
|
Q5.initWebGPU = async () => {
|
|
8199
8332
|
if (!navigator.gpu) {
|