q5 2.24.3 → 2.25.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/README.md +6 -5
- package/deno.json +1 -1
- package/package.json +1 -1
- package/q5.d.ts +158 -23
- package/q5.js +145 -116
- package/q5.min.js +2 -2
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.25
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
@@ -71,14 +71,19 @@ function Q5(scope, parent, renderer) {
|
|
|
71
71
|
$.deviceOrientation = window.screen?.orientation?.type;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
$.
|
|
75
|
-
$._incrementPreload = () => q._preloadCount++;
|
|
76
|
-
$._decrementPreload = () => q._preloadCount--;
|
|
77
|
-
|
|
74
|
+
$._preloadPromises = [];
|
|
78
75
|
$._usePreload = true;
|
|
79
76
|
$.usePreloadSystem = (v) => ($._usePreload = v);
|
|
80
77
|
$.isPreloadSupported = () => $._usePreload;
|
|
81
78
|
|
|
79
|
+
const resolvers = [];
|
|
80
|
+
$._incrementPreload = () => {
|
|
81
|
+
$._preloadPromises.push(new Promise((resolve) => resolvers.push(resolve)));
|
|
82
|
+
};
|
|
83
|
+
$._decrementPreload = () => {
|
|
84
|
+
if (resolvers.length) resolvers.pop()();
|
|
85
|
+
};
|
|
86
|
+
|
|
82
87
|
$._draw = (timestamp) => {
|
|
83
88
|
let ts = timestamp || performance.now();
|
|
84
89
|
$._lastFrameTime ??= ts - $._targetFrameDuration;
|
|
@@ -94,7 +99,6 @@ function Q5(scope, parent, renderer) {
|
|
|
94
99
|
let nextTS = ts + $._targetFrameDuration;
|
|
95
100
|
let frameDelay = nextTS - performance.now();
|
|
96
101
|
while (frameDelay < 0) frameDelay += $._targetFrameDuration;
|
|
97
|
-
log(frameDelay);
|
|
98
102
|
looper = setTimeout(() => $._draw(nextTS), frameDelay);
|
|
99
103
|
}
|
|
100
104
|
} else if ($.frameCount && !$._redraw) return;
|
|
@@ -131,7 +135,7 @@ function Q5(scope, parent, renderer) {
|
|
|
131
135
|
};
|
|
132
136
|
$.noLoop = () => {
|
|
133
137
|
$._loop = false;
|
|
134
|
-
if (looper) {
|
|
138
|
+
if (looper != null) {
|
|
135
139
|
if (useRAF) cancelAnimationFrame(looper);
|
|
136
140
|
else clearTimeout(looper);
|
|
137
141
|
}
|
|
@@ -155,17 +159,20 @@ function Q5(scope, parent, renderer) {
|
|
|
155
159
|
};
|
|
156
160
|
|
|
157
161
|
$.frameRate = (hz) => {
|
|
158
|
-
if (hz) {
|
|
162
|
+
if (hz != $._targetFrameRate) {
|
|
159
163
|
$._targetFrameRate = hz;
|
|
160
164
|
$._targetFrameDuration = 1000 / hz;
|
|
161
165
|
|
|
162
|
-
if ($._loop &&
|
|
166
|
+
if ($._loop && looper != null) {
|
|
163
167
|
if (useRAF) cancelAnimationFrame(looper);
|
|
164
168
|
else clearTimeout(looper);
|
|
165
169
|
looper = null;
|
|
166
170
|
}
|
|
167
171
|
useRAF = hz <= 60;
|
|
168
|
-
|
|
172
|
+
if ($._setupDone) {
|
|
173
|
+
if (useRAF) looper = raf($._draw);
|
|
174
|
+
else looper = setTimeout(() => $._draw(), $._targetFrameDuration);
|
|
175
|
+
}
|
|
169
176
|
}
|
|
170
177
|
return $._frameRate;
|
|
171
178
|
};
|
|
@@ -215,11 +222,23 @@ function Q5(scope, parent, renderer) {
|
|
|
215
222
|
if (n[0] != '_' && typeof $[n] == 'function') $[n] = fn.bind($);
|
|
216
223
|
}
|
|
217
224
|
|
|
225
|
+
for (let [n, fn] of Object.entries(Q5.preloadMethods)) {
|
|
226
|
+
$[n] = function () {
|
|
227
|
+
$._incrementPreload();
|
|
228
|
+
return fn.apply($, arguments);
|
|
229
|
+
// fn is responsible for calling $._decrementPreload
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
218
233
|
if (scope == 'global') {
|
|
219
234
|
let props = Object.getOwnPropertyNames($);
|
|
220
235
|
for (let p of props) {
|
|
221
236
|
if (p[0] != '_') globalScope[p] = $[p];
|
|
222
237
|
}
|
|
238
|
+
// to support p5.sound
|
|
239
|
+
for (let p of ['_incrementPreload', '_decrementPreload']) {
|
|
240
|
+
globalScope[p] = $[p];
|
|
241
|
+
}
|
|
223
242
|
}
|
|
224
243
|
|
|
225
244
|
if (typeof scope == 'function') scope($);
|
|
@@ -282,7 +301,8 @@ function Q5(scope, parent, renderer) {
|
|
|
282
301
|
|
|
283
302
|
async function _setup() {
|
|
284
303
|
$._startDone = true;
|
|
285
|
-
|
|
304
|
+
await Promise.all($._preloadPromises);
|
|
305
|
+
if ($._g) await Promise.all($._g._preloadPromises);
|
|
286
306
|
millisStart = performance.now();
|
|
287
307
|
await $.setup();
|
|
288
308
|
$._setupDone = true;
|
|
@@ -326,7 +346,9 @@ Q5.methods = {
|
|
|
326
346
|
remove: []
|
|
327
347
|
};
|
|
328
348
|
Q5.prototype.registerMethod = (m, fn) => Q5.methods[m].push(fn);
|
|
329
|
-
|
|
349
|
+
|
|
350
|
+
Q5.preloadMethods = {};
|
|
351
|
+
Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.preloadMethods[n] = fn[n]);
|
|
330
352
|
|
|
331
353
|
if (Q5._server) global.p5 ??= global.Q5 = Q5;
|
|
332
354
|
|
|
@@ -340,7 +362,7 @@ function createCanvas(w, h, opt) {
|
|
|
340
362
|
}
|
|
341
363
|
}
|
|
342
364
|
|
|
343
|
-
Q5.version = Q5.VERSION = '2.
|
|
365
|
+
Q5.version = Q5.VERSION = '2.25';
|
|
344
366
|
|
|
345
367
|
if (typeof document == 'object') {
|
|
346
368
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -934,6 +956,9 @@ Q5.renderers.c2d.shapes = ($) => {
|
|
|
934
956
|
w /= 2;
|
|
935
957
|
h /= 2;
|
|
936
958
|
|
|
959
|
+
w = Math.abs(w);
|
|
960
|
+
h = Math.abs(h);
|
|
961
|
+
|
|
937
962
|
if (!$._doFill && mode == $.PIE_OPEN) mode = $.CHORD_OPEN;
|
|
938
963
|
|
|
939
964
|
$.ctx.beginPath();
|
|
@@ -975,7 +1000,7 @@ Q5.renderers.c2d.shapes = ($) => {
|
|
|
975
1000
|
|
|
976
1001
|
function ellipse(x, y, w, h) {
|
|
977
1002
|
$.ctx.beginPath();
|
|
978
|
-
$.ctx.ellipse(x, y, w / 2, h / 2, 0, 0, TAU);
|
|
1003
|
+
$.ctx.ellipse(x, y, Math.abs(w / 2), Math.abs(h / 2), 0, 0, TAU);
|
|
979
1004
|
ink();
|
|
980
1005
|
}
|
|
981
1006
|
|
|
@@ -1006,7 +1031,7 @@ Q5.renderers.c2d.shapes = ($) => {
|
|
|
1006
1031
|
d *= $._da;
|
|
1007
1032
|
}
|
|
1008
1033
|
$.ctx.beginPath();
|
|
1009
|
-
$.ctx.arc(x, y, d / 2, 0, TAU);
|
|
1034
|
+
$.ctx.arc(x, y, Math.abs(d / 2), 0, TAU);
|
|
1010
1035
|
ink();
|
|
1011
1036
|
} else $.ellipse(x, y, d, d);
|
|
1012
1037
|
};
|
|
@@ -1310,7 +1335,6 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1310
1335
|
`q5 doesn't support GIFs. Use a video or p5play animation instead. https://github.com/q5js/q5.js/issues/84`
|
|
1311
1336
|
);
|
|
1312
1337
|
}
|
|
1313
|
-
q._preloadCount++;
|
|
1314
1338
|
let last = [...arguments].at(-1);
|
|
1315
1339
|
if (typeof last == 'object') {
|
|
1316
1340
|
opt = last;
|
|
@@ -1323,7 +1347,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1323
1347
|
let img = new window.Image();
|
|
1324
1348
|
img.crossOrigin = 'Anonymous';
|
|
1325
1349
|
|
|
1326
|
-
g.
|
|
1350
|
+
g.promise = new Promise((resolve, reject) => {
|
|
1327
1351
|
img.onload = () => {
|
|
1328
1352
|
img._pixelDensity = pd;
|
|
1329
1353
|
g.defaultWidth = img.width * $._defaultImageScale;
|
|
@@ -1333,20 +1357,17 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1333
1357
|
g._setImageSize(Math.ceil(g.naturalWidth / pd), Math.ceil(g.naturalHeight / pd));
|
|
1334
1358
|
|
|
1335
1359
|
g.ctx.drawImage(img, 0, 0);
|
|
1336
|
-
q._preloadCount--;
|
|
1337
1360
|
if (cb) cb(g);
|
|
1338
|
-
delete g.
|
|
1361
|
+
delete g.promise;
|
|
1339
1362
|
resolve(g);
|
|
1340
1363
|
};
|
|
1341
|
-
img.onerror =
|
|
1342
|
-
q._preloadCount--;
|
|
1343
|
-
reject(e);
|
|
1344
|
-
};
|
|
1364
|
+
img.onerror = reject;
|
|
1345
1365
|
});
|
|
1366
|
+
$._preloadPromises.push(g.promise);
|
|
1346
1367
|
|
|
1347
1368
|
g.src = img.src = url;
|
|
1348
1369
|
|
|
1349
|
-
if (!$._usePreload) return g.
|
|
1370
|
+
if (!$._usePreload) return g.promise;
|
|
1350
1371
|
return g;
|
|
1351
1372
|
};
|
|
1352
1373
|
|
|
@@ -1783,34 +1804,32 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1783
1804
|
fontMod = false,
|
|
1784
1805
|
styleHash = 0,
|
|
1785
1806
|
styleHashes = [],
|
|
1786
|
-
useCache = false,
|
|
1787
1807
|
genTextImage = false,
|
|
1788
|
-
cacheSize = 0
|
|
1789
|
-
cacheMax = 12000;
|
|
1808
|
+
cacheSize = 0;
|
|
1790
1809
|
|
|
1791
1810
|
let cache = ($._textCache = {});
|
|
1811
|
+
$._textCacheMaxSize = 12000;
|
|
1792
1812
|
|
|
1793
1813
|
$.loadFont = (url, cb) => {
|
|
1794
|
-
q._preloadCount++;
|
|
1795
1814
|
let name = url.split('/').pop().split('.')[0].replace(' ', '');
|
|
1796
1815
|
|
|
1797
1816
|
let f = new FontFace(name, `url(${url})`);
|
|
1798
1817
|
document.fonts.add(f);
|
|
1799
|
-
f.
|
|
1818
|
+
f.promise = (async () => {
|
|
1800
1819
|
let err;
|
|
1801
1820
|
try {
|
|
1802
1821
|
await f.load();
|
|
1803
1822
|
} catch (e) {
|
|
1804
1823
|
err = e;
|
|
1805
1824
|
}
|
|
1806
|
-
|
|
1807
|
-
delete f._loader;
|
|
1825
|
+
delete f.promise;
|
|
1808
1826
|
if (err) throw err;
|
|
1809
1827
|
if (cb) cb(f);
|
|
1810
1828
|
return f;
|
|
1811
1829
|
})();
|
|
1830
|
+
$._preloadPromises.push(f.promise);
|
|
1812
1831
|
$.textFont(name);
|
|
1813
|
-
if (!$._usePreload) return f.
|
|
1832
|
+
if (!$._usePreload) return f.promise;
|
|
1814
1833
|
return f;
|
|
1815
1834
|
};
|
|
1816
1835
|
|
|
@@ -1896,12 +1915,6 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1896
1915
|
styleHash = hash >>> 0;
|
|
1897
1916
|
};
|
|
1898
1917
|
|
|
1899
|
-
$.textCache = (enable, maxSize) => {
|
|
1900
|
-
if (maxSize) cacheMax = maxSize;
|
|
1901
|
-
if (enable !== undefined) useCache = enable;
|
|
1902
|
-
return useCache;
|
|
1903
|
-
};
|
|
1904
|
-
|
|
1905
1918
|
$.createTextImage = (str, w, h) => {
|
|
1906
1919
|
genTextImage = true;
|
|
1907
1920
|
let img = $.text(str, 0, 0, w, h);
|
|
@@ -1923,7 +1936,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1923
1936
|
|
|
1924
1937
|
if (fontMod) updateFont();
|
|
1925
1938
|
|
|
1926
|
-
if (
|
|
1939
|
+
if (genTextImage) {
|
|
1927
1940
|
if (styleHash == -1) updateStyleHash();
|
|
1928
1941
|
|
|
1929
1942
|
img = cache[str];
|
|
@@ -1931,8 +1944,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1931
1944
|
|
|
1932
1945
|
if (img) {
|
|
1933
1946
|
if (img._fill == $._fill && img._stroke == $._stroke && img._strokeWeight == $._strokeWeight) {
|
|
1934
|
-
|
|
1935
|
-
return $.textImage(img, x, y);
|
|
1947
|
+
return img;
|
|
1936
1948
|
} else img.clear();
|
|
1937
1949
|
}
|
|
1938
1950
|
}
|
|
@@ -1960,7 +1972,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1960
1972
|
lines = wrapped;
|
|
1961
1973
|
}
|
|
1962
1974
|
|
|
1963
|
-
if (!
|
|
1975
|
+
if (!genTextImage) {
|
|
1964
1976
|
tX = x;
|
|
1965
1977
|
tY = y;
|
|
1966
1978
|
} else {
|
|
@@ -2021,12 +2033,12 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2021
2033
|
|
|
2022
2034
|
if (!$._fillSet) ctx.fillStyle = ogFill;
|
|
2023
2035
|
|
|
2024
|
-
if (
|
|
2036
|
+
if (genTextImage) {
|
|
2025
2037
|
styleHashes.push(styleHash);
|
|
2026
2038
|
(cache[str] ??= {})[styleHash] = img;
|
|
2027
2039
|
|
|
2028
2040
|
cacheSize++;
|
|
2029
|
-
if (cacheSize >
|
|
2041
|
+
if (cacheSize > $._textCacheMaxSize) {
|
|
2030
2042
|
let half = Math.ceil(cacheSize / 2);
|
|
2031
2043
|
let hashes = styleHashes.splice(0, half);
|
|
2032
2044
|
for (let s in cache) {
|
|
@@ -2036,8 +2048,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2036
2048
|
cacheSize -= half;
|
|
2037
2049
|
}
|
|
2038
2050
|
|
|
2039
|
-
|
|
2040
|
-
$.textImage(img, x, y);
|
|
2051
|
+
return img;
|
|
2041
2052
|
}
|
|
2042
2053
|
};
|
|
2043
2054
|
|
|
@@ -2932,24 +2943,21 @@ Q5.modules.dom = ($, q) => {
|
|
|
2932
2943
|
};
|
|
2933
2944
|
|
|
2934
2945
|
if (src) {
|
|
2935
|
-
|
|
2936
|
-
el._loader = new Promise((resolve) => {
|
|
2946
|
+
el.promise = new Promise((resolve) => {
|
|
2937
2947
|
el.addEventListener('loadeddata', () => {
|
|
2938
2948
|
el._load();
|
|
2939
|
-
q._preloadCount--;
|
|
2940
2949
|
resolve(el);
|
|
2941
2950
|
});
|
|
2942
2951
|
el.src = src;
|
|
2943
2952
|
});
|
|
2953
|
+
$._preloadPromises.push(el.promise);
|
|
2944
2954
|
|
|
2945
|
-
if (!$._usePreload) return el.
|
|
2955
|
+
if (!$._usePreload) return el.promise;
|
|
2946
2956
|
}
|
|
2947
2957
|
return el;
|
|
2948
2958
|
};
|
|
2949
2959
|
|
|
2950
2960
|
$.createCapture = function (type, flipped = true, cb) {
|
|
2951
|
-
q._preloadCount++;
|
|
2952
|
-
|
|
2953
2961
|
let constraints = typeof type == 'string' ? { [type]: true } : type || { video: true, audio: true };
|
|
2954
2962
|
|
|
2955
2963
|
if (constraints.video === true) {
|
|
@@ -2971,12 +2979,11 @@ Q5.modules.dom = ($, q) => {
|
|
|
2971
2979
|
vid.pixels = g.pixels;
|
|
2972
2980
|
g.remove();
|
|
2973
2981
|
};
|
|
2974
|
-
vid.
|
|
2982
|
+
vid.promise = (async () => {
|
|
2975
2983
|
let stream;
|
|
2976
2984
|
try {
|
|
2977
2985
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
2978
2986
|
} catch (e) {
|
|
2979
|
-
q._preloadCount--;
|
|
2980
2987
|
throw e;
|
|
2981
2988
|
}
|
|
2982
2989
|
|
|
@@ -2985,11 +2992,11 @@ Q5.modules.dom = ($, q) => {
|
|
|
2985
2992
|
|
|
2986
2993
|
vid._load();
|
|
2987
2994
|
if (cb) cb(vid);
|
|
2988
|
-
q._preloadCount--;
|
|
2989
2995
|
return vid;
|
|
2990
2996
|
})();
|
|
2997
|
+
$._preloadPromises.push(vid.promise);
|
|
2991
2998
|
|
|
2992
|
-
if (!$._usePreload) return vid.
|
|
2999
|
+
if (!$._usePreload) return vid.promise;
|
|
2993
3000
|
return vid;
|
|
2994
3001
|
};
|
|
2995
3002
|
|
|
@@ -4065,49 +4072,44 @@ Q5.modules.sound = ($, q) => {
|
|
|
4065
4072
|
let sounds = [];
|
|
4066
4073
|
|
|
4067
4074
|
$.loadSound = (url, cb) => {
|
|
4068
|
-
q._preloadCount++;
|
|
4069
|
-
|
|
4070
4075
|
let s = new Q5.Sound();
|
|
4071
4076
|
sounds.push(s);
|
|
4072
4077
|
|
|
4073
|
-
s.
|
|
4078
|
+
s.promise = (async () => {
|
|
4074
4079
|
let err;
|
|
4075
4080
|
try {
|
|
4076
4081
|
await s.load(url);
|
|
4077
4082
|
} catch (e) {
|
|
4078
4083
|
err = e;
|
|
4079
4084
|
}
|
|
4080
|
-
|
|
4081
|
-
delete s._loader;
|
|
4085
|
+
delete s.promise;
|
|
4082
4086
|
if (err) throw err;
|
|
4083
4087
|
if (cb) cb(s);
|
|
4084
4088
|
return s;
|
|
4085
4089
|
})();
|
|
4090
|
+
$._preloadPromises.push(s.promise);
|
|
4086
4091
|
|
|
4087
|
-
if (!$._usePreload) return s.
|
|
4092
|
+
if (!$._usePreload) return s.promise;
|
|
4088
4093
|
return s;
|
|
4089
4094
|
};
|
|
4090
4095
|
|
|
4091
4096
|
$.loadAudio = (url, cb) => {
|
|
4092
|
-
q._preloadCount++;
|
|
4093
4097
|
let a = new Audio(url);
|
|
4094
4098
|
a.crossOrigin = 'Anonymous';
|
|
4095
|
-
a.
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
a.
|
|
4104
|
-
|
|
4105
|
-
};
|
|
4106
|
-
a.addEventListener('suspend', preloadSkip);
|
|
4107
|
-
a.addEventListener('error', (e) => {
|
|
4108
|
-
preloadSkip();
|
|
4109
|
-
throw e;
|
|
4099
|
+
a.promise = new Promise((resolve, reject) => {
|
|
4100
|
+
a.addEventListener('canplaythrough', () => {
|
|
4101
|
+
if (!a.loaded) {
|
|
4102
|
+
a.loaded = true;
|
|
4103
|
+
if (cb) cb(a);
|
|
4104
|
+
resolve(a);
|
|
4105
|
+
}
|
|
4106
|
+
});
|
|
4107
|
+
a.addEventListener('suspend', resolve);
|
|
4108
|
+
a.addEventListener('error', reject);
|
|
4110
4109
|
});
|
|
4110
|
+
$._preloadPromises.push(a.promise);
|
|
4111
|
+
|
|
4112
|
+
if (!$._usePreload) return a.promise;
|
|
4111
4113
|
return a;
|
|
4112
4114
|
};
|
|
4113
4115
|
|
|
@@ -4273,9 +4275,8 @@ Q5.Sound = class {
|
|
|
4273
4275
|
};
|
|
4274
4276
|
Q5.modules.util = ($, q) => {
|
|
4275
4277
|
$._loadFile = (url, cb, type) => {
|
|
4276
|
-
q._preloadCount++;
|
|
4277
4278
|
let ret = {};
|
|
4278
|
-
ret.
|
|
4279
|
+
ret.promise = new Promise((resolve, reject) => {
|
|
4279
4280
|
fetch(url)
|
|
4280
4281
|
.then((res) => {
|
|
4281
4282
|
if (!res.ok) {
|
|
@@ -4289,9 +4290,8 @@ Q5.modules.util = ($, q) => {
|
|
|
4289
4290
|
if (type == 'csv') f = $.CSV.parse(f);
|
|
4290
4291
|
if (typeof f == 'string') ret.text = f;
|
|
4291
4292
|
else Object.assign(ret, f);
|
|
4292
|
-
delete ret.
|
|
4293
|
+
delete ret.promise;
|
|
4293
4294
|
if (cb) cb(f);
|
|
4294
|
-
q._preloadCount--;
|
|
4295
4295
|
resolve(f);
|
|
4296
4296
|
});
|
|
4297
4297
|
});
|
|
@@ -4302,14 +4302,15 @@ Q5.modules.util = ($, q) => {
|
|
|
4302
4302
|
$.loadJSON = (url, cb) => $._loadFile(url, cb, 'json');
|
|
4303
4303
|
$.loadCSV = (url, cb) => $._loadFile(url, cb, 'csv');
|
|
4304
4304
|
|
|
4305
|
-
const imgRegex = /(jpe?g|png|gif|webp|avif|svg)
|
|
4306
|
-
fontRegex = /(ttf|otf|woff2?|eot|json)
|
|
4307
|
-
|
|
4305
|
+
const imgRegex = /(jpe?g|png|gif|webp|avif|svg)/i,
|
|
4306
|
+
fontRegex = /(ttf|otf|woff2?|eot|json)/i,
|
|
4307
|
+
fontCategoryRegex = /(serif|sans-serif|monospace|cursive|fantasy)/i,
|
|
4308
|
+
audioRegex = /(wav|flac|mp3|ogg|m4a|aac|aiff|weba)/i;
|
|
4308
4309
|
|
|
4309
4310
|
$.load = function (...urls) {
|
|
4310
4311
|
if (Array.isArray(urls[0])) urls = urls[0];
|
|
4311
4312
|
|
|
4312
|
-
let
|
|
4313
|
+
let promises = [];
|
|
4313
4314
|
|
|
4314
4315
|
for (let url of urls) {
|
|
4315
4316
|
let ext = url.split('.').pop().toLowerCase();
|
|
@@ -4321,18 +4322,18 @@ Q5.modules.util = ($, q) => {
|
|
|
4321
4322
|
obj = $.loadCSV(url);
|
|
4322
4323
|
} else if (imgRegex.test(ext)) {
|
|
4323
4324
|
obj = $.loadImage(url);
|
|
4324
|
-
} else if (fontRegex.test(ext)) {
|
|
4325
|
+
} else if (fontRegex.test(ext) || fontCategoryRegex.test(url)) {
|
|
4325
4326
|
obj = $.loadFont(url);
|
|
4326
4327
|
} else if (audioRegex.test(ext)) {
|
|
4327
4328
|
obj = $.loadSound(url);
|
|
4328
4329
|
} else {
|
|
4329
4330
|
obj = $.loadText(url);
|
|
4330
4331
|
}
|
|
4331
|
-
|
|
4332
|
+
promises.push(obj.promise);
|
|
4332
4333
|
}
|
|
4333
4334
|
|
|
4334
|
-
if (urls.length == 1) return
|
|
4335
|
-
return Promise.all(
|
|
4335
|
+
if (urls.length == 1) return promises[0];
|
|
4336
|
+
return Promise.all(promises);
|
|
4336
4337
|
};
|
|
4337
4338
|
|
|
4338
4339
|
async function saveFile(data, name, ext) {
|
|
@@ -4837,6 +4838,8 @@ struct Q5 {
|
|
|
4837
4838
|
$._frameA = frameA = Q5.device.createTexture({ size, format, usage });
|
|
4838
4839
|
$._frameB = frameB = Q5.device.createTexture({ size, format, usage });
|
|
4839
4840
|
|
|
4841
|
+
$.canvas.texture = frameA;
|
|
4842
|
+
|
|
4840
4843
|
$._frameShaderCode =
|
|
4841
4844
|
$._baseShaderCode +
|
|
4842
4845
|
/* wgsl */ `
|
|
@@ -5727,6 +5730,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5727
5730
|
$.rectMode = (x) => ($._rectMode = x);
|
|
5728
5731
|
|
|
5729
5732
|
$.rect = (x, y, w, h, rr = 0) => {
|
|
5733
|
+
h ??= w;
|
|
5730
5734
|
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
5731
5735
|
let ci, ti;
|
|
5732
5736
|
if ($._matrixDirty) $._saveMatrix();
|
|
@@ -6526,10 +6530,8 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6526
6530
|
};
|
|
6527
6531
|
|
|
6528
6532
|
$.loadImage = (src, cb) => {
|
|
6529
|
-
q._preloadCount++;
|
|
6530
6533
|
let g = $._g.loadImage(src, (img) => {
|
|
6531
6534
|
$._addTexture(img);
|
|
6532
|
-
q._preloadCount--;
|
|
6533
6535
|
if (cb) cb(g);
|
|
6534
6536
|
});
|
|
6535
6537
|
return g;
|
|
@@ -6703,7 +6705,8 @@ struct FragParams {
|
|
|
6703
6705
|
@location(0) texCoord : vec2f,
|
|
6704
6706
|
@location(1) fillColor : vec4f,
|
|
6705
6707
|
@location(2) strokeColor : vec4f,
|
|
6706
|
-
@location(3) strokeWeight : f32
|
|
6708
|
+
@location(3) strokeWeight : f32,
|
|
6709
|
+
@location(4) edge : f32
|
|
6707
6710
|
}
|
|
6708
6711
|
struct Char {
|
|
6709
6712
|
texOffset: vec2f,
|
|
@@ -6717,7 +6720,8 @@ struct Text {
|
|
|
6717
6720
|
matrixIndex: f32,
|
|
6718
6721
|
fillIndex: f32,
|
|
6719
6722
|
strokeIndex: f32,
|
|
6720
|
-
strokeWeight: f32
|
|
6723
|
+
strokeWeight: f32,
|
|
6724
|
+
edge: f32
|
|
6721
6725
|
}
|
|
6722
6726
|
|
|
6723
6727
|
@group(0) @binding(0) var<uniform> q: Q5;
|
|
@@ -6778,12 +6782,13 @@ fn vertexMain(v : VertexParams) -> FragParams {
|
|
|
6778
6782
|
f.fillColor = colors[i32(text.fillIndex)];
|
|
6779
6783
|
f.strokeColor = colors[i32(text.strokeIndex)];
|
|
6780
6784
|
f.strokeWeight = text.strokeWeight;
|
|
6785
|
+
f.edge = text.edge;
|
|
6781
6786
|
return f;
|
|
6782
6787
|
}
|
|
6783
6788
|
|
|
6784
6789
|
@fragment
|
|
6785
6790
|
fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
6786
|
-
let edge =
|
|
6791
|
+
let edge = f.edge;
|
|
6787
6792
|
let dist = calcDist(f.texCoord, edge);
|
|
6788
6793
|
|
|
6789
6794
|
if (f.strokeWeight == 0.0) {
|
|
@@ -6906,14 +6911,10 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
6906
6911
|
$._fonts = [];
|
|
6907
6912
|
let fonts = {};
|
|
6908
6913
|
|
|
6909
|
-
|
|
6910
|
-
q._preloadCount++;
|
|
6911
|
-
|
|
6914
|
+
async function createFont(fontJsonUrl, fontName, cb) {
|
|
6912
6915
|
let res = await fetch(fontJsonUrl);
|
|
6913
|
-
if (res.status == 404)
|
|
6914
|
-
|
|
6915
|
-
return '';
|
|
6916
|
-
}
|
|
6916
|
+
if (res.status == 404) return '';
|
|
6917
|
+
|
|
6917
6918
|
let atlas = await res.json();
|
|
6918
6919
|
|
|
6919
6920
|
let slashIdx = fontJsonUrl.lastIndexOf('/');
|
|
@@ -6994,31 +6995,32 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
6994
6995
|
$._fonts.push($._font);
|
|
6995
6996
|
fonts[fontName] = $._font;
|
|
6996
6997
|
|
|
6997
|
-
q._preloadCount--;
|
|
6998
|
-
|
|
6999
6998
|
if (cb) cb(fontName);
|
|
7000
|
-
}
|
|
6999
|
+
}
|
|
7001
7000
|
|
|
7002
7001
|
$.loadFont = (url, cb) => {
|
|
7003
7002
|
let ext = url.slice(url.lastIndexOf('.') + 1);
|
|
7003
|
+
if (url == ext) return $._loadDefaultFont(url, cb);
|
|
7004
7004
|
if (ext != 'json') return $._g.loadFont(url, cb);
|
|
7005
7005
|
let fontName = url.slice(url.lastIndexOf('/') + 1, url.lastIndexOf('-'));
|
|
7006
7006
|
let f = { family: fontName };
|
|
7007
|
-
f.
|
|
7008
|
-
delete f.
|
|
7007
|
+
f.promise = createFont(url, fontName, () => {
|
|
7008
|
+
delete f.promise;
|
|
7009
7009
|
if (cb) cb(f);
|
|
7010
7010
|
});
|
|
7011
|
-
|
|
7011
|
+
$._preloadPromises.push(f.promise);
|
|
7012
|
+
|
|
7013
|
+
if (!$._usePreload) return f.promise;
|
|
7012
7014
|
return f;
|
|
7013
7015
|
};
|
|
7014
7016
|
|
|
7015
|
-
$._loadDefaultFont = (fontName) => {
|
|
7017
|
+
$._loadDefaultFont = (fontName, cb) => {
|
|
7016
7018
|
fonts[fontName] = null;
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
$.loadFont(`/node_modules/q5/builtinFonts/${fontName}-msdf.json`);
|
|
7019
|
+
let url = `https://q5js.org/fonts/${fontName}-msdf.json`;
|
|
7020
|
+
if (!navigator.onLine) {
|
|
7021
|
+
url = `/node_modules/q5/builtinFonts/${fontName}-msdf.json`;
|
|
7021
7022
|
}
|
|
7023
|
+
return $.loadFont(url, cb);
|
|
7022
7024
|
};
|
|
7023
7025
|
|
|
7024
7026
|
$._textSize = 18;
|
|
@@ -7034,7 +7036,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7034
7036
|
if (typeof fontName != 'string') fontName = fontName.family;
|
|
7035
7037
|
let font = fonts[fontName];
|
|
7036
7038
|
if (font) $._font = font;
|
|
7037
|
-
else if (font === undefined) $._loadDefaultFont(fontName);
|
|
7039
|
+
else if (font === undefined) return $._loadDefaultFont(fontName);
|
|
7038
7040
|
};
|
|
7039
7041
|
|
|
7040
7042
|
$.textSize = (size) => {
|
|
@@ -7046,6 +7048,33 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7046
7048
|
}
|
|
7047
7049
|
};
|
|
7048
7050
|
|
|
7051
|
+
let weights = {
|
|
7052
|
+
thin: 100,
|
|
7053
|
+
extralight: 200,
|
|
7054
|
+
light: 300,
|
|
7055
|
+
normal: 400,
|
|
7056
|
+
regular: 400,
|
|
7057
|
+
medium: 500,
|
|
7058
|
+
semibold: 600,
|
|
7059
|
+
bold: 700,
|
|
7060
|
+
bolder: 800,
|
|
7061
|
+
extrabold: 800,
|
|
7062
|
+
black: 900,
|
|
7063
|
+
heavy: 900
|
|
7064
|
+
};
|
|
7065
|
+
|
|
7066
|
+
// ranges from 0.35 (black) to 0.65 (thin)
|
|
7067
|
+
$._textEdge = 0.5;
|
|
7068
|
+
|
|
7069
|
+
$.textWeight = (weight) => {
|
|
7070
|
+
if (!weight) return $._textWeight;
|
|
7071
|
+
if (typeof weight == 'string') {
|
|
7072
|
+
weight = weights[weight.toLowerCase().replace(/[ _-]/g, '')];
|
|
7073
|
+
if (!weight) throw new Error(`Invalid font weight: ${weight}`);
|
|
7074
|
+
}
|
|
7075
|
+
$._textEdge = 0.6875 - weight * 0.000375;
|
|
7076
|
+
};
|
|
7077
|
+
|
|
7049
7078
|
$.textLeading = (lineHeight) => {
|
|
7050
7079
|
$._font.lineHeight = leading = lineHeight;
|
|
7051
7080
|
leadDiff = leading - $._textSize;
|
|
@@ -7204,12 +7233,12 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7204
7233
|
|
|
7205
7234
|
txt[0] = x;
|
|
7206
7235
|
txt[1] = -y;
|
|
7207
|
-
txt[2] = $._textSize /
|
|
7236
|
+
txt[2] = $._textSize / 42;
|
|
7208
7237
|
txt[3] = $._matrixIndex;
|
|
7209
7238
|
txt[4] = $._doFill && $._fillSet ? $._fill : 0;
|
|
7210
7239
|
txt[5] = $._stroke;
|
|
7211
7240
|
txt[6] = $._doStroke && $._strokeSet ? $._strokeWeight : 0;
|
|
7212
|
-
txt[7] =
|
|
7241
|
+
txt[7] = $._textEdge;
|
|
7213
7242
|
|
|
7214
7243
|
textStack.push(txt);
|
|
7215
7244
|
$._drawStack.push($._textPL, measurements.printedCharCount, $._font.index);
|