q5 4.4.1 → 4.4.2

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 +12 -17
  4. package/q5.js +206 -403
  5. package/q5.min.js +1 -1
package/q5.js CHANGED
@@ -6202,15 +6202,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6202
6202
  shouldClear = false;
6203
6203
  };
6204
6204
 
6205
- let transformsBuffer,
6206
- colorsBuffer,
6207
- shapesVertBuff,
6208
- imgVertBuff,
6209
- polygonVertBuff,
6210
- polyPtsBuffer,
6211
- polyPtsBindGroup,
6212
- charBuffer,
6213
- textBuffer;
6205
+ let transformsBuffer, colorsBuffer, shapesVertBuff, imgVertBuff, charBuffer, textBuffer;
6214
6206
  let mainBindGroup, lastTransformsBuffer, lastColorsBuffer;
6215
6207
 
6216
6208
  $._render = () => {
@@ -6288,38 +6280,6 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6288
6280
  $._pass.setVertexBuffer(0, shapesVertBuff);
6289
6281
 
6290
6282
  // prepare to render images and videos
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
-
6323
6283
  if (imgVertIdx) {
6324
6284
  $._pass.setPipeline($._pipelines[2]); // images pipeline
6325
6285
 
@@ -6418,7 +6378,6 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6418
6378
  let drawVertOffset = 0,
6419
6379
  imageVertOffset = 0,
6420
6380
  textCharOffset = 0,
6421
- polygonVertOffset = 0,
6422
6381
  rectIdx = 0,
6423
6382
  ellipseIdx = 0,
6424
6383
  curPipelineIndex = -1;
@@ -6446,18 +6405,12 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6446
6405
  } else if (curPipelineIndex == 6) {
6447
6406
  pass.setIndexBuffer(ellipseIndexBuffer, 'uint16');
6448
6407
  pass.setBindGroup(1, ellipseBindGroup);
6449
- } else if (curPipelineIndex == 7) {
6450
- pass.setBindGroup(1, polyPtsBindGroup);
6451
6408
  } else if ($._customBindHandlers[curPipelineIndex]) {
6452
6409
  $._customBindHandlers[curPipelineIndex](pass);
6453
6410
  }
6454
6411
  }
6455
6412
 
