@almadar/ui 5.140.0 → 5.141.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.
@@ -12629,9 +12629,26 @@ var init_imageCache = __esm({
12629
12629
  });
12630
12630
 
12631
12631
  // lib/webPainter2d.ts
12632
+ function makeNoiseTile(alpha, color) {
12633
+ const tile = document.createElement("canvas");
12634
+ tile.width = NOISE_CELLS;
12635
+ tile.height = NOISE_CELLS;
12636
+ const t = tile.getContext("2d");
12637
+ if (t) {
12638
+ t.fillStyle = color;
12639
+ for (let y = 0; y < NOISE_CELLS; y++) {
12640
+ for (let x = 0; x < NOISE_CELLS; x++) {
12641
+ t.globalAlpha = noiseHash(x, y) * alpha;
12642
+ t.fillRect(x, y, 1, 1);
12643
+ }
12644
+ }
12645
+ }
12646
+ return tile;
12647
+ }
12632
12648
  function createWebPainter(ctx, onAssetLoad) {
12633
12649
  let vw = 0;
12634
12650
  let vh = 0;
12651
+ const patternCache = /* @__PURE__ */ new Map();
12635
12652
  const tracePoly = (points, closed) => {
12636
12653
  if (points.length === 0) return;
12637
12654
  ctx.beginPath();
@@ -12639,9 +12656,28 @@ function createWebPainter(ctx, onAssetLoad) {
12639
12656
  for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
12640
12657
  if (closed) ctx.closePath();
12641
12658
  };
12659
+ const toCanvasPattern = (style) => {
12660
+ const key = JSON.stringify(style);
12661
+ let pattern = patternCache.get(key);
12662
+ if (pattern === void 0) {
12663
+ if (style.kind === "noise") {
12664
+ pattern = ctx.createPattern(makeNoiseTile(style.alpha ?? 0.12, style.color ?? "#000000"), "repeat");
12665
+ } else {
12666
+ const img = getOrLoadImage(style.url, onAssetLoad);
12667
+ if (!img) return "rgba(0,0,0,0)";
12668
+ pattern = ctx.createPattern(img, "repeat");
12669
+ }
12670
+ if (pattern && style.scale !== void 0 && style.scale !== 1) {
12671
+ pattern.setTransform(new DOMMatrix().scale(style.scale));
12672
+ }
12673
+ patternCache.set(key, pattern);
12674
+ }
12675
+ return pattern ?? "rgba(0,0,0,0)";
12676
+ };
12642
12677
  const toCanvasStyle = (style) => {
12643
12678
  if (typeof style === "string") return style;
12644
- const g = style.kind === "linear" ? ctx.createLinearGradient(style.x1, style.y1, style.x2, style.y2) : ctx.createRadialGradient(style.cx, style.cy, 0, style.cx, style.cy, style.r);
12679
+ if (style.kind === "noise" || style.kind === "image") return toCanvasPattern(style);
12680
+ const g = style.kind === "linear" ? ctx.createLinearGradient(style.x1, style.y1, style.x2, style.y2) : style.kind === "conic" ? ctx.createConicGradient(style.angle, style.cx, style.cy) : ctx.createRadialGradient(style.cx, style.cy, 0, style.cx, style.cy, style.r);
12645
12681
  for (const stop of style.stops) g.addColorStop(stop.offset, stop.color);
12646
12682
  return g;
12647
12683
  };
@@ -12676,6 +12712,19 @@ function createWebPainter(ctx, onAssetLoad) {
12676
12712
  ctx.shadowColor = shadow ? shadow.color : "transparent";
12677
12713
  ctx.shadowBlur = shadow ? shadow.blur : 0;
12678
12714
  },
12715
+ setBlend(mode) {
12716
+ ctx.globalCompositeOperation = mode ?? "source-over";
12717
+ },
12718
+ setLineDash(pattern, offset = 0) {
12719
+ ctx.setLineDash(pattern ? [...pattern] : []);
12720
+ ctx.lineDashOffset = offset;
12721
+ },
12722
+ setBlur(px) {
12723
+ ctx.filter = px && px > 0 ? `blur(${px}px)` : "none";
12724
+ },
12725
+ clipPath(d) {
12726
+ ctx.clip(new Path2D(d));
12727
+ },
12679
12728
  resolveTexture(url) {
12680
12729
  const img = getOrLoadImage(url, onAssetLoad);
12681
12730
  if (!img) return null;
@@ -12751,13 +12800,18 @@ function createWebPainter(ctx, onAssetLoad) {
12751
12800
  }
12752
12801
  };
12753
12802
  }
12754
- var handleByImage, imageByHandle;
12803
+ var handleByImage, imageByHandle, noiseHash, NOISE_CELLS;
12755
12804
  var init_webPainter2d = __esm({
12756
12805
  "lib/webPainter2d.ts"() {
12757
12806
  "use client";
12758
12807
  init_imageCache();
12759
12808
  handleByImage = /* @__PURE__ */ new WeakMap();
12760
12809
  imageByHandle = /* @__PURE__ */ new WeakMap();
12810
+ noiseHash = (x, y) => {
12811
+ const s = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453;
12812
+ return s - Math.floor(s);
12813
+ };
12814
+ NOISE_CELLS = 32;
12761
12815
  }
12762
12816
  });
12763
12817
 
@@ -12982,6 +13036,16 @@ function gradientStyle(g, ox, oy, scale) {
12982
13036
  stops: g.stops
12983
13037
  };
12984
13038
  }
