q5 4.5.3 → 4.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/README.md +5 -3
- package/deno.json +1 -1
- package/package.json +1 -8
- package/q5.js +61 -35
- package/q5.min.js +2 -2
- package/q5.min.js.map +1 -1
package/README.md
CHANGED
|
@@ -17,13 +17,13 @@ circle(0, 0, 80);
|
|
|
17
17
|
|
|
18
18
|
## Documentation
|
|
19
19
|
|
|
20
|
-
The [q5 learn pages](https://q5js.org/learn)
|
|
20
|
+
The [q5 learn pages](https://q5js.org/learn) have interactive mini-examples.
|
|
21
21
|
|
|
22
22
|
See the [wiki](https://github.com/q5js/q5.js/wiki) for extended documentation.
|
|
23
23
|
|
|
24
24
|
## Support q5 💙
|
|
25
25
|
|
|
26
|
-
q5 is open source
|
|
26
|
+
q5 is open source. Anyone can use it for free under the terms of the LGPL. 🎉
|
|
27
27
|
|
|
28
28
|
We need your support though! If you enjoy using q5, please donate via [GitHub Sponsors](https://github.com/sponsors/quinton-ashley), [ko-fi](https://ko-fi.com/q5play), or [Patreon](https://www.patreon.com/q5play).
|
|
29
29
|
|
|
@@ -47,7 +47,9 @@ p5.js is licensed under the LGPLv2, small sections of p5' code directly copied i
|
|
|
47
47
|
|
|
48
48
|
q5 was inspired by the incredible work of [Ben Fry](https://benfry.com) and [Casey Reas](https://x.com/REAS) on Java [Processing](https://processingfoundation.org/) from 2001 to 2023, [Lauren McCarthy](http://lauren-mccarthy.com)'s work on [p5.js](https://p5js.org) from 2013 to 2019, and all contributors to these projects.
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Huge thanks to all the [q5 contributors](https://github.com/q5js/q5.js/graphs/contributors)!
|
|
51
|
+
|
|
52
|
+
@evanalulu, @Tezumie, @keturn, @ormaq, @bertubi, @RedWilly, @Dukemz, @LingDong-
|
|
51
53
|
|
|
52
54
|
WebGPU MSDF text rendering:
|
|
53
55
|
https://webgpu.github.io/webgpu-samples/?sample=textRenderingMsdf
|
package/deno.json
CHANGED
package/package.json
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "q5",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Beginner friendly graphics powered by WebGPU, optimized for interactive art!",
|
|
5
5
|
"author": "quinton-ashley",
|
|
6
|
-
"contributors": [
|
|
7
|
-
"evanalulu",
|
|
8
|
-
"Tezumie",
|
|
9
|
-
"ormaq",
|
|
10
|
-
"Dukemz",
|
|
11
|
-
"LingDong-"
|
|
12
|
-
],
|
|
13
6
|
"license": "LGPL-3.0-only",
|
|
14
7
|
"homepage": "https://q5js.org/home",
|
|
15
8
|
"types": "q5.d.ts",
|
package/q5.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* q5.js
|
|
3
3
|
* @version 4.5
|
|
4
4
|
* @author quinton-ashley
|
|
5
|
-
* @contributors evanalulu, Tezumie, ormaq, Dukemz, LingDong-
|
|
5
|
+
* @contributors evanalulu, Tezumie, keturn, ormaq, bertubi, RedWilly, Dukemz, LingDong-
|
|
6
6
|
* @license LGPL-3.0
|
|
7
7
|
* @class Q5
|
|
8
8
|
*/
|
|
@@ -393,6 +393,7 @@ Q5._esm = this === undefined;
|
|
|
393
393
|
|
|
394
394
|
Q5._instanceCount = 0;
|
|
395
395
|
Q5.instances = [];
|
|
396
|
+
Q5.errorTolerant = false;
|
|
396
397
|
Q5._friendlyError = (msg, func) => {
|
|
397
398
|
if (!Q5.disableFriendlyErrors) console.error(func + ': ' + msg);
|
|
398
399
|
};
|
|
@@ -9591,43 +9592,32 @@ const runPython = async function () {
|
|
|
9591
9592
|
}
|
|
9592
9593
|
|
|
9593
9594
|
const useWebGPU = !code.slice(0, code.indexOf('\n')).includes('C2D'),
|
|
9594
|
-
|
|
9595
|
+
q = useWebGPU ? await Q5.WebGPU() : new Q5();
|
|
9595
9596
|
|
|
9596
9597
|
// `window.Canvas` returns a promise that resolves when Q5 is ready
|
|
9597
9598
|
// but `q5py.Canvas` returns the renderer synchronously
|
|
9598
9599
|
// so to make Brython happy with `await Canvas()` we need to make it async
|
|
9599
|
-
const Canvas =
|
|
9600
|
-
|
|
9600
|
+
const Canvas = q.Canvas;
|
|
9601
|
+
q.Canvas = async (...a) => Canvas(...a);
|
|
9601
9602
|
|
|
9602
|
-
|
|
9603
|
+
// add a tab before each line of code to nest it inside the __run function
|
|
9604
|
+
// but not within triple-quoted strings
|
|
9605
|
+
code = code
|
|
9606
|
+
.split(/(\"\"\"[\s\S]*?\"\"\"|\'\'\'[\s\S]*?\'\'\')/g)
|
|
9607
|
+
.map((part, i) => (i % 2 === 0 ? part.replaceAll('\n', '\n\t') : part))
|
|
9608
|
+
.join('');
|
|
9603
9609
|
|
|
9604
9610
|
code = `
|
|
9605
|
-
async def __run():
|
|
9611
|
+
async def __run(q):
|
|
9606
9612
|
${code}
|
|
9607
9613
|
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
def _sync_and_call(fn):
|
|
9611
|
-
def _wrapper(*args):
|
|
9612
|
-
try:
|
|
9613
|
-
for var in _q5_state_vars:
|
|
9614
|
-
if hasattr(q5py, var):
|
|
9615
|
-
ns[var] = getattr(q5py, var)
|
|
9616
|
-
return fn(*args)
|
|
9617
|
-
except Exception as e:
|
|
9618
|
-
window._pyErr(_err())
|
|
9619
|
-
raise e
|
|
9620
|
-
return _wrapper
|
|
9621
|
-
|
|
9622
|
-
for fn_name in ["update", "draw", "mousePressed", "mouseReleased", "mouseMoved", "mouseDragged", "mouseClicked", "doubleClicked", "mouseWheel", "keyPressed", "keyReleased", "keyTyped", "touchStarted", "touchMoved", "touchEnded", "windowResized"]:
|
|
9623
|
-
if fn_name in locals():
|
|
9624
|
-
setattr(window, fn_name, _sync_and_call(locals()[fn_name]))
|
|
9614
|
+
_wrap_fns(q, locals(), ns)
|
|
9625
9615
|
`;
|
|
9626
9616
|
|
|
9627
9617
|
window._pyErr = (err, lineNum) => {
|
|
9628
9618
|
if (typeof err === 'string' && err.includes('Traceback')) {
|
|
9629
9619
|
let lines = err.split('\n');
|
|
9630
|
-
for (let i =
|
|
9620
|
+
for (let i = lines.length - 1; i > 0; i--) {
|
|
9631
9621
|
const match = lines[i].match(/File "<string>", line (\d+)/);
|
|
9632
9622
|
if (match) {
|
|
9633
9623
|
lineNum = parseInt(match[1]);
|
|
@@ -9639,6 +9629,9 @@ async def __run():
|
|
|
9639
9629
|
for (let j = 0; j < Math.min(2, lines.length); j++) {
|
|
9640
9630
|
lines[j] = lines[j].slice(indent.length);
|
|
9641
9631
|
}
|
|
9632
|
+
} else {
|
|
9633
|
+
let line = code.split('\n')[lineNum - 1].trim();
|
|
9634
|
+
lines.unshift(line, '');
|
|
9642
9635
|
}
|
|
9643
9636
|
err = lines.join('\n');
|
|
9644
9637
|
break;
|
|
@@ -9646,7 +9639,9 @@ async def __run():
|
|
|
9646
9639
|
}
|
|
9647
9640
|
}
|
|
9648
9641
|
|
|
9649
|
-
let file = scripts[0].src
|
|
9642
|
+
let file = scripts[0].src || scripts[0]['data-filename'] || 'sketch.py';
|
|
9643
|
+
file = file.split('/').at(-1);
|
|
9644
|
+
|
|
9650
9645
|
lineNum -= 2; // adjust for the wrapper code lines
|
|
9651
9646
|
if (Q5.friendlyError) Q5.friendlyError(file, lineNum, err);
|
|
9652
9647
|
else console.error(`Error in ${file} on line ${lineNum}:\n\n${err}`);
|
|
@@ -9663,41 +9658,72 @@ from browser import window, aio
|
|
|
9663
9658
|
import traceback
|
|
9664
9659
|
import io
|
|
9665
9660
|
|
|
9661
|
+
_state_vars = ["frameCount", "deltaTime", "width", "height", "halfWidth", "halfHeight", "windowWidth", "windowHeight", "mouseX", "mouseY", "pmouseX", "pmouseY", "movedX", "movedY", "mouseIsPressed", "mouseButton", "keyIsPressed", "key", "keyCode", "touches", "recording"]
|
|
9662
|
+
|
|
9663
|
+
_usr_fns = ["update", "draw", "postProcess", "mousePressed", "mouseReleased", "mouseMoved", "mouseDragged", "mouseClicked", "doubleClicked", "mouseWheel", "keyPressed", "keyReleased", "keyTyped", "touchStarted", "touchMoved", "touchEnded", "windowResized"]
|
|
9664
|
+
|
|
9666
9665
|
def _err():
|
|
9667
9666
|
f = io.StringIO()
|
|
9668
9667
|
traceback.print_exc(file=f)
|
|
9669
9668
|
return f.getvalue()
|
|
9670
9669
|
|
|
9671
|
-
|
|
9670
|
+
def _sync_state(q, ns):
|
|
9671
|
+
for var in _state_vars:
|
|
9672
|
+
if hasattr(q, var):
|
|
9673
|
+
ns[var] = getattr(q, var)
|
|
9674
|
+
|
|
9675
|
+
def _sync_and_call(q, fn, ns):
|
|
9676
|
+
def _wrapper(*args):
|
|
9677
|
+
try:
|
|
9678
|
+
_sync_state(q, ns)
|
|
9679
|
+
return fn(*args)
|
|
9680
|
+
except Exception as e:
|
|
9681
|
+
window._pyErr(_err(), None, q)
|
|
9682
|
+
if not window.Q5.errorTolerant: q.noLoop()
|
|
9683
|
+
return _wrapper
|
|
9684
|
+
|
|
9685
|
+
def _wrap_fns(q, locs, ns):
|
|
9686
|
+
for fn_name in _usr_fns:
|
|
9687
|
+
if fn_name in locs:
|
|
9688
|
+
setattr(q, fn_name, _sync_and_call(q, locs[fn_name], ns))
|
|
9689
|
+
|
|
9690
|
+
async def _run_py(q, code):
|
|
9672
9691
|
ns = globals().copy()
|
|
9673
9692
|
ns['ns'] = ns
|
|
9674
|
-
ns['
|
|
9693
|
+
ns['Q5'] = window.Q5
|
|
9675
9694
|
|
|
9676
|
-
for attr in dir(
|
|
9695
|
+
for attr in dir(q):
|
|
9677
9696
|
if not attr.startswith('_'):
|
|
9678
9697
|
try:
|
|
9679
|
-
ns[attr] = getattr(
|
|
9698
|
+
ns[attr] = getattr(q, attr)
|
|
9680
9699
|
except Exception:
|
|
9681
9700
|
pass
|
|
9682
9701
|
|
|
9702
|
+
_orig_Canvas = ns['Canvas']
|
|
9703
|
+
async def _canvas_wrapper(*args):
|
|
9704
|
+
result = await _orig_Canvas(*args)
|
|
9705
|
+
_sync_state(q, ns)
|
|
9706
|
+
return result
|
|
9707
|
+
ns['Canvas'] = ns['createCanvas'] = _canvas_wrapper
|
|
9708
|
+
|
|
9683
9709
|
try:
|
|
9684
9710
|
exec(code, ns)
|
|
9685
9711
|
except SyntaxError as e:
|
|
9686
|
-
return window._pyErr(_err(), e.lineno)
|
|
9712
|
+
return window._pyErr(_err(), e.lineno, q)
|
|
9687
9713
|
except Exception as e:
|
|
9688
|
-
return window._pyErr(_err())
|
|
9689
|
-
|
|
9714
|
+
return window._pyErr(_err(), 0, q)
|
|
9715
|
+
|
|
9690
9716
|
try:
|
|
9691
|
-
await ns["__run"]()
|
|
9717
|
+
await ns["__run"](q)
|
|
9692
9718
|
except Exception as e:
|
|
9693
|
-
window._pyErr(_err())
|
|
9719
|
+
window._pyErr(_err(), 0, q)
|
|
9694
9720
|
|
|
9695
9721
|
window._runPy = _run_py
|
|
9696
9722
|
`);
|
|
9697
9723
|
|
|
9698
9724
|
console.log = log;
|
|
9699
9725
|
|
|
9700
|
-
await window._runPy(
|
|
9726
|
+
await window._runPy(q, code);
|
|
9701
9727
|
};
|
|
9702
9728
|
|
|
9703
9729
|
if (typeof document == 'object') {
|