q5 4.3.0 → 4.4.0

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 (5) hide show
  1. package/deno.json +1 -1
  2. package/package.json +1 -1
  3. package/q5.d.ts +50 -16
  4. package/q5.js +437 -109
  5. package/q5.min.js +2 -2
package/q5.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * q5.js
3
- * @version 4.3
3
+ * @version 4.4
4
4
  * @author quinton-ashley
5
5
  * @contributors evanalulu, Tezumie, ormaq, Dukemz, LingDong-
6
6
  * @license LGPL-3.0
@@ -490,7 +490,7 @@ if (typeof window == 'object') {
490
490
  window.addEventListener('pagehide', cleanup);
491
491
  } else global.window = 0;
492
492
 
493
- Q5.version = Q5.VERSION = '4.3';
493
+ Q5.version = Q5.VERSION = '4.4';
494
494
 
495
495
  if (typeof document == 'object') {
496
496
  document.addEventListener('DOMContentLoaded', () => {
@@ -6153,11 +6153,16 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6153
6153
  $.pop();
6154
6154
  } else {
6155
6155
  addColor(r, g, b, a);
6156
- let lx = -c.hw,
6156
+ let ci = colorIndex,
6157
+ lx = -c.hw,
6157
6158
  rx = c.hw,
6158
6159
  ty = -c.hh,
6159
6160
  by = c.hh;
6160
- addQuad(lx, ty, rx, ty, rx, by, lx, by, colorIndex, 0);
6161
+ addVert(lx, ty, ci, 0);
6162
+ addVert(rx, ty, ci, 0);
6163
+ addVert(lx, by, ci, 0);
6164
+ addVert(rx, by, ci, 0);
6165
+ drawStack.push(1, 4); // always use the default shapes pipeline
6161
6166
  }
6162
6167
  };
6163
6168
 
@@ -6197,7 +6202,15 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6197
6202
  shouldClear = false;
6198
6203
  };
6199
6204
 
6200
- let transformsBuffer, colorsBuffer, shapesVertBuff, imgVertBuff, charBuffer, textBuffer;
6205
+ let transformsBuffer,
6206
+ colorsBuffer,
6207
+ shapesVertBuff,
6208
+ imgVertBuff,
6209
+ polygonVertBuff,
6210
+ polyPtsBuffer,
6211
+ polyPtsBindGroup,
6212
+ charBuffer,
6213
+ textBuffer;
6201
6214
  let mainBindGroup, lastTransformsBuffer, lastColorsBuffer;
6202
6215
 
6203
6216
  $._render = () => {
@@ -6276,6 +6289,37 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6276
6289
 
6277
6290
  // prepare to render images and videos
6278
6291
 
6292
+ if (polygonVertIdx) {
6293
+ let polygonVertSize = polygonVertIdx * 4; // 4 bytes per float
6294
+ if (!polygonVertBuff || polygonVertBuff.size < polygonVertSize) {
6295
+ if (polygonVertBuff) polygonVertBuff.destroy();
6296
+ polygonVertBuff = Q5.device.createBuffer({
6297
+ size: polygonVertSize * 2,
6298
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
6299
+ });
6300
+ }
6301
+
6302
+ Q5.device.queue.writeBuffer(polygonVertBuff, 0, polygonVertStack.subarray(0, polygonVertIdx));
6303
+ $._pass.setVertexBuffer(2, polygonVertBuff);
6304
+
6305
+ if (polyPtsIdx) {
6306
+ let polyPtsSize = polyPtsIdx * 4;
6307
+ if (!polyPtsBuffer || polyPtsBuffer.size < polyPtsSize) {
6308
+ if (polyPtsBuffer) polyPtsBuffer.destroy();
6309
+ polyPtsBuffer = Q5.device.createBuffer({
6310
+ size: Math.max(polyPtsSize * 2, 64),
6311
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
6312
+ });
6313
+ }
6314
+ Q5.device.queue.writeBuffer(polyPtsBuffer, 0, polyPtsStack.subarray(0, polyPtsIdx));
6315
+
6316
+ polyPtsBindGroup = Q5.device.createBindGroup({
6317
+ layout: polygonBindGroupLayout,
6318
+ entries: [{ binding: 0, resource: { buffer: polyPtsBuffer } }]
6319
+ });
6320
+ }
6321
+ }
6322
+
6279
6323
  if (imgVertIdx) {
6280
6324
  $._pass.setPipeline($._pipelines[2]); // images pipeline
6281
6325
 
@@ -6374,6 +6418,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6374
6418
  let drawVertOffset = 0,
6375
6419
  imageVertOffset = 0,
6376
6420
  textCharOffset = 0,
6421
+ polygonVertOffset = 0,
6377
6422
  rectIdx = 0,
6378
6423
  ellipseIdx = 0,
6379
6424
  curPipelineIndex = -1;
@@ -6401,12 +6446,18 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6401
6446
  } else if (curPipelineIndex == 6) {
6402
6447
  pass.setIndexBuffer(ellipseIndexBuffer, 'uint16');
6403
6448
  pass.setBindGroup(1, ellipseBindGroup);
6449
+ } else if (curPipelineIndex == 7) {
6450
+ pass.setBindGroup(1, polyPtsBindGroup);
6404
6451
  } else if ($._customBindHandlers[curPipelineIndex]) {
6405
6452
  $._customBindHandlers[curPipelineIndex](pass);
6406
6453
  }
6407
6454
  }
