q5 2.14.3 → 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.
@@ -3,6 +3,8 @@ Q5.renderers.webgpu.drawing = ($, q) => {
3
3
  drawStack = $.drawStack,
4
4
  vertexStack = new Float32Array(1e7),
5
5
  vertIndex = 0;
6
+ const TAU = Math.PI * 2;
7
+ const HALF_PI = Math.PI / 2;
6
8
 
7
9
  let drawingShader = Q5.device.createShaderModule({
8
10
  label: 'drawingShader',
@@ -116,10 +118,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
116
118
  drawStack.push(0, 4);
117
119
  };
118
120
 
119
- const addEllipse = (x, y, a, b, n, ci, ti) => {
120
- y = -y;
121
- let t = 0,
122
- angleIncrement = $.TAU / n;
121
+ const addArc = (x, y, a, b, startAngle, endAngle, n, ci, ti) => {
122
+ let angleRange = endAngle - startAngle;
123
+ let angleIncrement = angleRange / n;
124
+ let t = startAngle;
123
125
 
124
126
  let v = vertexStack,
125
127
  i = vertIndex;
@@ -144,27 +146,14 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
144
146
  t += angleIncrement;
145
147
  }
146
148
 
147
- // close the triangle strip
148
- // add center vertex
149
- v[i++] = x;
150
- v[i++] = y;
151
- v[i++] = ci;
152
- v[i++] = ti;
153
-
154
- // add first perimeter vertex
155
- v[i++] = x + a;
156
- v[i++] = y;
157
- v[i++] = ci;
158
- v[i++] = ti;
159
-
160
149
  vertIndex = i;
161
- drawStack.push(0, (n + 1) * 2 + 2);
150
+ drawStack.push(0, (n + 1) * 2);
162
151
  };
163
152
 
164
- const addEllipseStroke = (x, y, outerA, outerB, innerA, innerB, n, ci, ti) => {
165
- y = -y;
166
- let angleIncrement = $.TAU / n;
167
- let t = 0;
153
+ const addArcStroke = (x, y, outerA, outerB, innerA, innerB, startAngle, endAngle, n, ci, ti) => {
154
+ let angleRange = endAngle - startAngle;
155
+ let angleIncrement = angleRange / n;
156
+ let t = startAngle;
168
157
 
169
158
  let v = vertexStack,
170
159
  i = vertIndex;
@@ -193,45 +182,106 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
193
182
  }
194
183
 
195
184
  vertIndex = i;
196
- drawStack.push(0, (n + 1) * 2); // Use triangle strip
185
+ drawStack.push(0, (n + 1) * 2);
197
186
  };
198
187
 
199
188
  $.rectMode = (x) => ($._rectMode = x);
200
189
 
201
- $.rect = (x, y, w, h) => {
190
+ $.rect = (x, y, w, h, rr = 0) => {
202
191
  let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
203
192
  let ci, ti;
204
193
  if ($._matrixDirty) $._saveMatrix();
205
194
  ti = $._matrixIndex;
206
195
 
196
+ if (!rr) {
197
+ if ($._doFill) {
198
+ ci = $._fill;
199
+ addRect(l, t, r, t, r, b, l, b, ci, ti);
200
+ }
201
+
202
+ if ($._doStroke) {
203
+ ci = $._stroke;
204
+ let sw = $._strokeWeight / 2;
205
+
206
+ // Calculate stroke positions
207
+ let lsw = l - sw,
208
+ rsw = r + sw,
209
+ tsw = t + sw,
210
+ bsw = b - sw,
211
+ lpsw = l + sw,
212
+ rpsw = r - sw,
213
+ tpsw = t - sw,
214
+ bpsw = b + sw;
215
+
216
+ addRect(lsw, tpsw, rsw, tpsw, rsw, tsw, lsw, tsw, ci, ti); // Top
217
+ addRect(lsw, bsw, rsw, bsw, rsw, bpsw, lsw, bpsw, ci, ti); // Bottom
218
+
219
+ // Adjust side strokes to avoid overlapping corners
220
+ tsw = t - sw;
221
+ bsw = b + sw;
222
+
223
+ addRect(lsw, tsw, lpsw, tsw, lpsw, bsw, lsw, bsw, ci, ti); // Left
224
+ addRect(rpsw, tsw, rsw, tsw, rsw, bsw, rpsw, bsw, ci, ti); // Right
225
+ }
226
+ return;
227
+ }
228
+
229
+ l += rr;
230
+ r -= rr;
231
+ t -= rr;
232
+ b += rr;
233
+
234
+ // Clamp radius
235
+ rr = Math.min(rr, Math.min(w, h) / 2);
236
+
237
+ let n = getArcSegments(rr * $._scale);
238
+
239
+ let trr = t + rr,
240
+ brr = b - rr,
241
+ lrr = l - rr,
242
+ rrr = r + rr;
243
+
207
244
  if ($._doFill) {
208
245
  ci = $._fill;
209
- addRect(l, t, r, t, r, b, l, b, ci, ti);
246
+ // Corner arcs
247
+ addArc(r, b, rr, rr, -HALF_PI, 0, n, ci, ti);
248
+ addArc(l, b, rr, rr, -Math.PI, -HALF_PI, n, ci, ti);
249
+ addArc(l, t, rr, rr, Math.PI, HALF_PI, n, ci, ti);
250
+ addArc(r, t, rr, rr, 0, HALF_PI, n, ci, ti);
251
+
252
+ addRect(l, trr, r, trr, r, brr, l, brr, ci, ti); // center
253
+ addRect(l, t, lrr, t, lrr, b, l, b, ci, ti); // Left
254
+ addRect(rrr, t, r, t, r, b, rrr, b, ci, ti); // Right
210
255
  }
211
256
 
212
257
  if ($._doStroke) {
213
258
  ci = $._stroke;
214
- let sw = $._strokeWeight / 2;
215
-
216
- // Calculate stroke positions
217
- let lsw = l - sw,
218
- rsw = r + sw,
219
- tsw = t + sw,
220
- bsw = b - sw,
221
- lpsw = l + sw,
222
- rpsw = r - sw,
223
- tpsw = t - sw,
224
- bpsw = b + sw;
225
-
226
- addRect(lsw, tpsw, rsw, tpsw, rsw, tsw, lsw, tsw, ci, ti); // Top
227
- addRect(lsw, bsw, rsw, bsw, rsw, bpsw, lsw, bpsw, ci, ti); // Bottom
228
-
229
- // Adjust side strokes to avoid overlapping corners
230
- tsw = t - sw;
231
- bsw = b + sw;
232
-
233
- addRect(lsw, tsw, lpsw, tsw, lpsw, bsw, lsw, bsw, ci, ti); // Left
234
- addRect(rpsw, tsw, rsw, tsw, rsw, bsw, rpsw, bsw, ci, ti); // Right
259
+ let hsw = $._hsw;
260
+
261
+ let outerA = rr + hsw,
262
+ outerB = rr + hsw,
263
+ innerA = rr - hsw,
264
+ innerB = rr - hsw;
265
+ // Corner arc strokes
266
+ addArcStroke(r, b, outerA, outerB, innerA, innerB, -HALF_PI, 0, n, ci, ti);
267
+ addArcStroke(l, b, outerA, outerB, innerA, innerB, -Math.PI, -HALF_PI, n, ci, ti);
268
+ addArcStroke(l, t, outerA, outerB, innerA, innerB, Math.PI, HALF_PI, n, ci, ti);
269
+ addArcStroke(r, t, outerA, outerB, innerA, innerB, 0, HALF_PI, n, ci, ti);
270
+
271
+ let lrrMin = lrr - hsw,
272
+ lrrMax = lrr + hsw,
273
+ rrrMin = rrr - hsw,
274
+ rrrMax = rrr + hsw,
275
+ trrMin = trr - hsw,
276
+ trrMax = trr + hsw,
277
+ brrMin = brr - hsw,
278
+ brrMax = brr + hsw;
279
+
280
+ // Side strokes - positioned outside
281
+ addRect(lrrMin, t, lrrMax, t, lrrMax, b, lrrMin, b, ci, ti); // Left
282
+ addRect(rrrMin, t, rrrMax, t, rrrMax, b, rrrMin, b, ci, ti); // Right
283
+ addRect(l, trrMin, r, trrMin, r, trrMax, l, trrMax, ci, ti); // Top
284
+ addRect(l, brrMin, r, brrMin, r, brrMax, l, brrMax, ci, ti); // Bottom
235
285
  }
236
286
  };
237
287
 
@@ -268,6 +318,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
268
318
  d < 2400 ? 90 :
269
319
  100;
270
320
 
321
+ $._ellipseMode = Q5.CENTER;
271
322
  $.ellipseMode = (x) => ($._ellipseMode = x);
272
323
 
273
324
  $.ellipse = (x, y, w, h) => {
@@ -279,17 +330,70 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
279
330
  let ti = $._matrixIndex;
280
331
 
281
332
  if ($._doFill) {
282
- addEllipse(x, y, a, b, n, $._fill, ti);
333
+ addArc(x, -y, a, b, 0, TAU, n, $._fill, ti);
283
334
  }
284
335
  if ($._doStroke) {
285
336
  let sw = $._strokeWeight / 2;
286
337
  // Draw the stroke as a ring using triangle strips
287
- addEllipseStroke(x, y, a + sw, b + sw, a - sw, b - sw, n, $._stroke, ti);
338
+ addArcStroke(x, -y, a + sw, b + sw, a - sw, b - sw, 0, TAU, n, $._stroke, ti);
288
339
  }
289
340
  };
290
341
 
291
342
  $.circle = (x, y, d) => $.ellipse(x, y, d, d);
292
343
 
344
+ $.arc = (x, y, w, h, start, stop) => {
345
+ if (start === stop) return $.ellipse(x, y, w, h);
346
+
347
+ // Convert angles if needed
348
+ if ($._angleMode) {
349
+ start = $.radians(start);
350
+ stop = $.radians(stop);
351
+ }
352
+
353
+ // Normalize angles
354
+ start %= TAU;
355
+ stop %= TAU;
356
+ if (start < 0) start += TAU;
357
+ if (stop < 0) stop += TAU;
358
+ if (start > stop) stop += TAU;
359
+ if (start == stop) return $.ellipse(x, y, w, h);
360
+
361
+ // Calculate position based on ellipseMode
362
+ let a, b;
363
+ if ($._ellipseMode == $.CENTER) {
364
+ a = w / 2;
365
+ b = h / 2;
366
+ } else if ($._ellipseMode == $.RADIUS) {
367
+ a = w;
368
+ b = h;
369
+ } else if ($._ellipseMode == $.CORNER) {
370
+ x += w / 2;
371
+ y += h / 2;
372
+ a = w / 2;
373
+ b = h / 2;
374
+ } else if ($._ellipseMode == $.CORNERS) {
375
+ x = (x + w) / 2;
376
+ y = (y + h) / 2;
377
+ a = (w - x) / 2;
378
+ b = (h - y) / 2;
379
+ }
380
+
381
+ let ti = $._matrixIndex;
382
+ if ($._matrixDirty) $._saveMatrix();
383
+ let n = getArcSegments(Math.max(Math.abs(w), Math.abs(h)) * $._scale);
384
+
385
+ // Draw fill
386
+ if ($._doFill) {
387
+ addArc(x, -y, a, b, start, stop, n, $._fill, ti);
388
+ }
389
+
390
+ // Draw stroke
391
+ if ($._doStroke) {
392
+ let sw = $._strokeWeight;
393
+ addArcStroke(x, -y, a + sw, b + sw, a - sw, b - sw, start, stop, n, $._stroke, ti);
394
+ }
395
+ };
396
+
293
397
  $.point = (x, y) => {
294
398
  if ($._matrixDirty) $._saveMatrix();
295
399
  let ti = $._matrixIndex,
@@ -300,9 +404,9 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
300
404
  let [l, r, t, b] = $._calcBox(x, y, sw, sw, 'corner');
301
405
  addRect(l, t, r, t, r, b, l, b, ci, ti);
302
406
  } else {
303
- let n = getArcSegments(sw);
407
+ let n = getArcSegments($._scaledSW);
304
408
  sw /= 2;
305
- addEllipse(x, y, sw, sw, n, ci, ti);
409
+ addArc(x, -y, sw, sw, 0, TAU, n, ci, ti);
306
410
  }
307
411
  };
308
412
 
@@ -317,7 +421,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
317
421
  let ti = $._matrixIndex,
318
422
  ci = $._stroke,
319
423
  sw = $._strokeWeight,
320
- hsw = sw / 2;
424
+ hsw = $._hsw;
321
425
 
322
426
  // calculate the direction vector and length
323
427
  let dx = x2 - x1,
@@ -332,8 +436,8 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
332
436
 
333
437
  if ($._scaledSW > 2 && $._strokeJoin != 'none') {
334
438
  let n = getArcSegments($._scaledSW);
335
- addEllipse(x1, y1, hsw, hsw, n, ci, ti);
336
- addEllipse(x2, y2, hsw, hsw, n, ci, ti);
439
+ addArc(x1, -y1, hsw, hsw, 0, TAU, n, ci, ti);
440
+ addArc(x2, -y2, hsw, hsw, 0, TAU, n, ci, ti);
337
441
  }
338
442
  };
339
443
 
@@ -370,6 +474,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
370
474
  }
