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/README.md +20 -18
- package/package.json +1 -1
- package/q5.js +196 -103
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +8 -2
- package/src/q5-2d-drawing.js +41 -83
- package/src/q5-2d-image.js +1 -0
- package/src/q5-2d-text.js +7 -0
- package/src/q5-canvas.js +6 -5
- package/src/q5-color.js +5 -0
- package/src/q5-core.js +11 -1
- package/src/q5-input.js +12 -0
- package/src/q5-math.js +10 -3
- package/src/q5-record.js +2 -0
- package/src/q5-vector.js +33 -0
- package/src/q5-webgpu-canvas.js +4 -4
- package/src/q5-webgpu-drawing.js +53 -5
- package/src/q5-webgpu-text.js +5 -0
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
|
}
|
package/src/q5-webgpu-canvas.js
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|
package/src/q5-webgpu-drawing.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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) => {
|
package/src/q5-webgpu-text.js
CHANGED
|
@@ -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;
|