6408
6455
 
6409
- if (curPipelineIndex == 6) {
6456
+ if (curPipelineIndex == 7) {
6457
+ // draw a polygon
6458
+ pass.draw(v, 1, polygonVertOffset, 0);
6459
+ polygonVertOffset += v;
6460
+ } else if (curPipelineIndex == 6) {
6410
6461
  // draw an ellipse
6411
6462
  pass.drawIndexed(18, v, 0, 0, ellipseIdx);
6412
6463
  ellipseIdx += v;
@@ -6483,6 +6534,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6483
6534
 
6484
6535
  // reset
6485
6536
  shapesVertIdx = 0;
6537
+ polygonVertIdx = 0;
6538
+ polyPtsIdx = 0;
6486
6539
  imgVertIdx = 0;
6487
6540
  // Remove video frames without creating new array
6488
6541
  if (vidFrames > 0) {
@@ -6633,19 +6686,16 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6633
6686
  };
6634
6687
 
6635
6688
  $.vertex = (x, y) => {
6636
- if (matrixDirty) saveMatrix();
6637
- sv.push(x, y, fillIdx, matrixIdx);
6689
+ sv.push(x, y, fillIdx);
6638
6690
  shapeVertCount++;
6639
6691
  };
6640
6692
 
6641
6693
  $.curveVertex = (x, y) => {
6642
- if (matrixDirty) saveMatrix();
6643
6694
  curveVertices.push({ x, y });
6644
6695
  };
6645
6696
 
6646
6697
  $.bezierVertex = function (cx1, cy1, cx2, cy2, x, y) {
6647
6698
  if (shapeVertCount === 0) throw new Error('Shape needs a vertex()');
6648
- if (matrixDirty) saveMatrix();
6649
6699
 
6650
6700
  // Get the last vertex as the starting point (P₀)
6651
6701
  let prevIndex = (shapeVertCount - 1) * 4;
@@ -6681,136 +6731,413 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6681
6731
  vy = mt3 * startY + 3 * mt2 * t * cy1 + 3 * mt * t2 * cy2 + t3 * y;
6682
6732
  }
6683
6733
 
6684
- sv.push(vx, vy, fillIdx, matrixIdx);
6734
+ sv.push(vx, vy, fillIdx);
6685
6735
  shapeVertCount++;
6686
6736
  }
6687
6737
  };
6688
6738
 
6689
6739
  $.quadraticVertex = (cx, cy, x, y) => $.bezierVertex(cx, cy, x, y);
6690
6740
 
