q5 2.14.1 → 2.14.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5",
3
- "version": "2.14.1",
3
+ "version": "2.14.4",
4
4
  "description": "A sequel to p5.js that's optimized for interactive art",
5
5
  "author": "quinton-ashley",
6
6
  "contributors": [
package/q5.js CHANGED
@@ -151,8 +151,6 @@ function Q5(scope, parent, renderer) {
151
151
  $._elements = [];
152
152
  $.describe = () => {};
153
153
 
154
- $.TWO_PI = $.TAU = Math.PI * 2;
155
-
156
154
  $.log = $.print = console.log;
157
155
 
158
156
  for (let m in Q5.modules) {
@@ -260,7 +258,6 @@ function Q5(scope, parent, renderer) {
260
258
  $._setupDone = true;
261
259
  if ($.frameCount) return;
262
260
  if ($.ctx === null) $.createCanvas(200, 200);
263
- if ($.ctx) $.resetMatrix();
264
261
  raf($._draw);
265
262
  }
266
263
 
@@ -710,6 +707,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
710
707
  $.ctx.lineCap = 'round';
711
708
  $.ctx.lineJoin = 'miter';
712
709
  $.ctx.textAlign = 'left';
710
+ $._strokeWeight = 1;
713
711
  }
714
712
  $.ctx.scale($._pixelDensity, $._pixelDensity);
715
713
  $.ctx.save();
@@ -945,17 +943,18 @@ Q5.renderers.q2d.drawing = ($) => {
945
943
  }
946
944
  };
947
945
 
946
+ const TAU = Math.PI * 2;
947
+
948
948
  function arc(x, y, w, h, lo, hi, mode) {
949
949
  if ($._angleMode) {
950
950
  lo = $.radians(lo);
951
951
  hi = $.radians(hi);
952
952
  }
953
- let full = $.TAU;
954
- lo %= full;
955
- hi %= full;
956
- if (lo < 0) lo += full;
957
- if (hi < 0) hi += full;
958
- if (lo > hi) hi += full;
953
+ lo %= TAU;
954
+ hi %= TAU;
955
+ if (lo < 0) lo += TAU;
956
+ if (hi < 0) hi += TAU;
957
+ if (lo > hi) hi += TAU;
959
958
  if (lo == hi) return $.ellipse(x, y, w, h);
960
959
 
961
960
  w /= 2;
@@ -1002,7 +1001,7 @@ Q5.renderers.q2d.drawing = ($) => {
1002
1001
 
1003
1002
  function ellipse(x, y, w, h) {
1004
1003
  $.ctx.beginPath();
1005
- $.ctx.ellipse(x, y, w / 2, h / 2, 0, 0, $.TAU);
1004
+ $.ctx.ellipse(x, y, w / 2, h / 2, 0, 0, TAU);
1006
1005
  ink();
1007
1006
  }
1008
1007
 
@@ -1033,7 +1032,7 @@ Q5.renderers.q2d.drawing = ($) => {
1033
1032
  d *= $._da;
1034
1033
  }
1035
1034
  $.ctx.beginPath();
1036
- $.ctx.arc(x, y, d / 2, 0, $.TAU);
1035
+ $.ctx.arc(x, y, d / 2, 0, TAU);
1037
1036
  ink();
1038
1037
  } else $.ellipse(x, y, d, d);
1039
1038
  };
@@ -2750,6 +2749,7 @@ Q5.modules.math = ($, q) => {
2750
2749
  $.PI = Math.PI;
2751
2750
  $.HALF_PI = Math.PI / 2;
2752
2751
  $.QUARTER_PI = Math.PI / 4;
2752
+ $.TWO_PI = $.TAU = Math.PI * 2;
2753
2753
 
2754
2754
  $.abs = Math.abs;
2755
2755
  $.ceil = Math.ceil;
@@ -3742,7 +3742,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3742
3742
  $.noTint = () => ($._tint = 1);
3743
3743
 
3744
3744
  $._strokeWeight = 1;
3745
- $.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
3745
+ $._hsw = 0.5;
3746
+ $._scaledSW = 1;
3747
+ $.strokeWeight = (v) => {
3748
+ v = Math.abs(v);
3749
+ $._strokeWeight = v;
3750
+ $._scaledSW = v * $._scale;
3751
+ $._hsw = v / 2;
3752
+ };
3746
3753
 
3747
3754
  const MAX_TRANSFORMS = 1e7, // or whatever maximum you need
3748
3755
  MATRIX_SIZE = 16, // 4x4 matrix
@@ -3810,10 +3817,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3810
3817
  $._matrixDirty = true;
3811
3818
  };
3812
3819
 
3820
+ $._scale = 1;
3821
+
3813
3822
  $.scale = (x = 1, y, z = 1) => {
3814
3823
  y ??= x;
3815
3824
 
3816
3825
  $._scale = Math.max(Math.abs(x), Math.abs(y));
3826
+ $._scaledSW = $._strokeWeight * $._scale;
3817
3827
 
3818
3828
  let m = matrix;
3819
3829
 
@@ -4145,6 +4155,8 @@ Q5.renderers.webgpu.drawing = ($, q) => {
4145
4155
  drawStack = $.drawStack,
4146
4156
  vertexStack = new Float32Array(1e7),
4147
4157
  vertIndex = 0;
4158
+ const TAU = Math.PI * 2;
4159
+ const HALF_PI = Math.PI / 2;
4148
4160
 
4149
4161
  let drawingShader = Q5.device.createShaderModule({
4150
4162
  label: 'drawingShader',
@@ -4258,10 +4270,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4258
4270
  drawStack.push(0, 4);
4259
4271
  };
4260
4272
 
4261
- const addEllipse = (x, y, a, b, n, ci, ti) => {
4262
- y = -y;
4263
- let t = 0,
4264
- angleIncrement = $.TAU / n;
4273
+ const addArc = (x, y, a, b, startAngle, endAngle, n, ci, ti) => {
4274
+ let angleRange = endAngle - startAngle;
4275
+ let angleIncrement = angleRange / n;
4276
+ let t = startAngle;
4265
4277
 
4266
4278
  let v = vertexStack,
4267
4279
  i = vertIndex;
@@ -4286,27 +4298,14 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4286
4298
  t += angleIncrement;
4287
4299
  }
4288
4300
 
4289
- // close the triangle strip
4290
- // add center vertex
4291
- v[i++] = x;
4292
- v[i++] = y;
4293
- v[i++] = ci;
4294
- v[i++] = ti;
4295
-
4296
- // add first perimeter vertex
4297
- v[i++] = x + a;
4298
- v[i++] = y;
4299
- v[i++] = ci;
4300
- v[i++] = ti;
4301
-
4302
4301
  vertIndex = i;
4303
- drawStack.push(0, (n + 1) * 2 + 2);
4302
+ drawStack.push(0, (n + 1) * 2);
4304
4303
  };
4305
4304
 
4306
- const addEllipseStroke = (x, y, outerA, outerB, innerA, innerB, n, ci, ti) => {
4307
- y = -y;
4308
- let angleIncrement = $.TAU / n;
4309
- let t = 0;
4305
+ const addArcStroke = (x, y, outerA, outerB, innerA, innerB, startAngle, endAngle, n, ci, ti) => {
4306
+ let angleRange = endAngle - startAngle;
4307
+ let angleIncrement = angleRange / n;
4308
+ let t = startAngle;
4310
4309
 
4311
4310
  let v = vertexStack,
4312
4311
  i = vertIndex;
@@ -4335,45 +4334,106 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4335
4334
  }
4336
4335
 
4337
4336
  vertIndex = i;
4338
- drawStack.push(0, (n + 1) * 2); // Use triangle strip
4337
+ drawStack.push(0, (n + 1) * 2);
4339
4338
  };
4340
4339
 
4341
4340
  $.rectMode = (x) => ($._rectMode = x);
4342
4341
 
4343
- $.rect = (x, y, w, h) => {
4342
+ $.rect = (x, y, w, h, rr = 0) => {
4344
4343
  let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
4345
4344
  let ci, ti;
4346
4345
  if ($._matrixDirty) $._saveMatrix();
4347
4346
  ti = $._matrixIndex;
4348
4347
 
4349
- if ($._doFill) {
4350
- ci = $._fill;
4351
- addRect(l, t, r, t, r, b, l, b, ci, ti);
4348
+ if (!rr) {
4349
+ if ($._doFill) {
4350
+ ci = $._fill;
4351
+ addRect(l, t, r, t, r, b, l, b, ci, ti);
4352
+ }
4353
+
4354
+ if ($._doStroke) {
4355
+ ci = $._stroke;
4356
+ let sw = $._strokeWeight / 2;
4357
+
4358
+ // Calculate stroke positions
4359
+ let lsw = l - sw,
4360
+ rsw = r + sw,
4361
+ tsw = t + sw,
4362
+ bsw = b - sw,
4363
+ lpsw = l + sw,
4364
+ rpsw = r - sw,
4365
+ tpsw = t - sw,
4366
+ bpsw = b + sw;
4367
+
4368
+ addRect(lsw, tpsw, rsw, tpsw, rsw, tsw, lsw, tsw, ci, ti); // Top
4369
+ addRect(lsw, bsw, rsw, bsw, rsw, bpsw, lsw, bpsw, ci, ti); // Bottom
4370
+
4371
+ // Adjust side strokes to avoid overlapping corners
4372
+ tsw = t - sw;
4373
+ bsw = b + sw;
4374
+
4375
+ addRect(lsw, tsw, lpsw, tsw, lpsw, bsw, lsw, bsw, ci, ti); // Left
4376
+ addRect(rpsw, tsw, rsw, tsw, rsw, bsw, rpsw, bsw, ci, ti); // Right
4377
+ }
4378
+ return;
4352
4379
  }
4353
4380
 
4354
- if ($._doStroke) {
4355
- ci = $._stroke;
4356
- let sw = $._strokeWeight / 2;
4381
+ l += rr;
4382
+ r -= rr;
4383
+ t -= rr;
4384
+ b += rr;
4357
4385
 
4358
- // Calculate stroke positions
4359
- let lsw = l - sw,
4360
- rsw = r + sw,
4361
- tsw = t + sw,
4362
- bsw = b - sw,
4363
- lpsw = l + sw,
4364
- rpsw = r - sw,
4365
- tpsw = t - sw,
4366
- bpsw = b + sw;
4386
+ // Clamp radius
4387
+ rr = Math.min(rr, Math.min(w, h) / 2);
4367
4388
 
4368
- addRect(lsw, tpsw, rsw, tpsw, rsw, tsw, lsw, tsw, ci, ti); // Top
4369
- addRect(lsw, bsw, rsw, bsw, rsw, bpsw, lsw, bpsw, ci, ti); // Bottom
4389
+ let n = getArcSegments(rr * $._scale);
4370
4390
 
4371
- // Adjust side strokes to avoid overlapping corners
4372
- tsw = t - sw;
4373
- bsw = b + sw;
4391
+ let trr = t + rr,
4392
+ brr = b - rr,
4393
+ lrr = l - rr,
4394
+ rrr = r + rr;
4374
4395
 
4375
- addRect(lsw, tsw, lpsw, tsw, lpsw, bsw, lsw, bsw, ci, ti); // Left
4376
- addRect(rpsw, tsw, rsw, tsw, rsw, bsw, rpsw, bsw, ci, ti); // Right
4396
+ if ($._doFill) {
4397
+ ci = $._fill;
4398
+ // Corner arcs
4399
+ addArc(r, b, rr, rr, -HALF_PI, 0, n, ci, ti);
4400
+ addArc(l, b, rr, rr, -Math.PI, -HALF_PI, n, ci, ti);
4401
+ addArc(l, t, rr, rr, Math.PI, HALF_PI, n, ci, ti);
4402
+ addArc(r, t, rr, rr, 0, HALF_PI, n, ci, ti);
4403
+
4404
+ addRect(l, trr, r, trr, r, brr, l, brr, ci, ti); // center
4405
+ addRect(l, t, lrr, t, lrr, b, l, b, ci, ti); // Left
4406
+ addRect(rrr, t, r, t, r, b, rrr, b, ci, ti); // Right
4407
+ }
4408
+
4409
+ if ($._doStroke) {
4410
+ ci = $._stroke;
4411
+ let hsw = $._hsw;
4412
+
4413
+ let outerA = rr + hsw,
4414
+ outerB = rr + hsw,
4415
+ innerA = rr - hsw,
4416
+ innerB = rr - hsw;
4417
+ // Corner arc strokes
4418
+ addArcStroke(r, b, outerA, outerB, innerA, innerB, -HALF_PI, 0, n, ci, ti);
4419
+ addArcStroke(l, b, outerA, outerB, innerA, innerB, -Math.PI, -HALF_PI, n, ci, ti);
4420
+ addArcStroke(l, t, outerA, outerB, innerA, innerB, Math.PI, HALF_PI, n, ci, ti);
4421
+ addArcStroke(r, t, outerA, outerB, innerA, innerB, 0, HALF_PI, n, ci, ti);
4422
+
4423
+ let lrrMin = lrr - hsw,
4424
+ lrrMax = lrr + hsw,
4425
+ rrrMin = rrr - hsw,
4426
+ rrrMax = rrr + hsw,
4427
+ trrMin = trr - hsw,
4428
+ trrMax = trr + hsw,
4429
+ brrMin = brr - hsw,
4430
+ brrMax = brr + hsw;
4431
+
4432
+ // Side strokes - positioned outside
4433
+ addRect(lrrMin, t, lrrMax, t, lrrMax, b, lrrMin, b, ci, ti); // Left
4434
+ addRect(rrrMin, t, rrrMax, t, rrrMax, b, rrrMin, b, ci, ti); // Right
4435
+ addRect(l, trrMin, r, trrMin, r, trrMax, l, trrMax, ci, ti); // Top
4436
+ addRect(l, brrMin, r, brrMin, r, brrMax, l, brrMax, ci, ti); // Bottom
4377
4437
  }
4378
4438
  };
4379
4439
 
@@ -4410,6 +4470,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4410
4470
  d < 2400 ? 90 :
4411
4471
  100;
4412
4472
 
4473
+ $._ellipseMode = Q5.CENTER;
4413
4474
  $.ellipseMode = (x) => ($._ellipseMode = x);
4414
4475
 
4415
4476
  $.ellipse = (x, y, w, h) => {
@@ -4421,30 +4482,83 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4421
4482
  let ti = $._matrixIndex;
4422
4483
 
4423
4484
  if ($._doFill) {
4424
- addEllipse(x, y, a, b, n, $._fill, ti);
4485
+ addArc(x, -y, a, b, 0, TAU, n, $._fill, ti);
4425
4486
  }
4426
4487
  if ($._doStroke) {
4427
4488
  let sw = $._strokeWeight / 2;
4428
4489
  // Draw the stroke as a ring using triangle strips
4429
- addEllipseStroke(x, y, a + sw, b + sw, a - sw, b - sw, n, $._stroke, ti);
4490
+ addArcStroke(x, -y, a + sw, b + sw, a - sw, b - sw, 0, TAU, n, $._stroke, ti);
4430
4491
  }
4431
4492
  };
4432
4493
 
4433
4494
  $.circle = (x, y, d) => $.ellipse(x, y, d, d);
4434
4495
 
4496
+ $.arc = (x, y, w, h, start, stop) => {
4497
+ if (start === stop) return $.ellipse(x, y, w, h);
4498
+
4499
+ // Convert angles if needed
4500
+ if ($._angleMode) {
4501
+ start = $.radians(start);
4502
+ stop = $.radians(stop);
4503
+ }
4504
+
4505
+ // Normalize angles
4506
+ start %= TAU;
4507
+ stop %= TAU;
4508
+ if (start < 0) start += TAU;
4509
+ if (stop < 0) stop += TAU;
4510
+ if (start > stop) stop += TAU;
4511
+ if (start == stop) return $.ellipse(x, y, w, h);
4512
+
4513
+ // Calculate position based on ellipseMode
4514
+ let a, b;
4515
+ if ($._ellipseMode == $.CENTER) {
4516
+ a = w / 2;
4517
+ b = h / 2;
4518
+ } else if ($._ellipseMode == $.RADIUS) {
4519
+ a = w;
4520
+ b = h;
4521
+ } else if ($._ellipseMode == $.CORNER) {
4522
+ x += w / 2;
4523
+ y += h / 2;
4524
+ a = w / 2;
4525
+ b = h / 2;
4526
+ } else if ($._ellipseMode == $.CORNERS) {
4527
+ x = (x + w) / 2;
4528
+ y = (y + h) / 2;
4529
+ a = (w - x) / 2;
4530
+ b = (h - y) / 2;
4531
+ }
4532
+
4533
+ let ti = $._matrixIndex;
4534
+ if ($._matrixDirty) $._saveMatrix();
4535
+ let n = getArcSegments(Math.max(Math.abs(w), Math.abs(h)) * $._scale);
4536
+
4537
+ // Draw fill
4538
+ if ($._doFill) {
4539
+ addArc(x, -y, a, b, start, stop, n, $._fill, ti);
4540
+ }
4541
+
4542
+ // Draw stroke
4543
+ if ($._doStroke) {
4544
+ let sw = $._strokeWeight;
4545
+ addArcStroke(x, -y, a + sw, b + sw, a - sw, b - sw, start, stop, n, $._stroke, ti);
4546
+ }
4547
+ };
4548
+
4435
4549
  $.point = (x, y) => {
4436
4550
  if ($._matrixDirty) $._saveMatrix();
4437
4551
  let ti = $._matrixIndex,
4438
4552
  ci = $._stroke,
4439
4553
  sw = $._strokeWeight;
4440
4554
 
4441
- if (sw < 2) {
4555
+ if ($._scaledSW < 2) {
4442
4556
  let [l, r, t, b] = $._calcBox(x, y, sw, sw, 'corner');
4443
4557
  addRect(l, t, r, t, r, b, l, b, ci, ti);
4444
4558
  } else {
4445
- let n = getArcSegments(sw);
4559
+ let n = getArcSegments($._scaledSW);
4446
4560
  sw /= 2;
4447
- addEllipse(x, y, sw, sw, n, ci, ti);
4561
+ addArc(x, -y, sw, sw, 0, TAU, n, ci, ti);
4448
4562
  }
4449
4563
  };
4450
4564
 
@@ -4459,7 +4573,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4459
4573
  let ti = $._matrixIndex,
4460
4574
  ci = $._stroke,
4461
4575
  sw = $._strokeWeight,
4462
- hsw = sw / 2;
4576
+ hsw = $._hsw;
4463
4577
 
4464
4578
  // calculate the direction vector and length
4465
4579
  let dx = x2 - x1,
@@ -4472,10 +4586,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4472
4586
 
4473
4587
  addRect(x1 + px, -y1 - py, x1 - px, -y1 + py, x2 - px, -y2 + py, x2 + px, -y2 - py, ci, ti);
4474
4588
 
4475
- if (sw > 2 && $._strokeJoin != 'none') {
4476
- let n = getArcSegments(sw);
4477
- addEllipse(x1, y1, hsw, hsw, n, ci, ti);
4478
- addEllipse(x2, y2, hsw, hsw, n, ci, ti);
4589
+ if ($._scaledSW > 2 && $._strokeJoin != 'none') {
4590
+ let n = getArcSegments($._scaledSW);
4591
+ addArc(x1, -y1, hsw, hsw, 0, TAU, n, ci, ti);
4592
+ addArc(x2, -y2, hsw, hsw, 0, TAU, n, ci, ti);
4479
4593
  }
4480
4594
  };
4481
4595
 
@@ -4512,6 +4626,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4512
4626
  }
4513
4627
  }
4514
4628
 
4629
+ // calculate catmull-rom spline curve points
4515
4630
  for (let i = 0; i < points.length - 3; i++) {
4516
4631
  let p0 = points[i];
4517
4632
  let p1 = points[i + 1];
@@ -4586,17 +4701,25 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4586
4701
  }
4587
4702
 
4588
4703
  if ($._doStroke) {
4704
+ let hsw = $._hsw,
4705
+ n = getArcSegments($._scaledSW),
4706
+ ti = $._matrixIndex,
4707
+ ogStrokeJoin = $._strokeJoin;
4708
+ $._strokeJoin = 'none';
4589
4709
  // draw lines between vertices
4590
4710
  for (let i = 0; i < shapeVertCount - 1; i++) {
4591
4711
  let v1 = i * 4;
4592
4712
  let v2 = (i + 1) * 4;
4593
4713
  $.line(sv[v1], -sv[v1 + 1], sv[v2], -sv[v2 + 1]);
4714
+
4715
+ addArc(sv[v1], sv[v1 + 1], hsw, hsw, 0, TAU, n, $._stroke, ti);
4594
4716
  }
4595
4717
  if (close) {
4596
4718
  let v1 = (shapeVertCount - 1) * 4;
4597
4719
  let v2 = 0;
4598
4720
  $.line(sv[v1], -sv[v1 + 1], sv[v2], -sv[v2 + 1]);
4599
4721
  }
4722
+ $._strokeJoin = ogStrokeJoin;
4600
4723
  }
4601
4724
 
4602
4725
  // reset for the next shape
@@ -5240,8 +5363,9 @@ fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
5240
5363
  leadPercent = 1.25;
5241
5364
 
5242
5365
  $.textFont = (fontName) => {
5243
- $._font = fonts[fontName];
5244
- if ($._font === undefined) $._loadDefaultFont(fontName);
5366
+ let font = fonts[fontName];
5367
+ if (font) $._font = font;
5368
+ else if (font === undefined) $._loadDefaultFont(fontName);
5245
5369
  };
5246
5370
 
5247
5371
  $.textSize = (size) => {