6456
- if (curPipelineIndex == 7) {
6457
- // draw a polygon
6458
- pass.draw(v, 1, polygonVertOffset, 0);
6459
- polygonVertOffset += v;
6460
- } else if (curPipelineIndex == 6) {
6413
+ if (curPipelineIndex == 6) {
6461
6414
  // draw an ellipse
6462
6415
  pass.drawIndexed(18, v, 0, 0, ellipseIdx);
6463
6416
  ellipseIdx += v;
@@ -6534,8 +6487,6 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
6534
6487
 
6535
6488
  // reset
6536
6489
  shapesVertIdx = 0;
6537
- polygonVertIdx = 0;
6538
- polyPtsIdx = 0;
6539
6490
  imgVertIdx = 0;
6540
6491
  // Remove video frames without creating new array
6541
6492
  if (vidFrames > 0) {
@@ -6659,15 +6610,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6659
6610
  shapesVertIdx = i;
6660
6611
  };
6661
6612
 
6662
- let _strokeCap = 'round',
6663
- _strokeJoin = 'round';
6613
+ let _strokeCap = 'butt', // SQUARE
6614
+ _strokeJoin = 'miter';
6664
6615
 
6665
6616
  $.strokeCap = (x) => (_strokeCap = x);
6666
6617
  $.strokeJoin = (x) => (_strokeJoin = x);
6667
- $.lineMode = () => {
6668
- _strokeCap = 'square';
6669
- _strokeJoin = 'none';
6670
- };
6671
6618
 
6672
6619
  let curveSegments = 20;
6673
6620
  $.curveDetail = (v) => (curveSegments = v);
@@ -6738,266 +6685,6 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6738
6685
 
6739
6686
  $.quadraticVertex = (cx, cy, x, y) => $.bezierVertex(cx, cy, x, y);
6740
6687
 
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 fill = getPolyColor(f.localPos, f.polyStart, f.polyCount, f.fillIndex);
6889
- let stroke = colors[i32(f.strokeIndex)];
6890
-
6891
- // f.isClosed is derived from the sign of v.polyCount (used for fill closure)
6892
- let distFill = sdPolygon(f.localPos, f.polyStart, f.polyCount, f.isClosed);
6893
-
6894
- // encode stroke-closure in the sign of strokeWeight; use absolute stroke weight for width
6895
- let isClosedStroke = step(0.0, f.strokeWeight);
6896
- let strokeW = abs(f.strokeWeight);
6897
- let distStroke = sdPolygon(f.localPos, f.polyStart, f.polyCount, isClosedStroke);
6898
-
6899
- // compute AA using gradients from both distances (conservative)
6900
- let dpdx_fill = dpdx(distFill);
6901
- let dpdy_fill = dpdy(distFill);
6902
- let gradFill = sqrt(dpdx_fill * dpdx_fill + dpdy_fill * dpdy_fill);
6903
-
6904
- let dpdx_stroke = dpdx(distStroke);
6905
- let dpdy_stroke = dpdy(distStroke);
6906
- let gradStroke = sqrt(dpdx_stroke * dpdx_stroke + dpdy_stroke * dpdy_stroke);
6907
-
6908
- let aa = clamp(max(gradFill, gradStroke) * 1.5, 0.001, 2.0);
6909
-
6910
- let halfStroke = strokeW * 0.5;
6911
-
6912
- var outFragColor: vec4f;
6913
-
6914
- if (fill.a != 0.0 && strokeW == 0.0) {
6915
- let fillAlpha = 1.0 - smoothstep(-aa, aa, distFill);
6916
- if (fillAlpha <= 0.0) { discard; }
6917
- outFragColor = vec4f(fill.rgb, fill.a * fillAlpha);
6918
- } else if (fill.a != 0.0) {
6919
- let fillAlpha = 1.0 - smoothstep(-aa, aa, distFill);
6920
- let strokeDist = abs(distStroke) - halfStroke;
6921
- let strokeAlphaMask = 1.0 - smoothstep(-aa, aa, strokeDist);
6922
-
6923
- if (fillAlpha <= 0.0 && strokeAlphaMask <= 0.0) { discard; }
6924
-
6925
- let sA = stroke.a * strokeAlphaMask;
6926
- let fA = fill.a * fillAlpha;
6927
- let outAlpha = sA + fA * (1.0 - sA);
6928
- let outCol = stroke.rgb * sA + fill.rgb * fA * (1.0 - sA);
6929
- outFragColor = vec4f(outCol / max(outAlpha, 1e-5), outAlpha);
6930
- } else {
6931
- let strokeDist = abs(distStroke) - halfStroke;
6932
- let strokeAlpha = 1.0 - smoothstep(-aa, aa, strokeDist);
6933
-
6934
- if (strokeAlpha <= 0.0) { discard; }
6935
- outFragColor = vec4f(stroke.rgb, stroke.a * strokeAlpha);
6936
- }
6937
- return outFragColor;
6938
- }
6939
- `;
6940
-
6941
- let polygonShader = Q5.device.createShaderModule({
6942
- label: 'polygonShader',
6943
- code: $._polygonShaderCode
6944
- });
6945
-
6946
- let polygonVertStack = new Float32Array($._isGraphics ? 1000 : 1e7),
6947
- polygonVertIdx = 0;
6948
- let polyPtsStack = new Float32Array($._isGraphics ? 1000 : 1e7),
6949
- polyPtsIdx = 0;
6950
-
6951
- let polygonVertBuffLayout = {
6952
- arrayStride: 32, // 8 floats * 4 bytes
6953
- attributes: [
6954
- { format: 'float32x2', offset: 0, shaderLocation: 0 },
6955
- { format: 'float32', offset: 8, shaderLocation: 1 },
6956
- { format: 'float32', offset: 12, shaderLocation: 2 },
6957
- { format: 'float32', offset: 16, shaderLocation: 3 },
6958
- { format: 'float32', offset: 20, shaderLocation: 4 },
6959
- { format: 'float32', offset: 24, shaderLocation: 5 },
6960
- { format: 'float32', offset: 28, shaderLocation: 6 }
6961
- ]
6962
- };
6963
-
6964
- let polygonBindGroupLayout = Q5.device.createBindGroupLayout({
6965
- entries: [{ binding: 0, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'read-only-storage' } }]
6966
- });
6967
-
6968
- let polygonPipelineLayout = Q5.device.createPipelineLayout({
6969
- label: 'polygonPipelineLayout',
6970
- bindGroupLayouts: [mainLayout, polygonBindGroupLayout]
6971
- });
6972
-
6973
- $._pipelineConfigs[7] = {
6974
- label: 'polygonPipeline',
6975
- layout: polygonPipelineLayout,
6976
- vertex: { module: polygonShader, entryPoint: 'vertexMain', buffers: [null, null, polygonVertBuffLayout] },
6977
- fragment: {
6978
- module: polygonShader,
6979
- entryPoint: 'fragMain',
6980
- targets: [{ format: 'bgra8unorm', blend: $.blendConfigs['source-over'] }]
6981
- },
6982
- primitive: { topology: 'triangle-list' },
6983
- multisample: { count: 4 }
6984
- };
6985
- $._pipelines[7] = Q5.device.createRenderPipeline($._pipelineConfigs[7]);
6986
-
6987
- const addPolygonVert = (x, y, start, count, fIdx, sIdx, sWeight, mIdx) => {
6988
- let v = polygonVertStack,
6989
- i = polygonVertIdx;
6990
- v[i++] = x;
6991
- v[i++] = y;
6992
- v[i++] = start;
6993
- v[i++] = count;
6994
- v[i++] = fIdx;
6995
- v[i++] = sIdx;
6996
- v[i++] = sWeight;
6997
- v[i++] = mIdx;
6998
- polygonVertIdx = i;
6999
- };
7000
-
7001
6688
  $.endShape = (close) => {
7002
6689
  if (curveVertices.length > 0) {
7003
6690
  let points = [...curveVertices];
@@ -7054,98 +6741,185 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
7054
6741
  if (matrixDirty) saveMatrix();
7055
6742
  let ti = matrixIdx;
7056
6743
 
7057
- let isAutoClosed = false;
7058
- let isClosedPath = false;
7059
- let isClosedForStroke = false;
7060
- let firstX = sv[0],
7061
- firstY = sv[1];
7062
- let lastX = sv[(shapeVertCount - 1) * 3],
7063
- lastY = sv[(shapeVertCount - 1) * 3 + 1];
7064
-
7065
- if (firstX === lastX && firstY === lastY) {
7066
- isClosedPath = true;
7067
- isClosedForStroke = true;
7068
- } else if (close || doFill) {
7069
- sv.push(firstX, firstY, sv[2]);
6744
+ if (doFill) {
6745
+ sv.push(sv[0], sv[1], sv[2]);
7070
6746
  shapeVertCount++;
7071
- isAutoClosed = !close;
7072
- isClosedPath = true;
7073
- isClosedForStroke = !!close;
6747
+
6748
+ if (shapeVertCount == 5) {
6749
+ // Quads
6750
+ addVert(sv[0], sv[1], sv[2], ti);
6751
+ addVert(sv[3], sv[4], sv[5], ti);
6752
+ addVert(sv[9], sv[10], sv[11], ti);
6753
+ addVert(sv[6], sv[7], sv[8], ti);
6754
+ drawStack.push(shapesPL, 4);
6755
+ } else {
6756
+ // Triangulation fan
6757
+ for (let i = 1; i < shapeVertCount - 1; i++) {
6758
+ let v0 = 0,
6759
+ v1 = i * 3,
6760
+ v2 = (i + 1) * 3;
6761
+ addVert(sv[v0], sv[v0 + 1], sv[v0 + 2], ti);
6762
+ addVert(sv[v1], sv[v1 + 1], sv[v1 + 2], ti);
6763
+ addVert(sv[v2], sv[v2 + 1], sv[v2 + 2], ti);
6764
+ }
6765
+ drawStack.push(shapesPL, (shapeVertCount - 2) * 3);
6766
+ }
7074
6767
  }
7075
6768
 
7076
- let runSDF = shapeVertCount >= 3 && doStroke && hswScaled > 3;
6769
+ if (doStroke && sw > 0) {
6770
+ let numSegments = shapeVertCount - 1;
6771
+ if (!close && doFill) numSegments--;
7077
6772
 
7078
- if (runSDF) {
7079
- let fi = doFill ? fillIdx : 0,
7080
- si = strokeIdx;
6773
+ if (_strokeJoin == 'round' || hswScaled < 2) {
6774
+ for (let i = 0; i < numSegments; i++) {
6775
+ let p0x = sv[i * 3],
6776
+ p0y = sv[i * 3 + 1],
6777
+ p1x = sv[(i + 1) * 3],
6778
+ p1y = sv[(i + 1) * 3 + 1];
7081
6779
 
7082
- let polyStart = polyPtsIdx / 4,
7083
- polyCount = isClosedPath ? shapeVertCount - 1 : shapeVertCount;
7084
- for (let i = 0; i < polyCount; i++) {
7085
- polyPtsStack[polyPtsIdx++] = sv[i * 3];
7086
- polyPtsStack[polyPtsIdx++] = sv[i * 3 + 1];
7087
- polyPtsStack[polyPtsIdx++] = sv[i * 3 + 2];
7088
- polyPtsStack[polyPtsIdx++] = ti;
7089
- }
6780
+ // Skip zero-length segments
6781
+ if (p0x === p1x && p0y === p1y) continue;
7090
6782
 
7091
- let minX = Infinity,
7092
- minY = Infinity,
7093
- maxX = -Infinity,
7094
- maxY = -Infinity;
7095
- for (let i = 0; i < shapeVertCount; i++) {
7096
- let vx = sv[i * 3],
7097
- vy = sv[i * 3 + 1];
7098
- if (vx < minX) minX = vx;
7099
- if (vx > maxX) maxX = vx;
7100
- if (vy < minY) minY = vy;
7101
- if (vy > maxY) maxY = vy;
7102
- }
7103
- let padding = sw * 0.5 + 1.0; // padding for stroke and AA
7104
- minX -= padding;
7105
- minY -= padding;
7106
- maxX += padding;
7107
- maxY += padding;
7108
-
7109
- // single SDF pass: encode fill-closure in polyCount sign, stroke-closure in strokeWeight sign
7110
- let passedCount = doFill ? polyCount : -polyCount;
7111
- let strokeWeightSigned = isClosedForStroke ? sw : -sw;
7112
- addPolygonVert(minX, minY, polyStart, passedCount, fi, si, strokeWeightSigned, ti);
7113
- addPolygonVert(maxX, minY, polyStart, passedCount, fi, si, strokeWeightSigned, ti);
7114
- addPolygonVert(minX, maxY, polyStart, passedCount, fi, si, strokeWeightSigned, ti);
7115
-
7116
- addPolygonVert(maxX, minY, polyStart, passedCount, fi, si, strokeWeightSigned, ti);
7117
- addPolygonVert(maxX, maxY, polyStart, passedCount, fi, si, strokeWeightSigned, ti);
7118
- addPolygonVert(minX, maxY, polyStart, passedCount, fi, si, strokeWeightSigned, ti);
7119
-
7120
- drawStack.push(polygonPL, 6);
7121
- } else {
7122
- if (doFill) {
7123
- if (shapeVertCount == 5) {
7124
- // Quads
7125
- addVert(sv[0], sv[1], sv[2], ti);
7126
- addVert(sv[3], sv[4], sv[5], ti);
7127
- addVert(sv[9], sv[10], sv[11], ti);
7128
- addVert(sv[6], sv[7], sv[8], ti);
7129
- drawStack.push(shapesPL, 4);
7130
- } else {
7131
- // Triangulation fan
7132
- for (let i = 1; i < shapeVertCount - 1; i++) {
7133
- let v0 = 0,
7134
- v1 = i * 3,
7135
- v2 = (i + 1) * 3;
7136
- addVert(sv[v0], sv[v0 + 1], sv[v0 + 2], ti);
7137
- addVert(sv[v1], sv[v1 + 1], sv[v1 + 2], ti);
7138
- addVert(sv[v2], sv[v2 + 1], sv[v2 + 2], ti);
6783
+ if (hswScaled < 2) {
6784
+ $.line(p0x, p0y, p1x, p1y);
6785
+ } else {
6786
+ addCapsule(p0x, p0y, p1x, p1y, sw * 0.5, 0, strokeIdx);
7139
6787
  }
7140
- drawStack.push(shapesPL, (shapeVertCount - 2) * 3);
7141
6788
  }
7142
- }
7143
- if (doStroke) {
7144
- let maxLines = isAutoClosed ? shapeVertCount - 2 : shapeVertCount - 1;
7145
- for (let i = 0; i < maxLines; i++) {
7146
- let v1 = i * 3,
7147
- v2 = (i + 1) * 3;
7148
- $.line(sv[v1], sv[v1 + 1], sv[v2], sv[v2 + 1]);
6789
+ } else {
6790
+ let cx = 0,
6791
+ cy = 0;
6792
+ for (let i = 0; i < shapeVertCount - 1; i++) {
6793
+ cx += sv[i * 3];
6794
+ cy += sv[i * 3 + 1];
6795
+ }
6796
+ cx /= Math.max(1, shapeVertCount - 1);
6797
+ cy /= Math.max(1, shapeVertCount - 1);
6798
+
6799
+ let signedArea = 0,
6800
+ sign = 1;
6801
+ for (let i = 0; i < shapeVertCount - 1; i++) {
6802
+ let j = i + 1;
6803
+ signedArea += sv[i * 3] * sv[j * 3 + 1] - sv[j * 3] * sv[i * 3 + 1];
6804
+ }
6805
+ sign = signedArea < 0 ? -1 : 1;
6806
+
6807
+ let strokeI = doStroke ? strokeIdx : 0,
6808
+ baseWeight = doStroke ? sw : 0,
6809
+ exp = baseWeight * 0.5;
6810
+
6811
+ // Compute quads on-the-fly per edge without storing per-vertex arrays/objects
6812
+ for (let i = 0; i < numSegments; i++) {
6813
+ let j = i + 1,
6814
+ iprev = i === 0 ? shapeVertCount - 2 : i - 1,
6815
+ inext = i === shapeVertCount - 1 ? 1 : i + 1,
6816
+ ivx = sv[i * 3],
6817
+ ivy = sv[i * 3 + 1],
6818
+ ipx = sv[iprev * 3],
6819
+ ipy = sv[iprev * 3 + 1],
6820
+ inx = sv[inext * 3],
6821
+ iny = sv[inext * 3 + 1],
6822
+ idir1x = ivx - ipx,
6823
+ idir1y = ivy - ipy,
6824
+ il1 = idir1x * idir1x + idir1y * idir1y;
6825
+ if (il1 <= 0) {
6826
+ idir1x = 1;
6827
+ idir1y = 0;
6828
+ } else {
6829
+ let iinv = 1.0 / Math.sqrt(il1);
6830
+ idir1x *= iinv;
6831
+ idir1y *= iinv;
6832
+ }
6833
+ let idir2x = inx - ivx,
6834
+ idir2y = iny - ivy,
6835
+ il2 = idir2x * idir2x + idir2y * idir2y;
6836
+ if (il2 <= 0) {
6837
+ idir2x = 1;
6838
+ idir2y = 0;
6839
+ } else {
6840
+ let iinv2 = 1.0 / Math.sqrt(il2);
6841
+ idir2x *= iinv2;
6842
+ idir2y *= iinv2;
6843
+ }
6844
+ let in1x = idir1y * sign,
6845
+ in1y = -idir1x * sign,
6846
+ in2x = idir2y * sign,
6847
+ in2y = -idir2x * sign,
6848
+ imx = in1x + in2x,
6849
+ imy = in1y + in2y,
6850
+ iml = imx * imx + imy * imy;
6851
+ if (iml < 1e-6) {
6852
+ imx = in1x;
6853
+ imy = in1y;
6854
+ } else {
6855
+ let iinvm = 1.0 / Math.sqrt(iml);
6856
+ imx *= iinvm;
6857
+ imy *= iinvm;
6858
+ }
6859
+ let imiterDot = imx * in2x + imy * in2y,
6860
+ imiterExpansion = imiterDot !== 0 ? exp / imiterDot : exp;
6861
+ if (imiterExpansion > exp * 3.0) imiterExpansion = exp * 3.0;
6862
+ let inner_ix = ivx - imx * imiterExpansion,
6863
+ inner_iy = ivy - imy * imiterExpansion,
6864
+ outer_ix = ivx + imx * imiterExpansion,
6865
+ outer_iy = ivy + imy * imiterExpansion;
6866
+
6867
+ // compute miter for vertex j
6868
+ let jprev = j === 0 ? shapeVertCount - 2 : j - 1,
6869
+ jnext = j === shapeVertCount - 1 ? 1 : j + 1,
6870
+ jvx = sv[j * 3],
6871
+ jvy = sv[j * 3 + 1],
6872
+ jpx = sv[jprev * 3],
6873
+ jpy = sv[jprev * 3 + 1],
6874
+ jnx = sv[jnext * 3],
6875
+ jny = sv[jnext * 3 + 1];
6876
+
6877
+ let jdir1x = jvx - jpx,
6878
+ jdir1y = jvy - jpy,
6879
+ jl1 = jdir1x * jdir1x + jdir1y * jdir1y;
6880
+ if (jl1 <= 0) {
6881
+ jdir1x = 1;
6882
+ jdir1y = 0;
6883
+ } else {
6884
+ let jinv = 1.0 / Math.sqrt(jl1);
6885
+ jdir1x *= jinv;
6886
+ jdir1y *= jinv;
6887
+ }
6888
+ let jdir2x = jnx - jvx,
6889
+ jdir2y = jny - jvy,
6890
+ jl2 = jdir2x * jdir2x + jdir2y * jdir2y;
6891
+ if (jl2 <= 0) {
6892
+ jdir2x = 1;
6893
+ jdir2y = 0;
6894
+ } else {
6895
+ let jinv2 = 1.0 / Math.sqrt(jl2);
6896
+ jdir2x *= jinv2;
6897
+ jdir2y *= jinv2;
6898
+ }
6899
+ let jn1x = jdir1y * sign,
6900
+ jn1y = -jdir1x * sign,
6901
+ jn2x = jdir2y * sign,
6902
+ jn2y = -jdir2x * sign,
6903
+ jmx = jn1x + jn2x,
6904
+ jmy = jn1y + jn2y,
6905
+ jml = jmx * jmx + jmy * jmy;
6906
+ if (jml < 1e-6) {
6907
+ jmx = jn1x;
6908
+ jmy = jn1y;
6909
+ } else {
6910
+ let jinvm = 1.0 / Math.sqrt(jml);
6911
+ jmx *= jinvm;
6912
+ jmy *= jinvm;
6913
+ }
6914
+ let jmiterDot = jmx * jn2x + jmy * jn2y,
6915
+ mExp = jmiterDot !== 0 ? exp / jmiterDot : exp;
6916
+ if (mExp > exp * 3.0) mExp = exp * 3.0;
6917
+ let inner_jx = jvx - jmx * mExp,
6918
+ inner_jy = jvy - jmy * mExp,
6919
+ outer_jx = jvx + jmx * mExp,
6920
+ outer_jy = jvy + jmy * mExp;
6921
+
6922
+ addQuad(inner_ix, inner_iy, inner_jx, inner_jy, outer_jx, outer_jy, outer_ix, outer_iy, strokeI, ti);
7149
6923
  }
7150
6924
  }
7151
6925
  }
@@ -7172,6 +6946,37 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
7172
6946
  $.endShape(true);
7173
6947
  };
7174
6948
 
6949
+ $.curve = (x1, y1, x2, y2, x3, y3, x4, y4) => {
6950
+ $.beginShape();
6951
+ $.curveVertex(x1, y1);
6952
+ $.curveVertex(x2, y2);
6953
+ $.curveVertex(x3, y3);
6954
+ $.curveVertex(x4, y4);
6955
+ $.endShape();
6956
+ };
6957
+
6958
+ $.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
6959
+ $.beginShape();
6960
+ $.vertex(x1, y1);
6961
+ $.bezierVertex(x2, y2, x3, y3, x4, y4);
6962
+ $.endShape();
6963
+ };
6964
+
6965
+ function addQuad(x1, y1, x2, y2, x3, y3, x4, y4, ci, ti) {
6966
+ addVert(x1, y1, ci, ti); // v0
6967
+ addVert(x2, y2, ci, ti); // v1
6968
+ addVert(x4, y4, ci, ti); // v3
6969
+ addVert(x3, y3, ci, ti); // v2
6970
+ drawStack.push(shapesPL, 4);
6971
+ }
6972
+
6973
+ $.plane = (x, y, w, h) => {
6974
+ h ??= w;
6975
+ let [l, r, t, b] = calcBox(x, y, w, h, 'center');
6976
+ if (matrixDirty) saveMatrix();
6977
+ addQuad(l, t, r, t, r, b, l, b, fillIdx, matrixIdx);
6978
+ };
6979
+
7175
6980
  /* RECT */
7176
6981
 
7177
6982
  let rectPL = 5;
@@ -9121,8 +8926,6 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
9121
8926
  transformsBuffer?.destroy();
9122
8927
  colorsBuffer?.destroy();
9123
8928
  shapesVertBuff?.destroy();
9124
- polygonVertBuff?.destroy();
9125
- polyPtsBuffer?.destroy();
9126
8929
  imgVertBuff?.destroy();
9127
8930
  charBuffer?.destroy();
9128
8931
  textBuffer?.destroy();