q5 2.14.3 → 2.14.5

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 CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="q5js_logo.webp" height="64"> <img src="q5js_brand.webp" height="64">
1
+ # <img src="https://q5js.org/q5js_logo.webp" height="64"> <img src="https://q5js.org/q5js_brand.webp" height="64">
2
2
 
3
3
  [**q5.js**](https://q5js.org) is a spiritual successor to the [p5.js][] and [Processing Java][] graphics libraries.
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5",
3
- "version": "2.14.3",
3
+ "version": "2.14.5",
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) {
@@ -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,6 +3817,8 @@ 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
 
@@ -4146,6 +4155,8 @@ Q5.renderers.webgpu.drawing = ($, q) => {
4146
4155
  drawStack = $.drawStack,
4147
4156
  vertexStack = new Float32Array(1e7),
4148
4157
  vertIndex = 0;
4158
+ const TAU = Math.PI * 2;
4159
+ const HALF_PI = Math.PI / 2;
4149
4160
 
4150
4161
  let drawingShader = Q5.device.createShaderModule({
4151
4162
  label: 'drawingShader',
@@ -4259,10 +4270,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4259
4270
  drawStack.push(0, 4);
4260
4271
  };
4261
4272
 
4262
- const addEllipse = (x, y, a, b, n, ci, ti) => {
4263
- y = -y;
4264
- let t = 0,
4265
- 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;
4266
4277
 
4267
4278
  let v = vertexStack,
4268
4279
  i = vertIndex;
@@ -4287,27 +4298,14 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4287
4298
  t += angleIncrement;
4288
4299
  }
4289
4300
 
4290
- // close the triangle strip
4291
- // add center vertex
4292
- v[i++] = x;
4293
- v[i++] = y;
4294
- v[i++] = ci;
4295
- v[i++] = ti;
4296
-
4297
- // add first perimeter vertex
4298
- v[i++] = x + a;
4299
- v[i++] = y;
4300
- v[i++] = ci;
4301
- v[i++] = ti;
4302
-
4303
4301
  vertIndex = i;
4304
- drawStack.push(0, (n + 1) * 2 + 2);
4302
+ drawStack.push(0, (n + 1) * 2);
4305
4303
  };
4306
4304
 
4307
- const addEllipseStroke = (x, y, outerA, outerB, innerA, innerB, n, ci, ti) => {
4308
- y = -y;
4309
- let angleIncrement = $.TAU / n;
4310
- 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;
4311
4309
 
4312
4310
  let v = vertexStack,
4313
4311
  i = vertIndex;
@@ -4336,45 +4334,106 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4336
4334
  }
4337
4335
 
4338
4336
  vertIndex = i;
4339
- drawStack.push(0, (n + 1) * 2); // Use triangle strip
4337
+ drawStack.push(0, (n + 1) * 2);
4340
4338
  };
4341
4339
 
4342
4340
  $.rectMode = (x) => ($._rectMode = x);
4343
4341
 
4344
- $.rect = (x, y, w, h) => {
4342
+ $.rect = (x, y, w, h, rr = 0) => {
4345
4343
  let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
4346
4344
  let ci, ti;
4347
4345
  if ($._matrixDirty) $._saveMatrix();
4348
4346
  ti = $._matrixIndex;
4349
4347
 
4350
- if ($._doFill) {
4351
- ci = $._fill;
4352
- 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;
4353
4379
  }
4354
4380
 
4355
- if ($._doStroke) {
4356
- ci = $._stroke;
4357
- let sw = $._strokeWeight / 2;
4381
+ l += rr;
4382
+ r -= rr;
4383
+ t -= rr;
4384
+ b += rr;
4358
4385
 
4359
- // Calculate stroke positions
4360
- let lsw = l - sw,
4361
- rsw = r + sw,
4362
- tsw = t + sw,
4363
- bsw = b - sw,
4364
- lpsw = l + sw,
4365
- rpsw = r - sw,
4366
- tpsw = t - sw,
4367
- bpsw = b + sw;
4386
+ // Clamp radius
4387
+ rr = Math.min(rr, Math.min(w, h) / 2);
4368
4388
 
4369
- addRect(lsw, tpsw, rsw, tpsw, rsw, tsw, lsw, tsw, ci, ti); // Top
4370
- addRect(lsw, bsw, rsw, bsw, rsw, bpsw, lsw, bpsw, ci, ti); // Bottom
4389
+ let n = getArcSegments(rr * $._scale);
4371
4390
 
4372
- // Adjust side strokes to avoid overlapping corners
4373
- tsw = t - sw;
4374
- bsw = b + sw;
4391
+ let trr = t + rr,
4392
+ brr = b - rr,
4393
+ lrr = l - rr,
4394
+ rrr = r + rr;
4395
+
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
+ }
4375
4408
 
4376
- addRect(lsw, tsw, lpsw, tsw, lpsw, bsw, lsw, bsw, ci, ti); // Left
4377
- addRect(rpsw, tsw, rsw, tsw, rsw, bsw, rpsw, bsw, ci, ti); // Right
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
4378
4437
  }
4379
4438
  };
4380
4439
 
@@ -4411,6 +4470,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4411
4470
  d < 2400 ? 90 :
4412
4471
  100;
4413
4472
 
4473
+ $._ellipseMode = Q5.CENTER;
4414
4474
  $.ellipseMode = (x) => ($._ellipseMode = x);
4415
4475
 
4416
4476
  $.ellipse = (x, y, w, h) => {
@@ -4422,17 +4482,70 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4422
4482
  let ti = $._matrixIndex;
4423
4483
 
4424
4484
  if ($._doFill) {
4425
- addEllipse(x, y, a, b, n, $._fill, ti);
4485
+ addArc(x, -y, a, b, 0, TAU, n, $._fill, ti);
4426
4486
  }
4427
4487
  if ($._doStroke) {
4428
4488
  let sw = $._strokeWeight / 2;
4429
4489
  // Draw the stroke as a ring using triangle strips
4430
- 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);
4431
4491
  }
4432
4492
  };
