q5 2.9.20 → 2.9.22

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/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
  }
@@ -314,6 +314,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
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!');
@@ -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();
@@ -410,6 +412,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
410
412
  }
411
413
 
412
414
  $._blendMode = 'normal';
415
+
413
416
  $.blendMode = (mode) => {
414
417
  if (mode == $._blendMode) return;
415
418
  if (mode == 'source-over') mode = 'normal';
@@ -554,10 +557,7 @@ Q5.initWebGPU = async () => {
554
557
  Q5.webgpu = async function (scope, parent) {
555
558
  if (!scope || scope == 'global') Q5._hasGlobal = true;
556
559
  if (!(await Q5.initWebGPU())) {
557
- let q = new Q5(scope, parent);
558
- q.colorMode('rgb', 1);
559
- q._beginRender = () => q.translate(q.canvas.hw, q.canvas.hh);
560
- return q;
560
+ return new Q5(scope, parent, 'webgpu-fallback');
561
561
  }
562
562
  return new Q5(scope, parent, 'webgpu');
563
563
  };
@@ -322,10 +322,12 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
322
322
 
323
323
  let shapeVertCount;
324
324
  let sv = []; // shape vertices
325
+ let curveVertices = []; // curve vertices
325
326
 
326
327
  $.beginShape = () => {
327
328
  shapeVertCount = 0;
328
329
  sv = [];
330
+ curveVertices = [];
329
331
  };
330
332
 
331
333
  $.vertex = (x, y) => {
@@ -334,12 +336,58 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
334
336
  shapeVertCount++;
335
337
  };
336
338
 
339
+ $.curveVertex = (x, y) => {
340
+ if ($._matrixDirty) $._saveMatrix();
341
+ curveVertices.push({ x: x, y: -y });
342
+ };
343
+
337
344
  $.endShape = (close) => {
345
+ if (curveVertices.length > 0) {
346
+ // Duplicate start and end points if necessary
347
+ let points = [...curveVertices];
348
+ if (points.length < 4) {
349
+ // Duplicate first and last points
350
+ while (points.length < 4) {
351
+ points.unshift(points[0]);
352
+ points.push(points[points.length - 1]);
353
+ }
354
+ }
355
+
356
+ for (let i = 0; i < points.length - 3; i++) {
357
+ let p0 = points[i];
358
+ let p1 = points[i + 1];
359
+ let p2 = points[i + 2];
360
+ let p3 = points[i + 3];
361
+
362
+ for (let t = 0; t <= 1; t += 0.1) {
363
+ let t2 = t * t;
364
+ let t3 = t2 * t;
365
+
366
+ let x =
367
+ 0.5 *
368
+ (2 * p1.x +
369
+ (-p0.x + p2.x) * t +
370
+ (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
371
+ (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
372
+
373
+ let y =
374
+ 0.5 *
375
+ (2 * p1.y +
376
+ (-p0.y + p2.y) * t +
377
+ (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
378
+ (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
379
+
380
+ sv.push(x, y, $._fill, $._transformIndex);
381
+ shapeVertCount++;
382
+ }
383
+ }
384
+ }
385
+
338
386
  if (shapeVertCount < 3) {
339
387
  throw new Error('A shape must have at least 3 vertices.');
340
388
  }
341
389
 
342
- // close the stroke if required
390
+ // Close the shape if needed
343
391
  if (close) {
344
392
  let firstIndex = 0;
345
393
  let lastIndex = (shapeVertCount - 1) * 4;
@@ -350,14 +398,13 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
350
398
  let lastY = sv[lastIndex + 1];
351
399
 
352
400
  if (firstX !== lastX || firstY !== lastY) {
353
- // append the first vertex to close the shape
354
401
  sv.push(firstX, firstY, sv[firstIndex + 2], sv[firstIndex + 3]);
355
402
  shapeVertCount++;
356
403
  }
357
404
  }
358
405
 
359
406
  if ($._doFill) {
360
- // triangulate the shape
407
+ // Triangulate the shape
361
408
  for (let i = 1; i < shapeVertCount - 1; i++) {
362
409
  let v0 = 0;
363
410
  let v1 = i * 4;
@@ -371,7 +418,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
371
418
  }
372
419
 
373
420
  if ($._doStroke) {
374
- // draw lines between vertices
421
+ // Draw lines between vertices
375
422
  for (let i = 0; i < shapeVertCount - 1; i++) {
376
423
  let v1 = i * 4;
377
424
  let v2 = (i + 1) * 4;
@@ -384,9 +431,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
384
431
  }
385
432
  }
386
433
 
387
- // reset for the next shape
434
+ // Reset for the next shape
388
435
  shapeVertCount = 0;
389
436
  sv = [];
437
+ curveVertices = [];
390
438
  };
391
439
 
392
440
  $.triangle = (x1, y1, x2, y2, x3, y3) => {
@@ -114,6 +114,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
114
114
  mipmapFilter: 'linear',
115
115
  maxAnisotropy: 16
116
116
  });
117
+
117
118
  let fontBindGroupLayout = Q5.device.createBindGroupLayout({
118
119
  label: 'MSDF font group layout',
119
120
  entries: [
@@ -151,6 +152,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
151
152
  primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' },
152
153
  multisample: { count: 4 }
153
154
  };
155
+
154
156
  $._pipelines[2] = Q5.device.createRenderPipeline($._pipelineConfigs[2]);
155
157
 
156
158
  class MsdfFont {
@@ -299,6 +301,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
299
301
  $.textFont = (fontName) => {
300
302
  $._font = fonts[fontName];
301
303
  };
304
+
302
305
  $.textSize = (size) => {
303
306
  $._textSize = size;
304
307
  if (!leadingSet) {
@@ -306,12 +309,14 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
306
309
  leadDiff = leading - size;
307
310
  }
308
311
  };
312
+
309
313
  $.textLeading = (lineHeight) => {
310
314
  $._font.lineHeight = leading = lineHeight;
311
315
  leadDiff = leading - $._textSize;
312
316
  leadPercent = leading / $._textSize;
313
317
  leadingSet = true;
314
318
  };
319
+
315
320
  $.textAlign = (horiz, vert) => {
316
321
  $._textAlign = horiz;
317
322
  if (vert) $._textBaseline = vert;