6741
+ function addQuad(x1, y1, x2, y2, x3, y3, x4, y4, ci, ti) {
6742
+ addVert(x1, y1, ci, ti); // v0
6743
+ addVert(x2, y2, ci, ti); // v1
6744
+ addVert(x4, y4, ci, ti); // v3
6745
+ addVert(x3, y3, ci, ti); // v2
6746
+ drawStack.push(shapesPL, 4);
6747
+ }
6748
+
6749
+ $.plane = (x, y, w, h) => {
6750
+ h ??= w;
6751
+ let [l, r, t, b] = calcBox(x, y, w, h, 'center');
6752
+ if (matrixDirty) saveMatrix();
6753
+ addQuad(l, t, r, t, r, b, l, b, fillIdx, matrixIdx);
6754
+ };
6755
+
6756
+ $.curve = (x1, y1, x2, y2, x3, y3, x4, y4) => {
6757
+ $.beginShape();
6758
+ $.curveVertex(x1, y1);
6759
+ $.curveVertex(x2, y2);
6760
+ $.curveVertex(x3, y3);
6761
+ $.curveVertex(x4, y4);
6762
+ $.endShape();
6763
+ };
6764
+
6765
+ $.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
6766
+ $.beginShape();
6767
+ $.vertex(x1, y1);
6768
+ $.bezierVertex(x2, y2, x3, y3, x4, y4);
6769
+ $.endShape();
6770
+ };
6771
+
6772
+ /* POLYGONS */
6773
+
6774
+ let polygonPL = 7;
6775
+
6776
+ $._polygonShaderCode =
6777
+ $._baseShaderCode +
6778
+ /* wgsl */ `
6779
+ struct VertexParams {
6780
+ @builtin(vertex_index) vertexIndex : u32,
6781
+ @location(0) pos: vec2f,
6782
+ @location(1) polyStart: f32,
6783
+ @location(2) polyCount: f32,
6784
+ @location(3) fillIndex: f32,
6785
+ @location(4) strokeIndex: f32,
6786
+ @location(5) strokeWeight: f32,
6787
+ @location(6) matrixIndex: f32
6788
+ }
6789
+
6790
+ struct FragParams {
6791
+ @builtin(position) position: vec4f,
6792
+ @location(0) localPos: vec2f,
6793
+ @location(1) @interpolate(flat) polyStart: u32,
6794
+ @location(2) @interpolate(flat) polyCount: u32,
6795
+ @location(3) @interpolate(flat) fillIndex: f32,
6796
+ @location(4) @interpolate(flat) strokeIndex: f32,
6797
+ @location(5) @interpolate(flat) strokeWeight: f32,
6798
+ @location(6) @interpolate(flat) isClosed: f32
6799
+ }
6800
+
6801
+ @group(0) @binding(0) var<uniform> q: Q5;
6802
+ @group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
6803
+ @group(0) @binding(2) var<storage> colors : array<vec4f>;
6804
+
6805
+ @group(1) @binding(0) var<storage, read> polyPts: array<vec4f>;
6806
+
6807
+ fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
6808
+ var vert = vec4f(pos, 0.0, 1.0);
6809
+ vert = transforms[i32(matrixIndex)] * vert;
6810
+ vert.x /= q.halfWidth;
6811
+ vert.y /= q.halfHeight;
6812
+ return vert;
6813
+ }
6814
+
6815
+ fn getPolyColor(p: vec2f, start: u32, count: u32, fIdx: f32) -> vec4f {
6816
+ let uniformColor = colors[i32(fIdx)];
6817
+ if (uniformColor.a == 0.0) {
6818
+ return uniformColor;
6819
+ }
6820
+
6821
+ var sumWeight: f32 = 0.0;
6822
+ var sumColor = vec4f(0.0);
6823
+ for (var i: u32 = 0u; i < count; i = i + 1u) {
6824
+ let pt = polyPts[start + i];
6825
+ let d = distance(p, pt.xy);
6826
+ if (d < 0.1) {
6827
+ return colors[i32(pt.z)];
6828
+ }
6829
+ let w = 1.0 / (d * d * d);
6830
+ sumWeight += w;
6831
+ sumColor += colors[i32(pt.z)] * w;
6832
+ }
6833
+ return sumColor / sumWeight;
6834
+ }
6835
+
6836
+ fn sdPolygon(p: vec2f, start: u32, count: u32, isClosed: f32) -> f32 {
6837
+ var d: f32 = dot(p - polyPts[start].xy, p - polyPts[start].xy);
6838
+ var s: f32 = 1.0;
6839
+ var j: u32 = count - 1u;
6840
+ for (var i: u32 = 0u; i < count; i = i + 1u) {
6841
+ let vi = polyPts[start + i].xy;
6842
+ let vj = polyPts[start + j].xy;
6843
+ let e = vj - vi;
6844
+ let w = p - vi;
6845
+ let b = w - e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
6846
+ let bSq = dot(b, b);
6847
+ if (isClosed != 0.0 || i != 0u) {
6848
+ if (bSq < d) { d = bSq; }
6849
+ }
6850
+
6851
+ let condX = p.y >= vi.y;
6852
+ let condY = p.y < vj.y;
6853
+ let condZ = e.x * w.y > e.y * w.x;
6854
+ if ((condX && condY && condZ) || (!condX && !condY && !condZ)) {
6855
+ s = -s;
6856
+ }
6857
+ j = i;
6858
+ }
6859
+ if (isClosed == 0.0) {
6860
+ return sqrt(d);
6861
+ }
6862
+ return s * sqrt(d);
6863
+ }
6864
+
6865
+ @vertex
6866
+ fn vertexMain(v: VertexParams) -> FragParams {
6867
+ var f: FragParams;
6868
+
6869
+ // manually apply transform
6870
+ var vert = vec4f(v.pos, 0.0, 1.0);
6871
+ vert = transforms[i32(v.matrixIndex)] * vert;
6872
+ vert.x /= q.halfWidth;
6873
+ vert.y /= q.halfHeight;
6874
+
6875
+ f.position = vert;
6876
+ f.localPos = v.pos;
6877
+ f.polyStart = u32(v.polyStart + 0.1);
6878
+ f.polyCount = u32(abs(v.polyCount) + 0.1);
6879
+ f.isClosed = step(0.0, v.polyCount);
6880
+ f.fillIndex = v.fillIndex;
6881
+ f.strokeIndex = v.strokeIndex;
6882
+ f.strokeWeight = v.strokeWeight;
6883
+ return f;
6884
+ }
6885
+
6886
+ @fragment
6887
+ fn fragMain(f: FragParams) -> @location(0) vec4f {
6888
+ let dist = sdPolygon(f.localPos, f.polyStart, f.polyCount, f.isClosed);
6889
+ let fill = getPolyColor(f.localPos, f.polyStart, f.polyCount, f.fillIndex);
6890
+ let stroke = colors[i32(f.strokeIndex)];
6891
+
6892
+ let dpdx_d = dpdx(dist);
6893
+ let dpdy_d = dpdy(dist);
6894
+ let distGrad = sqrt(dpdx_d * dpdx_d + dpdy_d * dpdy_d);
6895
+ let aa = clamp(distGrad * 1.5, 0.001, 2.0);
6896
+
6897
+ let halfStroke = f.strokeWeight * 0.5;
6898
+
6899
+ var outFragColor: vec4f;
6900
+
6901
+ if (fill.a != 0.0 && f.strokeWeight == 0.0) {
6902
+ let fillAlpha = 1.0 - smoothstep(-aa, aa, dist);
6903
+ if (fillAlpha <= 0.0) { discard; }
6904
+ outFragColor = vec4f(fill.rgb, fill.a * fillAlpha);
6905
+ } else if (fill.a != 0.0) {
6906
+ let fillAlpha = 1.0 - smoothstep(-aa, aa, dist);
6907
+ let strokeDist = abs(dist) - halfStroke;
6908
+ let strokeAlphaMask = 1.0 - smoothstep(-aa, aa, strokeDist);
6909
+
6910
+ if (fillAlpha <= 0.0 && strokeAlphaMask <= 0.0) { discard; }
6911
+
6912
+ let sA = stroke.a * strokeAlphaMask;
6913
+ let fA = fill.a * fillAlpha;
6914
+ let outAlpha = sA + fA * (1.0 - sA);
6915
+ let outCol = stroke.rgb * sA + fill.rgb * fA * (1.0 - sA);
6916
+ outFragColor = vec4f(outCol / max(outAlpha, 1e-5), outAlpha);
6917
+ } else {
6918
+ let strokeDist = abs(dist) - halfStroke;
6919
+ let strokeAlpha = 1.0 - smoothstep(-aa, aa, strokeDist);
6920
+
6921
+ if (strokeAlpha <= 0.0) { discard; }
6922
+ outFragColor = vec4f(stroke.rgb, stroke.a * strokeAlpha);
6923
+ }
6924
+ return outFragColor;
6925
+ }
6926
+ `;
6927
+
6928
+ let polygonShader = Q5.device.createShaderModule({
6929
+ label: 'polygonShader',
6930
+ code: $._polygonShaderCode
6931
+ });
6932
+
6933
+ let polygonVertStack = new Float32Array($._isGraphics ? 1000 : 1e7),
6934
+ polygonVertIdx = 0;
6935
+ let polyPtsStack = new Float32Array($._isGraphics ? 1000 : 1e7),
6936
+ polyPtsIdx = 0;
6937
+
6938
+ let polygonVertBuffLayout = {
6939
+ arrayStride: 32, // 8 floats * 4 bytes
6940
+ attributes: [
6941
+ { format: 'float32x2', offset: 0, shaderLocation: 0 },
6942
+ { format: 'float32', offset: 8, shaderLocation: 1 },
6943
+ { format: 'float32', offset: 12, shaderLocation: 2 },
6944
+ { format: 'float32', offset: 16, shaderLocation: 3 },
6945
+ { format: 'float32', offset: 20, shaderLocation: 4 },
6946
+ { format: 'float32', offset: 24, shaderLocation: 5 },
6947
+ { format: 'float32', offset: 28, shaderLocation: 6 }
6948
+ ]
6949
+ };
6950
+
6951
+ let polygonBindGroupLayout = Q5.device.createBindGroupLayout({
6952
+ entries: [{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'read-only-storage' } }]
6953
+ });
6954
+
6955
+ let polygonPipelineLayout = Q5.device.createPipelineLayout({
6956
+ label: 'polygonPipelineLayout',
6957
+ bindGroupLayouts: [mainLayout, polygonBindGroupLayout]
6958
+ });
6959
+
6960
+ $._pipelineConfigs[7] = {
6961
+ label: 'polygonPipeline',
6962
+ layout: polygonPipelineLayout,
6963
+ vertex: { module: polygonShader, entryPoint: 'vertexMain', buffers: [null, null, polygonVertBuffLayout] },
6964
+ fragment: {
6965
+ module: polygonShader,
6966
+ entryPoint: 'fragMain',
6967
+ targets: [{ format: 'bgra8unorm', blend: $.blendConfigs['source-over'] }]
6968
+ },
6969
+ primitive: { topology: 'triangle-list' },
6970
+ multisample: { count: 4 }
6971
+ };
6972
+ $._pipelines[7] = Q5.device.createRenderPipeline($._pipelineConfigs[7]);
6973
+
6974
+ const addPolygonVert = (x, y, start, count, fIdx, sIdx, sWeight, mIdx) => {
6975
+ let v = polygonVertStack,
6976
+ i = polygonVertIdx;
6977
+ v[i++] = x;
6978
+ v[i++] = y;
6979
+ v[i++] = start;
6980
+ v[i++] = count;
6981
+ v[i++] = fIdx;
6982
+ v[i++] = sIdx;
6983
+ v[i++] = sWeight;
6984
+ v[i++] = mIdx;
6985
+ polygonVertIdx = i;
6986
+ };
6987
+
6691
6988
  $.endShape = (close) => {
6692
6989
  if (curveVertices.length > 0) {
6693
- // duplicate start and end points if necessary
6694
6990
  let points = [...curveVertices];
6695
6991
  if (points.length < 4) {
6696
- // duplicate first and last points
6697
6992
  while (points.length < 4) {
6698
6993
  points.unshift(points[0]);
6699
6994
  points.push(points[points.length - 1]);
6700
6995
  }
6701
6996
  }
6702
-
6703
- // Use curveSegments to determine step size
6704
6997
  let step = 1 / curveSegments;
6705
-
6706
- // calculate catmull-rom spline curve points
6707
6998
  for (let i = 0; i < points.length - 3; i++) {
6708
- let p0 = points[i];
6709
- let p1 = points[i + 1];
6710
- let p2 = points[i + 2];
6711
- let p3 = points[i + 3];
6712
-
6713
- for (let t = 0; t <= 1; t += step) {
6714
- let t2 = t * t;
6715
- let t3 = t2 * t;
6716
-
6999
+ let p0 = points[i],
7000
+ p1 = points[i + 1],
7001
+ p2 = points[i + 2],
7002
+ p3 = points[i + 3];
7003
+ let startT = i === 0 ? 0 : 1;
7004
+ for (let j = startT; j <= curveSegments; j++) {
7005
+ let t = j / curveSegments;
7006
+ let t2 = t * t,
7007
+ t3 = t2 * t;
6717
7008
  let x =
6718
7009
  0.5 *
6719
7010
  (2 * p1.x +
6720
7011
  (-p0.x + p2.x) * t +
6721
7012
  (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
6722
7013
  (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
6723
-
6724
7014
  let y =
6725
7015
  0.5 *
6726
7016
  (2 * p1.y +
6727
7017
  (-p0.y + p2.y) * t +
6728
7018
  (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
6729
7019
  (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
6730
-
6731
- sv.push(x, y, fillIdx, matrixIdx);
7020
+ sv.push(x, y, fillIdx);
6732
7021
  shapeVertCount++;
6733
7022
  }
6734
7023
  }
6735
7024
  }
6736
7025
 
6737
7026
  if (!shapeVertCount) return;
6738
- if (shapeVertCount == 1) return $.point(sv[0], sv[1]);
6739
- if (shapeVertCount == 2) return $.line(sv[0], sv[1], sv[4], sv[5]);
6740
-
6741
- // close the shape if requested
6742
- if (close) {
6743
- let firstIndex = 0;
6744
- let lastIndex = (shapeVertCount - 1) * 4;
6745
-
6746
- let firstX = sv[firstIndex];
6747
- let firstY = sv[firstIndex + 1];
6748
- let lastX = sv[lastIndex];
6749
- let lastY = sv[lastIndex + 1];
6750
-
6751
- if (firstX !== lastX || firstY !== lastY) {
6752
- sv.push(firstX, firstY, sv[firstIndex + 2], sv[firstIndex + 3]);
6753
- shapeVertCount++;
6754
- }
7027
+ if (shapeVertCount == 1) {
7028
+ $.point(sv[0], sv[1]);
7029
+ shapeVertCount = 0;
7030
+ sv = [];
7031
+ curveVertices = [];
7032
+ return;
7033
+ }
7034
+ if (shapeVertCount == 2) {
7035
+ $.line(sv[0], sv[1], sv[3], sv[4]);
7036
+ shapeVertCount = 0;
7037
+ sv = [];
7038
+ curveVertices = [];
7039
+ return;
6755
7040
  }
6756
7041
 
6757
- if (doFill) {
6758
- if (shapeVertCount == 5) {
6759
- // for quads, draw two triangles
6760
- addVert(sv[0], sv[1], sv[2], sv[3]); // v0
6761
- addVert(sv[4], sv[5], sv[6], sv[7]); // v1
6762
- addVert(sv[12], sv[13], sv[14], sv[15]); // v3
6763
- addVert(sv[8], sv[9], sv[10], sv[11]); // v2
6764
- drawStack.push(shapesPL, 4);
6765
- } else {
6766
- // triangulate the shape
6767
- for (let i = 1; i < shapeVertCount - 1; i++) {
6768
- let v0 = 0;
6769
- let v1 = i * 4;
6770
- let v2 = (i + 1) * 4;
6771
-
6772
- addVert(sv[v0], sv[v0 + 1], sv[v0 + 2], sv[v0 + 3]);
6773
- addVert(sv[v1], sv[v1 + 1], sv[v1 + 2], sv[v1 + 3]);
6774
- addVert(sv[v2], sv[v2 + 1], sv[v2 + 2], sv[v2 + 3]);
6775
- }
6776
- drawStack.push(shapesPL, (shapeVertCount - 2) * 3);
6777
- }
7042
+ if (matrixDirty) saveMatrix();
7043
+ let ti = matrixIdx;
7044
+
7045
+ let isAutoClosed = false;
7046
+ let isClosedPath = false;
7047
+ let firstX = sv[0],
7048
+ firstY = sv[1];
7049
+ let lastX = sv[(shapeVertCount - 1) * 3],
7050
+ lastY = sv[(shapeVertCount - 1) * 3 + 1];
7051
+
7052
+ if (firstX === lastX && firstY === lastY) {
7053
+ isClosedPath = true;
7054
+ } else if (close || doFill) {
7055
+ sv.push(firstX, firstY, sv[2]);
7056
+ shapeVertCount++;
7057
+ isAutoClosed = !close;
7058
+ isClosedPath = true;
6778
7059
  }
6779
7060
 
6780
- if (doStroke) {
6781
- // draw lines between vertices
6782
- for (let i = 0; i < shapeVertCount - 1; i++) {
6783
- let v1 = i * 4;
6784
- let v2 = (i + 1) * 4;
6785
- $.line(sv[v1], sv[v1 + 1], sv[v2], sv[v2 + 1]);
7061
+ let runSDF = shapeVertCount >= 3 && doStroke;
7062
+
7063
+ if (runSDF) {
7064
+ let fi = doFill ? fillIdx : 0,
7065
+ si = strokeIdx;
7066
+
7067
+ let polyStart = polyPtsIdx / 4,
7068
+ polyCount = isClosedPath ? shapeVertCount - 1 : shapeVertCount;
7069
+ for (let i = 0; i < polyCount; i++) {
7070
+ polyPtsStack[polyPtsIdx++] = sv[i * 3];
7071
+ polyPtsStack[polyPtsIdx++] = sv[i * 3 + 1];
7072
+ polyPtsStack[polyPtsIdx++] = sv[i * 3 + 2];
7073
+ polyPtsStack[polyPtsIdx++] = ti;
7074
+ }
7075
+
7076
+ let minX = Infinity,
7077
+ minY = Infinity,
7078
+ maxX = -Infinity,
7079
+ maxY = -Infinity;
7080
+ for (let i = 0; i < shapeVertCount; i++) {
7081
+ let vx = sv[i * 3],
7082
+ vy = sv[i * 3 + 1];
7083
+ if (vx < minX) minX = vx;
7084
+ if (vx > maxX) maxX = vx;
7085
+ if (vy < minY) minY = vy;
7086
+ if (vy > maxY) maxY = vy;
7087
+ }
7088
+ let padding = sw * 0.5 + 1.0; // padding for stroke and AA
7089
+ minX -= padding;
7090
+ minY -= padding;
7091
+ maxX += padding;
7092
+ maxY += padding;
7093
+
7094
+ let passedCount = isClosedPath ? polyCount : -polyCount;
7095
+ addPolygonVert(minX, minY, polyStart, passedCount, fi, si, sw, ti);
7096
+ addPolygonVert(maxX, minY, polyStart, passedCount, fi, si, sw, ti);
7097
+ addPolygonVert(minX, maxY, polyStart, passedCount, fi, si, sw, ti);
7098
+
7099
+ addPolygonVert(maxX, minY, polyStart, passedCount, fi, si, sw, ti);
7100
+ addPolygonVert(maxX, maxY, polyStart, passedCount, fi, si, sw, ti);
7101
+ addPolygonVert(minX, maxY, polyStart, passedCount, fi, si, sw, ti);
7102
+
7103
+ drawStack.push(polygonPL, 6);
7104
+ } else {
7105
+ if (doFill) {
7106
+ if (shapeVertCount == 5) {
7107
+ // Quads
7108
+ addVert(sv[0], sv[1], sv[2], ti);
7109
+ addVert(sv[3], sv[4], sv[5], ti);
7110
+ addVert(sv[9], sv[10], sv[11], ti);
7111
+ addVert(sv[6], sv[7], sv[8], ti);
7112
+ drawStack.push(shapesPL, 4);
7113
+ } else {
7114
+ // Triangulation fan
7115
+ for (let i = 1; i < shapeVertCount - 1; i++) {
7116
+ let v0 = 0,
7117
+ v1 = i * 3,
7118
+ v2 = (i + 1) * 3;
7119
+ addVert(sv[v0], sv[v0 + 1], sv[v0 + 2], ti);
7120
+ addVert(sv[v1], sv[v1 + 1], sv[v1 + 2], ti);
7121
+ addVert(sv[v2], sv[v2 + 1], sv[v2 + 2], ti);
7122
+ }
7123
+ drawStack.push(shapesPL, (shapeVertCount - 2) * 3);
7124
+ }
7125
+ }
7126
+ if (doStroke) {
7127
+ let maxLines = isAutoClosed ? shapeVertCount - 2 : shapeVertCount - 1;
7128
+ for (let i = 0; i < maxLines; i++) {
7129
+ let v1 = i * 3,
7130
+ v2 = (i + 1) * 3;
7131
+ $.line(sv[v1], sv[v1 + 1], sv[v2], sv[v2 + 1]);
7132
+ }
6786
7133
  }
6787
- let v1 = (shapeVertCount - 1) * 4;
6788
- let v2 = 0;
6789
- if (close) $.line(sv[v1], sv[v1 + 1], sv[v2], sv[v2 + 1]);
6790
7134
  }
6791
7135
 
6792
- // reset for the next shape
6793
7136
  shapeVertCount = 0;
6794
7137
  sv = [];
6795
7138
  curveVertices = [];
6796
7139
  };
6797
7140
 
6798
- $.curve = (x1, y1, x2, y2, x3, y3, x4, y4) => {
6799
- $.beginShape();
6800
- $.curveVertex(x1, y1);
6801
- $.curveVertex(x2, y2);
6802
- $.curveVertex(x3, y3);
6803
- $.curveVertex(x4, y4);
6804
- $.endShape();
6805
- };
6806
-
6807
- $.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
6808
- $.beginShape();
6809
- $.vertex(x1, y1);
6810
- $.bezierVertex(x2, y2, x3, y3, x4, y4);
6811
- $.endShape();
6812
- };
6813
-
6814
7141
  $.triangle = (x1, y1, x2, y2, x3, y3) => {
6815
7142
  $.beginShape();
6816
7143
  $.vertex(x1, y1);
@@ -6828,21 +7155,6 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6828
7155
  $.endShape(true);
6829
7156
  };
6830
7157
 
6831
- function addQuad(x1, y1, x2, y2, x3, y3, x4, y4, ci, ti) {
6832
- addVert(x1, y1, ci, ti); // v0
6833
- addVert(x2, y2, ci, ti); // v1
6834
- addVert(x4, y4, ci, ti); // v3
6835
- addVert(x3, y3, ci, ti); // v2
6836
- drawStack.push(shapesPL, 4);
6837
- }
6838
-
6839
- $.plane = (x, y, w, h) => {
6840
- h ??= w;
6841
- let [l, r, t, b] = calcBox(x, y, w, h, 'center');
6842
- if (matrixDirty) saveMatrix();
6843
- addQuad(l, t, r, t, r, b, l, b, fillIdx, matrixIdx);
6844
- };
6845
-
6846
7158
  /* RECT */
6847
7159
 
6848
7160
  let rectPL = 5;
@@ -7135,7 +7447,21 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
7135
7447
  };
7136
7448
 
7137
7449
  $.line = (x1, y1, x2, y2) => {
7138
- if (doStroke) addCapsule(x1, y1, x2, y2, qsw, hsw, 0);
7450
+ if (!doStroke) return;
7451
+ if (matrixDirty) saveMatrix();
7452
+
7453
+ let dx = x2 - x1,
7454
+ dy = y2 - y1,
7455
+ sqLen = dx * dx + dy * dy;
7456
+
7457
+ if (sqLen === 0) return;
7458
+
7459
+ let len = Math.sqrt(sqLen),
7460
+ ratio = hsw / len,
7461
+ nx = -dy * ratio,
7462
+ ny = dx * ratio;
7463
+
7464
+ addQuad(x1 + nx, y1 + ny, x1 - nx, y1 - ny, x2 - nx, y2 - ny, x2 + nx, y2 + ny, strokeIdx, matrixIdx);
7139
7465
  };
7140
7466
 
7141
7467
  /* ELLIPSE */
@@ -8778,6 +9104,8 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
8778
9104
  transformsBuffer?.destroy();
8779
9105
  colorsBuffer?.destroy();
8780
9106
  shapesVertBuff?.destroy();
9107
+ polygonVertBuff?.destroy();
9108
+ polyPtsBuffer?.destroy();
8781
9109
  imgVertBuff?.destroy();
8782
9110
  charBuffer?.destroy();
8783
9111
  textBuffer?.destroy();