4433
4493
 
4434
4494
  $.circle = (x, y, d) => $.ellipse(x, y, d, d);
4435
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
+
4436
4549
  $.point = (x, y) => {
4437
4550
  if ($._matrixDirty) $._saveMatrix();
4438
4551
  let ti = $._matrixIndex,
@@ -4443,9 +4556,9 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4443
4556
  let [l, r, t, b] = $._calcBox(x, y, sw, sw, 'corner');
4444
4557
  addRect(l, t, r, t, r, b, l, b, ci, ti);
4445
4558
  } else {
4446
- let n = getArcSegments(sw);
4559
+ let n = getArcSegments($._scaledSW);
4447
4560
  sw /= 2;
4448
- addEllipse(x, y, sw, sw, n, ci, ti);
4561
+ addArc(x, -y, sw, sw, 0, TAU, n, ci, ti);
4449
4562
  }
4450
4563
  };
4451
4564
 
@@ -4460,7 +4573,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4460
4573
  let ti = $._matrixIndex,
4461
4574
  ci = $._stroke,
4462
4575
  sw = $._strokeWeight,
4463
- hsw = sw / 2;
4576
+ hsw = $._hsw;
4464
4577
 
4465
4578
  // calculate the direction vector and length
4466
4579
  let dx = x2 - x1,
@@ -4475,8 +4588,8 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4475
4588
 
4476
4589
  if ($._scaledSW > 2 && $._strokeJoin != 'none') {
4477
4590
  let n = getArcSegments($._scaledSW);
4478
- addEllipse(x1, y1, hsw, hsw, n, ci, ti);
4479
- addEllipse(x2, y2, hsw, hsw, n, ci, ti);
4591
+ addArc(x1, -y1, hsw, hsw, 0, TAU, n, ci, ti);
4592
+ addArc(x2, -y2, hsw, hsw, 0, TAU, n, ci, ti);
4480
4593
  }
4481
4594
  };
4482
4595
 
@@ -4513,6 +4626,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4513
4626
  }
4514
4627
  }
4515
4628
 
4629
+ // calculate catmull-rom spline curve points
4516
4630
  for (let i = 0; i < points.length - 3; i++) {
4517
4631
  let p0 = points[i];
4518
4632
  let p1 = points[i + 1];
@@ -4587,17 +4701,25 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4587
4701
  }
4588
4702
 
4589
4703
  if ($._doStroke) {
4704
+ let hsw = $._hsw,
4705
+ n = getArcSegments($._scaledSW),
4706
+ ti = $._matrixIndex,
4707
+ ogStrokeJoin = $._strokeJoin;
4708
+ $._strokeJoin = 'none';
4590
4709
  // draw lines between vertices
4591
4710
  for (let i = 0; i < shapeVertCount - 1; i++) {
4592
4711
  let v1 = i * 4;
4593
4712
  let v2 = (i + 1) * 4;
4594
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);
4595
4716
  }
4596
4717
  if (close) {
4597
4718
  let v1 = (shapeVertCount - 1) * 4;
4598
4719
  let v2 = 0;
4599
4720
  $.line(sv[v1], -sv[v1 + 1], sv[v2], -sv[v2 + 1]);
4600
4721
  }
4722
+ $._strokeJoin = ogStrokeJoin;
4601
4723
  }
4602
4724
 
4603
4725
  // reset for the next shape
@@ -4624,26 +4746,19 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4624
4746
  };
4625
4747
 
4626
4748
  $.background = (r, g, b, a) => {
4627
- let mi = $._matrixIndex;
4628
- $._matrixIndex = 0;
4629
- $._doStroke = false;
4749
+ $.push();
4750
+ $.resetMatrix();
4630
4751
  if (r.src) {
4631
4752
  let img = r;
4632
- let im = $._imageMode;
4633
4753
  $._imageMode = 'corner';
4634
4754
  $.image(img, -c.hw, -c.hh, c.w, c.h);
4635
- $._imageMode = im;
4636
4755
  } else {
4637
- let rm = $._rectMode;
4638
4756
  $._rectMode = 'corner';
4639
- let fill = $._fill;
4640
4757
  $.fill(r, g, b, a);
4758
+ $._doStroke = false;
4641
4759
  $.rect(-c.hw, -c.hh, c.w, c.h);
4642
- $._rectMode = rm;
4643
- $._fill = fill;
4644
4760
  }
4645
- $._doStroke = true;
4646
- $._matrixIndex = mi;
4761
+ $.pop();
4647
4762
  };
4648
4763
 
4649
4764
  $._hooks.preRender.push(() => {