371
475
  }
372
476
 
477
+ // calculate catmull-rom spline curve points
373
478
  for (let i = 0; i < points.length - 3; i++) {
374
479
  let p0 = points[i];
375
480
  let p1 = points[i + 1];
@@ -444,17 +549,25 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
444
549
  }
445
550
 
446
551
  if ($._doStroke) {
552
+ let hsw = $._hsw,
553
+ n = getArcSegments($._scaledSW),
554
+ ti = $._matrixIndex,
555
+ ogStrokeJoin = $._strokeJoin;
556
+ $._strokeJoin = 'none';
447
557
  // draw lines between vertices
448
558
  for (let i = 0; i < shapeVertCount - 1; i++) {
449
559
  let v1 = i * 4;
450
560
  let v2 = (i + 1) * 4;
451
561
  $.line(sv[v1], -sv[v1 + 1], sv[v2], -sv[v2 + 1]);
562
+
563
+ addArc(sv[v1], sv[v1 + 1], hsw, hsw, 0, TAU, n, $._stroke, ti);
452
564
  }
453
565
  if (close) {
454
566
  let v1 = (shapeVertCount - 1) * 4;
455
567
  let v2 = 0;
456
568
  $.line(sv[v1], -sv[v1 + 1], sv[v2], -sv[v2 + 1]);
457
569
  }
570
+ $._strokeJoin = ogStrokeJoin;
458
571
  }
459
572
 
460
573
  // reset for the next shape