13039
+ if (g.kind === "conic") {
13040
+ if (!g.center) return void 0;
13041
+ return {
13042
+ kind: "conic",
13043
+ cx: ox + g.center.x * scale,
13044
+ cy: oy + g.center.y * scale,
13045
+ angle: g.angle ?? 0,
13046
+ stops: g.stops
13047
+ };
13048
+ }
12985
13049
  if (!g.center || g.radius === void 0) return void 0;
12986
13050
  return {
12987
13051
  kind: "radial",
@@ -12991,6 +13055,13 @@ function gradientStyle(g, ox, oy, scale) {
12991
13055
  stops: g.stops
12992
13056
  };
12993
13057
  }
13058
+ function patternStyle(p, unit) {
13059
+ if (p.kind === "image") {
13060
+ if (!p.url) return void 0;
13061
+ return { kind: "image", url: p.url, scale: (p.scale ?? 1) / unit };
13062
+ }
13063
+ return { kind: "noise", scale: (p.scale ?? 2) / unit, alpha: p.alpha, color: p.color };
13064
+ }
12994
13065
  function DrawShape(props) {
12995
13066
  const register = React93.useContext(DrawableRegistryContext);
12996
13067
  if (register) {
@@ -13019,7 +13090,9 @@ var init_DrawShape = __esm({
13019
13090
  "radiusY",
13020
13091
  "width",
13021
13092
  "height",
13022
- "strokeWidth"
13093
+ "strokeWidth",
13094
+ "strokeDashOffset",
13095
+ "blur"
13023
13096
  ];
13024
13097
  lerp = (a, b, k) => a + (b - a) * k;
13025
13098
  paintShape = (painter, rawNode, dctx) => {
@@ -13029,6 +13102,16 @@ var init_DrawShape = __esm({
13029
13102
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
13030
13103
  const origin = dctx.projector.project(node.position);
13031
13104
  const tileWidth = dctx.projector.tileWidth;
13105
+ const groupScale = dctx.groupScale ?? 1;
13106
+ const strokePx = (node.strokeWidth ?? 1) / groupScale;
13107
+ if (node.blendMode) painter.setBlend(node.blendMode);
13108
+ if (node.strokeDash && node.strokeDash.length > 0) {
13109
+ painter.setLineDash(
13110
+ node.strokeDash.map((v) => v / groupScale),
13111
+ (node.strokeDashOffset ?? 0) / groupScale
13112
+ );
13113
+ }
13114
+ if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / groupScale);
13032
13115
  if (node.rotate) {
13033
13116
  const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
13034
13117
  const px = origin.x + pivot.x * tileWidth;
@@ -13037,15 +13120,18 @@ var init_DrawShape = __esm({
13037
13120
  painter.rotate(node.rotate);
13038
13121
  painter.translate(-px, -py);
13039
13122
  }
13040
- if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth });
13123
+ if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth * groupScale });
13041
13124
  const fill = node.fill === "none" ? void 0 : node.fill;
13042
13125
  const stroke = node.stroke === "none" ? void 0 : node.stroke;
13043
13126
  const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
13127
+ const pxStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, origin.x, origin.y, tileWidth) ?? stroke : stroke;
13128
+ const patFill = node.fillPattern ? patternStyle(node.fillPattern, groupScale) : void 0;
13044
13129
  switch (node.shape) {
13045
13130
  case "cell": {
13046
13131
  const pts = dctx.projector.cellPath(node.position);
13047
13132
  if (pxFill) painter.fillPoly(pts, pxFill);
13048
- if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
13133
+ if (patFill) painter.fillPoly(pts, patFill);
13134
+ if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
13049
13135
  break;
13050
13136
  }
13051
13137
  case "rect": {
@@ -13056,7 +13142,8 @@ var init_DrawShape = __esm({
13056
13142
  const w = (node.width ?? 0) * tw;
13057
13143
  const h = (node.height ?? 0) * tw;
13058
13144
  if (pxFill) painter.fillRect(x, y, w, h, pxFill);
13059
- if (stroke) painter.strokeRect(x, y, w, h, stroke, node.strokeWidth ?? 1);
13145
+ if (patFill) painter.fillRect(x, y, w, h, patFill);
13146
+ if (pxStroke) painter.strokeRect(x, y, w, h, pxStroke, strokePx);
13060
13147
  break;
13061
13148
  }
13062
13149
  case "ellipse": {
@@ -13067,7 +13154,8 @@ var init_DrawShape = __esm({
13067
13154
  const rx = (node.radiusX ?? 0) * tw;
13068
13155
  const ry = (node.radiusY ?? rx) * tw;
13069
13156
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
13070
- if (stroke) painter.strokeEllipse(cx, cy, rx, ry, stroke, node.strokeWidth ?? 1);
13157
+ if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
13158
+ if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
13071
13159
  break;
13072
13160
  }
13073
13161
  case "poly": {
@@ -13076,16 +13164,27 @@ var init_DrawShape = __esm({
13076
13164
  y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
13077
13165
  }));
13078
13166
  if (pxFill) painter.fillPoly(pts, pxFill);
13079
- if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
13167
+ if (patFill) painter.fillPoly(pts, patFill);
13168
+ if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
13080
13169
  break;
13081
13170
  }
13082
13171
  case "path": {
13083
13172
  if (!node.d) break;
13084
13173
  painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
13085
13174
  painter.scale(tileWidth, tileWidth);
13175
+ if (node.strokeDash && node.strokeDash.length > 0) {
13176
+ painter.setLineDash(
13177
+ node.strokeDash.map((v) => v / (groupScale * tileWidth)),
13178
+ (node.strokeDashOffset ?? 0) / (groupScale * tileWidth)
13179
+ );
13180
+ }
13181
+ if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / (groupScale * tileWidth));
13086
13182
  const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
13183
+ const localStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, 0, 0, 1) ?? stroke : stroke;
13184
+ const localPat = node.fillPattern ? patternStyle(node.fillPattern, groupScale * tileWidth) : void 0;
13087
13185
  if (localFill) painter.fillPath(node.d, localFill);
13088
- if (stroke) painter.strokePath(node.d, stroke, (node.strokeWidth ?? 1) / tileWidth);
13186
+ if (localPat) painter.fillPath(node.d, localPat);
13187
+ if (localStroke) painter.strokePath(node.d, localStroke, strokePx / tileWidth);
13089
13188
  break;
13090
13189
  }
13091
13190
  }
@@ -13166,8 +13265,6 @@ var init_DrawTextLayer = __esm({
13166
13265
  };
13167
13266
  }
13168
13267
  });
13169
-
13170
- // lib/drawable/paintDispatch.ts
13171
13268
  function paintDrawable(painter, node, dctx) {
13172
13269
  switch (node.type) {
13173
13270
  case "draw-sprite":
@@ -13188,10 +13285,20 @@ function paintDrawable(painter, node, dctx) {
13188
13285
  if (node.scale !== void 0) painter.scale(node.scale, node.scale);
13189
13286
  if (node.rotate !== void 0) painter.rotate(node.rotate);
13190
13287
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
13191
- for (const item of node.items) paintDrawable(painter, item, dctx);
13288
+ if (node.clip) {
13289
+ const tw = dctx.projector.tileWidth;
13290
+ painter.scale(tw, tw);
13291
+ painter.clipPath(node.clip);
13292
+ painter.scale(1 / tw, 1 / tw);
13293
+ }
13294
+ const childCtx = node.scale !== void 0 && node.scale !== 1 ? { ...dctx, groupScale: (dctx.groupScale ?? 1) * node.scale } : dctx;
13295
+ for (const item of node.items) paintDrawable(painter, item, childCtx);
13192
13296
  painter.restore();
13193
13297
  break;
13194
13298
  }
13299
+ case "draw-mesh":
13300
+ warnUnsupported2d("draw-mesh");
13301
+ break;
13195
13302
  case "draw-sprite-layer":
13196
13303
  paintSpriteLayer(painter, node, dctx);
13197
13304
  break;
@@ -13203,6 +13310,7 @@ function paintDrawable(painter, node, dctx) {
13203
13310
  break;
13204
13311
  }
13205
13312
  }
13313
+ var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
13206
13314
  var init_paintDispatch = __esm({
13207
13315
  "lib/drawable/paintDispatch.ts"() {
13208
13316
  init_contract();
@@ -13212,6 +13320,13 @@ var init_paintDispatch = __esm({
13212
13320
  init_DrawSpriteLayer();
13213
13321
  init_DrawShapeLayer();
13214
13322
  init_DrawTextLayer();
13323
+ paint2dLog = logger.createLogger("almadar:ui:drawable-2d");
13324
+ warnedUnsupported2d = /* @__PURE__ */ new Set();
13325
+ warnUnsupported2d = (kind) => {
13326
+ if (warnedUnsupported2d.has(kind)) return;
13327
+ warnedUnsupported2d.add(kind);
13328
+ paint2dLog.warn("unsupported drawable kind on the 2D painter \u2014 skipped", { kind });
13329
+ };
13215
13330
  }
13216
13331
  });
13217
13332
 
@@ -13226,6 +13341,7 @@ function collectDrawnItems(nodes) {
13226
13341
  case "draw-shape":
13227
13342
  case "draw-text":
13228
13343
  case "draw-group":
13344
+ case "draw-mesh":
13229
13345
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
13230
13346
  break;
13231
13347
  case "draw-sprite-layer":
@@ -13843,6 +13959,7 @@ function Canvas({
13843
13959
  isLoading,
13844
13960
  cameraMode: to3DCameraMode(camera?.mode),
13845
13961
  ...zoom !== void 0 ? { scale: zoom } : {},
13962
+ ...camera?.fov !== void 0 ? { fov: camera.fov } : {},
13846
13963
  ...camera?.target !== void 0 ? { followTarget: camera.target } : {},
13847
13964
  unitScale,
13848
13965
  backgroundColor,
@@ -40784,14 +40901,26 @@ var init_DetailPanel = __esm({
40784
40901
  DetailPanel.displayName = "DetailPanel";
40785
40902
  }
40786
40903
  });
40787
-
40788
- // components/game/atoms/DrawGroup.tsx
40789
- function DrawGroup(_props) {
40904
+ function DrawGroup(props) {
40905
+ const register = React93.useContext(DrawableRegistryContext);
40906
+ if (register) register({ ...props, type: "draw-group" });
40790
40907
  return null;
40791
40908
  }
40792
40909
  var init_DrawGroup = __esm({
40793
40910
  "components/game/atoms/DrawGroup.tsx"() {
40794
40911
  "use client";
40912
+ init_registry();
40913
+ }
40914
+ });
40915
+ function DrawMesh(props) {
40916
+ const register = React93.useContext(DrawableRegistryContext);
40917
+ if (register) register({ ...props, type: "draw-mesh" });
40918
+ return null;
40919
+ }
40920
+ var init_DrawMesh = __esm({
40921
+ "components/game/atoms/DrawMesh.tsx"() {
40922
+ "use client";
40923
+ init_registry();
40795
40924
  }
40796
40925
  });
40797
40926
  function extractTitle(children) {
@@ -46517,6 +46646,7 @@ var init_component_registry_generated = __esm({
46517
46646
  init_DocTOC();
46518
46647
  init_DocumentViewer();
46519
46648
  init_DrawGroup();
46649
+ init_DrawMesh();
46520
46650
  init_DrawShape();
46521
46651
  init_DrawShapeLayer();
46522
46652
  init_DrawSprite();
@@ -46785,6 +46915,7 @@ var init_component_registry_generated = __esm({
46785
46915
  "DocTOC": DocTOC,
46786
46916
  "DocumentViewer": DocumentViewer,
46787
46917
  "DrawGroup": DrawGroup,
46918
+ "DrawMesh": DrawMesh,
46788
46919
  "DrawShape": DrawShape,
46789
46920
  "DrawShapeLayer": DrawShapeLayer,
46790
46921
  "DrawSprite": DrawSprite,
package/dist/avl/index.js CHANGED
@@ -12553,9 +12553,26 @@ var init_imageCache = __esm({
12553
12553
  });
12554
12554
 
12555
12555
  // lib/webPainter2d.ts
12556
+ function makeNoiseTile(alpha, color) {
12557
+ const tile = document.createElement("canvas");
12558
+ tile.width = NOISE_CELLS;
12559
+ tile.height = NOISE_CELLS;
12560
+ const t = tile.getContext("2d");
12561
+ if (t) {
12562
+ t.fillStyle = color;
12563
+ for (let y = 0; y < NOISE_CELLS; y++) {
12564
+ for (let x = 0; x < NOISE_CELLS; x++) {
12565
+ t.globalAlpha = noiseHash(x, y) * alpha;
12566
+ t.fillRect(x, y, 1, 1);
12567
+ }
12568
+ }
12569
+ }
12570
+ return tile;
12571
+ }
12556
12572
  function createWebPainter(ctx, onAssetLoad) {
12557
12573
  let vw = 0;
12558
12574
  let vh = 0;
12575
+ const patternCache = /* @__PURE__ */ new Map();
12559
12576
  const tracePoly = (points, closed) => {
12560
12577
  if (points.length === 0) return;
12561
12578
  ctx.beginPath();
@@ -12563,9 +12580,28 @@ function createWebPainter(ctx, onAssetLoad) {
12563
12580
  for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
12564
12581
  if (closed) ctx.closePath();
12565
12582
  };
12583
+ const toCanvasPattern = (style) => {
12584
+ const key = JSON.stringify(style);
12585
+ let pattern = patternCache.get(key);
12586
+ if (pattern === void 0) {
12587
+ if (style.kind === "noise") {
12588
+ pattern = ctx.createPattern(makeNoiseTile(style.alpha ?? 0.12, style.color ?? "#000000"), "repeat");
12589
+ } else {
12590
+ const img = getOrLoadImage(style.url, onAssetLoad);
12591
+ if (!img) return "rgba(0,0,0,0)";
12592
+ pattern = ctx.createPattern(img, "repeat");
12593
+ }
12594
+ if (pattern && style.scale !== void 0 && style.scale !== 1) {
12595
+ pattern.setTransform(new DOMMatrix().scale(style.scale));
12596
+ }
12597
+ patternCache.set(key, pattern);
12598
+ }
12599
+ return pattern ?? "rgba(0,0,0,0)";
12600
+ };
12566
12601
  const toCanvasStyle = (style) => {
12567
12602
  if (typeof style === "string") return style;
12568
- const g = style.kind === "linear" ? ctx.createLinearGradient(style.x1, style.y1, style.x2, style.y2) : ctx.createRadialGradient(style.cx, style.cy, 0, style.cx, style.cy, style.r);
12603
+ if (style.kind === "noise" || style.kind === "image") return toCanvasPattern(style);
12604
+ const g = style.kind === "linear" ? ctx.createLinearGradient(style.x1, style.y1, style.x2, style.y2) : style.kind === "conic" ? ctx.createConicGradient(style.angle, style.cx, style.cy) : ctx.createRadialGradient(style.cx, style.cy, 0, style.cx, style.cy, style.r);
12569
12605
  for (const stop of style.stops) g.addColorStop(stop.offset, stop.color);
12570
12606
  return g;
12571
12607
  };
@@ -12600,6 +12636,19 @@ function createWebPainter(ctx, onAssetLoad) {
12600
12636
  ctx.shadowColor = shadow ? shadow.color : "transparent";
12601
12637
  ctx.shadowBlur = shadow ? shadow.blur : 0;
12602
12638
  },
12639
+ setBlend(mode) {
12640
+ ctx.globalCompositeOperation = mode ?? "source-over";
12641
+ },
12642
+ setLineDash(pattern, offset = 0) {
12643
+ ctx.setLineDash(pattern ? [...pattern] : []);
12644
+ ctx.lineDashOffset = offset;
12645
+ },
12646
+ setBlur(px) {
12647
+ ctx.filter = px && px > 0 ? `blur(${px}px)` : "none";
12648
+ },
12649
+ clipPath(d) {
12650
+ ctx.clip(new Path2D(d));
12651
+ },
12603
12652
  resolveTexture(url) {
12604
12653
  const img = getOrLoadImage(url, onAssetLoad);
12605
12654
  if (!img) return null;
@@ -12675,13 +12724,18 @@ function createWebPainter(ctx, onAssetLoad) {
12675
12724
  }
12676
12725
  };
12677
12726
  }
12678
- var handleByImage, imageByHandle;
12727
+ var handleByImage, imageByHandle, noiseHash, NOISE_CELLS;
12679
12728
  var init_webPainter2d = __esm({
12680
12729
  "lib/webPainter2d.ts"() {
12681
12730
  "use client";
12682
12731
  init_imageCache();
12683
12732
  handleByImage = /* @__PURE__ */ new WeakMap();
12684
12733
  imageByHandle = /* @__PURE__ */ new WeakMap();
12734
+ noiseHash = (x, y) => {
12735
+ const s = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453;
12736
+ return s - Math.floor(s);
12737
+ };
12738
+ NOISE_CELLS = 32;
12685
12739
  }
12686
12740
  });
12687
12741
 
@@ -12906,6 +12960,16 @@ function gradientStyle(g, ox, oy, scale) {
12906
12960
  stops: g.stops
12907
12961
  };
12908
12962
  }
12963
+ if (g.kind === "conic") {
12964
+ if (!g.center) return void 0;
12965
+ return {
12966
+ kind: "conic",
12967
+ cx: ox + g.center.x * scale,
12968
+ cy: oy + g.center.y * scale,
12969
+ angle: g.angle ?? 0,
12970
+ stops: g.stops
12971
+ };
12972
+ }
12909
12973
  if (!g.center || g.radius === void 0) return void 0;
12910
12974
  return {
12911
12975
  kind: "radial",
@@ -12915,6 +12979,13 @@ function gradientStyle(g, ox, oy, scale) {
12915
12979
  stops: g.stops
12916
12980
  };
12917
12981
  }
12982
+ function patternStyle(p, unit) {
12983
+ if (p.kind === "image") {
12984
+ if (!p.url) return void 0;
12985
+ return { kind: "image", url: p.url, scale: (p.scale ?? 1) / unit };
12986
+ }
12987
+ return { kind: "noise", scale: (p.scale ?? 2) / unit, alpha: p.alpha, color: p.color };
12988
+ }
12918
12989
  function DrawShape(props) {
12919
12990
  const register = useContext(DrawableRegistryContext);
12920
12991
  if (register) {
@@ -12943,7 +13014,9 @@ var init_DrawShape = __esm({
12943
13014
  "radiusY",
12944
13015
  "width",
12945
13016
  "height",
12946
- "strokeWidth"
13017
+ "strokeWidth",
13018
+ "strokeDashOffset",
13019
+ "blur"
12947
13020
  ];
12948
13021
  lerp = (a, b, k) => a + (b - a) * k;
12949
13022
  paintShape = (painter, rawNode, dctx) => {
@@ -12953,6 +13026,16 @@ var init_DrawShape = __esm({
12953
13026
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
12954
13027
  const origin = dctx.projector.project(node.position);
12955
13028
  const tileWidth = dctx.projector.tileWidth;
13029
+ const groupScale = dctx.groupScale ?? 1;
13030
+ const strokePx = (node.strokeWidth ?? 1) / groupScale;
13031
+ if (node.blendMode) painter.setBlend(node.blendMode);
13032
+ if (node.strokeDash && node.strokeDash.length > 0) {
13033
+ painter.setLineDash(
13034
+ node.strokeDash.map((v) => v / groupScale),
13035
+ (node.strokeDashOffset ?? 0) / groupScale
13036
+ );
13037
+ }
13038
+ if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / groupScale);
12956
13039
  if (node.rotate) {
12957
13040
  const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
12958
13041
  const px = origin.x + pivot.x * tileWidth;
@@ -12961,15 +13044,18 @@ var init_DrawShape = __esm({
12961
13044
  painter.rotate(node.rotate);
12962
13045
  painter.translate(-px, -py);
12963
13046
  }
12964
- if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth });
13047
+ if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth * groupScale });
12965
13048
  const fill = node.fill === "none" ? void 0 : node.fill;
12966
13049
  const stroke = node.stroke === "none" ? void 0 : node.stroke;
12967
13050
  const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
13051
+ const pxStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, origin.x, origin.y, tileWidth) ?? stroke : stroke;
13052
+ const patFill = node.fillPattern ? patternStyle(node.fillPattern, groupScale) : void 0;
12968
13053
  switch (node.shape) {
12969
13054
  case "cell": {
12970
13055
  const pts = dctx.projector.cellPath(node.position);
12971
13056
  if (pxFill) painter.fillPoly(pts, pxFill);
12972
- if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
13057
+ if (patFill) painter.fillPoly(pts, patFill);
13058
+ if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
12973
13059
  break;
12974
13060
  }
12975
13061
  case "rect": {
@@ -12980,7 +13066,8 @@ var init_DrawShape = __esm({
12980
13066
  const w = (node.width ?? 0) * tw;
12981
13067
  const h = (node.height ?? 0) * tw;
12982
13068
  if (pxFill) painter.fillRect(x, y, w, h, pxFill);
12983
- if (stroke) painter.strokeRect(x, y, w, h, stroke, node.strokeWidth ?? 1);
13069
+ if (patFill) painter.fillRect(x, y, w, h, patFill);
13070
+ if (pxStroke) painter.strokeRect(x, y, w, h, pxStroke, strokePx);
12984
13071
  break;
12985
13072
  }
12986
13073
  case "ellipse": {
@@ -12991,7 +13078,8 @@ var init_DrawShape = __esm({
12991
13078
  const rx = (node.radiusX ?? 0) * tw;
12992
13079
  const ry = (node.radiusY ?? rx) * tw;
12993
13080
  if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
12994
- if (stroke) painter.strokeEllipse(cx, cy, rx, ry, stroke, node.strokeWidth ?? 1);
13081
+ if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
13082
+ if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
12995
13083
  break;
12996
13084
  }
12997
13085
  case "poly": {
@@ -13000,16 +13088,27 @@ var init_DrawShape = __esm({
13000
13088
  y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
13001
13089
  }));
13002
13090
  if (pxFill) painter.fillPoly(pts, pxFill);
13003
- if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
13091
+ if (patFill) painter.fillPoly(pts, patFill);
13092
+ if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
13004
13093
  break;
13005
13094
  }
13006
13095
  case "path": {
13007
13096
  if (!node.d) break;
13008
13097
  painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
13009
13098
  painter.scale(tileWidth, tileWidth);
13099
+ if (node.strokeDash && node.strokeDash.length > 0) {
13100
+ painter.setLineDash(
13101
+ node.strokeDash.map((v) => v / (groupScale * tileWidth)),
13102
+ (node.strokeDashOffset ?? 0) / (groupScale * tileWidth)
13103
+ );
13104
+ }
13105
+ if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / (groupScale * tileWidth));
13010
13106
  const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
13107
+ const localStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, 0, 0, 1) ?? stroke : stroke;
13108
+ const localPat = node.fillPattern ? patternStyle(node.fillPattern, groupScale * tileWidth) : void 0;
13011
13109
  if (localFill) painter.fillPath(node.d, localFill);
13012
- if (stroke) painter.strokePath(node.d, stroke, (node.strokeWidth ?? 1) / tileWidth);
13110
+ if (localPat) painter.fillPath(node.d, localPat);
13111
+ if (localStroke) painter.strokePath(node.d, localStroke, strokePx / tileWidth);
13013
13112
  break;
13014
13113
  }
13015
13114
  }
@@ -13090,8 +13189,6 @@ var init_DrawTextLayer = __esm({
13090
13189
  };
13091
13190
  }
13092
13191
  });
13093
-
13094
- // lib/drawable/paintDispatch.ts
13095
13192
  function paintDrawable(painter, node, dctx) {
13096
13193
  switch (node.type) {
13097
13194
  case "draw-sprite":
@@ -13112,10 +13209,20 @@ function paintDrawable(painter, node, dctx) {
13112
13209
  if (node.scale !== void 0) painter.scale(node.scale, node.scale);
13113
13210
  if (node.rotate !== void 0) painter.rotate(node.rotate);
13114
13211
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
13115
- for (const item of node.items) paintDrawable(painter, item, dctx);
13212
+ if (node.clip) {
13213
+ const tw = dctx.projector.tileWidth;
13214
+ painter.scale(tw, tw);
13215
+ painter.clipPath(node.clip);
13216
+ painter.scale(1 / tw, 1 / tw);
13217
+ }
13218
+ const childCtx = node.scale !== void 0 && node.scale !== 1 ? { ...dctx, groupScale: (dctx.groupScale ?? 1) * node.scale } : dctx;
13219
+ for (const item of node.items) paintDrawable(painter, item, childCtx);
13116
13220
  painter.restore();
13117
13221
  break;
13118
13222
  }
13223
+ case "draw-mesh":
13224
+ warnUnsupported2d("draw-mesh");
13225
+ break;
13119
13226
  case "draw-sprite-layer":
13120
13227
  paintSpriteLayer(painter, node, dctx);
13121
13228
  break;
@@ -13127,6 +13234,7 @@ function paintDrawable(painter, node, dctx) {
13127
13234
  break;
13128
13235
  }
13129
13236
  }
13237
+ var paint2dLog, warnedUnsupported2d, warnUnsupported2d;
13130
13238
  var init_paintDispatch = __esm({
13131
13239
  "lib/drawable/paintDispatch.ts"() {
13132
13240
  init_contract();
@@ -13136,6 +13244,13 @@ var init_paintDispatch = __esm({
13136
13244
  init_DrawSpriteLayer();
13137
13245
  init_DrawShapeLayer();
13138
13246
  init_DrawTextLayer();
13247
+ paint2dLog = createLogger("almadar:ui:drawable-2d");
13248
+ warnedUnsupported2d = /* @__PURE__ */ new Set();
13249
+ warnUnsupported2d = (kind) => {
13250
+ if (warnedUnsupported2d.has(kind)) return;
13251
+ warnedUnsupported2d.add(kind);
13252
+ paint2dLog.warn("unsupported drawable kind on the 2D painter \u2014 skipped", { kind });
13253
+ };
13139
13254
  }
13140
13255
  });
13141
13256
 
@@ -13150,6 +13265,7 @@ function collectDrawnItems(nodes) {
13150
13265
  case "draw-shape":
13151
13266
  case "draw-text":
13152
13267
  case "draw-group":
13268
+ case "draw-mesh":
13153
13269
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
13154
13270
  break;
13155
13271
  case "draw-sprite-layer":
@@ -13767,6 +13883,7 @@ function Canvas({
13767
13883
  isLoading,
13768
13884
  cameraMode: to3DCameraMode(camera?.mode),
13769
13885
  ...zoom !== void 0 ? { scale: zoom } : {},
13886
+ ...camera?.fov !== void 0 ? { fov: camera.fov } : {},
13770
13887
  ...camera?.target !== void 0 ? { followTarget: camera.target } : {},
13771
13888
  unitScale,
13772
13889
  backgroundColor,
@@ -40708,14 +40825,26 @@ var init_DetailPanel = __esm({
40708
40825
  DetailPanel.displayName = "DetailPanel";
40709
40826
  }
40710
40827
  });
40711
-
40712
- // components/game/atoms/DrawGroup.tsx
40713
- function DrawGroup(_props) {
40828
+ function DrawGroup(props) {
40829
+ const register = useContext(DrawableRegistryContext);
40830
+ if (register) register({ ...props, type: "draw-group" });
40714
40831
  return null;
40715
40832
  }
40716
40833
  var init_DrawGroup = __esm({
40717
40834
  "components/game/atoms/DrawGroup.tsx"() {
40718
40835
  "use client";
40836
+ init_registry();
40837
+ }
40838
+ });
40839
+ function DrawMesh(props) {
40840
+ const register = useContext(DrawableRegistryContext);
40841
+ if (register) register({ ...props, type: "draw-mesh" });
40842
+ return null;
40843
+ }
40844
+ var init_DrawMesh = __esm({
40845
+ "components/game/atoms/DrawMesh.tsx"() {
40846
+ "use client";
40847
+ init_registry();
40719
40848
  }
40720
40849
  });
40721
40850
  function extractTitle(children) {
@@ -46441,6 +46570,7 @@ var init_component_registry_generated = __esm({
46441
46570
  init_DocTOC();
46442
46571
  init_DocumentViewer();
46443
46572
  init_DrawGroup();
46573
+ init_DrawMesh();
46444
46574
  init_DrawShape();
46445
46575
  init_DrawShapeLayer();
46446
46576
  init_DrawSprite();
@@ -46709,6 +46839,7 @@ var init_component_registry_generated = __esm({
46709
46839
  "DocTOC": DocTOC,
46710
46840
  "DocumentViewer": DocumentViewer,
46711
46841
  "DrawGroup": DrawGroup,
46842
+ "DrawMesh": DrawMesh,
46712
46843
  "DrawShape": DrawShape,
46713
46844
  "DrawShapeLayer": DrawShapeLayer,
46714
46845
  "DrawSprite": DrawSprite,
@@ -1,5 +1,5 @@
1
1
  import { OrbitalSchema, SExpr, Effect, OrbitalVerificationAPI, TraitStateSnapshot, EventPayload, BusEvent, VerificationCheck, BridgeHealth, VerificationSnapshot, VerificationSummary, TransitionTrace, ServerResponseTrace, CheckStatus, AssetLoadStatus } from '@almadar/core';
2
- import { D as DrawableNode } from './paintDispatch-B5pPeSEp.js';
2
+ import { D as DrawableNode } from './paintDispatch-BQn5Lyx1.js';
3
3
  import { ClassValue } from 'clsx';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { OrbitalSchema, SExpr, Effect, OrbitalVerificationAPI, TraitStateSnapshot, EventPayload, BusEvent, VerificationCheck, BridgeHealth, VerificationSnapshot, VerificationSummary, TransitionTrace, ServerResponseTrace, CheckStatus, AssetLoadStatus } from '@almadar/core';
2
- import { D as DrawableNode } from './paintDispatch-B5pPeSEp.cjs';
2
+ import { D as DrawableNode } from './paintDispatch-BQn5Lyx1.cjs';
3
3
  import { ClassValue } from 'clsx';
4
4
 
5
5
  /**