q5 2.9.21 → 2.9.23

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