@chanmeng666/archlang 0.4.0 → 0.5.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.
@@ -301,6 +301,58 @@ function levenshtein(a, b) {
301
301
  return dp[m];
302
302
  }
303
303
 
304
+ // src/theme.ts
305
+ var DEFAULT_THEME = {
306
+ bg: "#ffffff",
307
+ pocheBase: "#e9e4db",
308
+ pocheHatch: "#b9b1a4",
309
+ wallStroke: "#1b1b1b",
310
+ roomFill: "#fbfaf7",
311
+ roomLabel: "#222222",
312
+ areaLabel: "#7a7a7a",
313
+ furnitureStroke: "#a8a29a",
314
+ furnitureFill: "#f4f2ee",
315
+ furnitureLabel: "#9a948c",
316
+ opening: "#ffffff",
317
+ doorLeaf: "#555555",
318
+ windowPane: "#3a6ea5",
319
+ dim: "#0E5484",
320
+ annotation: "#333333",
321
+ annotationMuted: "#888888",
322
+ column: "#4a4a4a",
323
+ lineWeight: 1,
324
+ font: "Helvetica, Arial, sans-serif"
325
+ };
326
+ var ALIASES = {
327
+ background: "bg",
328
+ wall: "wallStroke",
329
+ wallFill: "pocheBase",
330
+ wallHatch: "pocheHatch",
331
+ room: "roomFill",
332
+ furniture: "furnitureFill",
333
+ door: "doorLeaf",
334
+ window: "windowPane"
335
+ };
336
+ function resolveThemeKey(key) {
337
+ if (key in DEFAULT_THEME) return key;
338
+ if (key in ALIASES) return ALIASES[key];
339
+ return null;
340
+ }
341
+ function isNumericThemeKey(key) {
342
+ return key === "lineWeight";
343
+ }
344
+ function mergeTheme(...layers) {
345
+ const out = { ...DEFAULT_THEME };
346
+ for (const layer of layers) {
347
+ if (!layer) continue;
348
+ for (const k of Object.keys(layer)) {
349
+ const v = layer[k];
350
+ if (v !== void 0) out[k] = v;
351
+ }
352
+ }
353
+ return out;
354
+ }
355
+
304
356
  // src/geometry.ts
305
357
  var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
306
358
  var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
@@ -392,6 +444,46 @@ function isOnSomeWall(walls, at, ref) {
392
444
  return false;
393
445
  }
394
446
 
447
+ // src/hatches.ts
448
+ var KNOWN_MATERIALS = ["poche", "concrete", "brick", "insulation", "tile", "none"];
449
+ var DEFAULT_MATERIAL = "poche";
450
+ function patternId(material) {
451
+ return material === "poche" ? "poche" : `hatch-${material}`;
452
+ }
453
+ var HATCHES = {
454
+ poche: (id, c) => `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${c.fmt(c.gap)}" height="${c.fmt(c.gap)}" patternTransform="rotate(45)"><rect width="${c.fmt(c.gap)}" height="${c.fmt(c.gap)}" fill="${c.base}"/><line x1="0" y1="0" x2="0" y2="${c.fmt(c.gap)}" stroke="${c.line}" stroke-width="${c.fmt(c.thin * 0.7)}"/></pattern>`,
455
+ // Aggregate speckle.
456
+ concrete: (id, c) => {
457
+ const w = c.gap * 1.6;
458
+ return `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${c.fmt(w)}" height="${c.fmt(w)}"><rect width="${c.fmt(w)}" height="${c.fmt(w)}" fill="${c.base}"/><circle cx="${c.fmt(w * 0.25)}" cy="${c.fmt(w * 0.3)}" r="${c.fmt(c.thin * 0.9)}" fill="${c.line}"/><circle cx="${c.fmt(w * 0.7)}" cy="${c.fmt(w * 0.62)}" r="${c.fmt(c.thin * 0.6)}" fill="${c.line}"/><circle cx="${c.fmt(w * 0.45)}" cy="${c.fmt(w * 0.85)}" r="${c.fmt(c.thin * 0.75)}" fill="${c.line}"/></pattern>`;
459
+ },
460
+ // Running-bond brick courses.
461
+ brick: (id, c) => {
462
+ const w = c.gap * 3;
463
+ const h = c.gap * 1.4;
464
+ const sw = c.fmt(c.thin * 0.6);
465
+ return `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${c.fmt(w)}" height="${c.fmt(h)}"><rect width="${c.fmt(w)}" height="${c.fmt(h)}" fill="${c.base}"/><line x1="0" y1="${c.fmt(h)}" x2="${c.fmt(w)}" y2="${c.fmt(h)}" stroke="${c.line}" stroke-width="${sw}"/><line x1="0" y1="${c.fmt(h / 2)}" x2="${c.fmt(w)}" y2="${c.fmt(h / 2)}" stroke="${c.line}" stroke-width="${sw}"/><line x1="${c.fmt(w / 2)}" y1="0" x2="${c.fmt(w / 2)}" y2="${c.fmt(h / 2)}" stroke="${c.line}" stroke-width="${sw}"/><line x1="0" y1="${c.fmt(h / 2)}" x2="0" y2="${c.fmt(h)}" stroke="${c.line}" stroke-width="${sw}"/><line x1="${c.fmt(w)}" y1="${c.fmt(h / 2)}" x2="${c.fmt(w)}" y2="${c.fmt(h)}" stroke="${c.line}" stroke-width="${sw}"/></pattern>`;
466
+ },
467
+ // Cross-hatch batting.
468
+ insulation: (id, c) => {
469
+ const w = c.gap * 1.2;
470
+ return `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${c.fmt(w)}" height="${c.fmt(w)}"><rect width="${c.fmt(w)}" height="${c.fmt(w)}" fill="${c.base}"/><path d="M0,0 L${c.fmt(w)},${c.fmt(w)} M${c.fmt(w)},0 L0,${c.fmt(w)}" stroke="${c.line}" stroke-width="${c.fmt(c.thin * 0.5)}" fill="none"/></pattern>`;
471
+ },
472
+ // Square tile grid.
473
+ tile: (id, c) => {
474
+ const w = c.gap * 1.8;
475
+ return `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${c.fmt(w)}" height="${c.fmt(w)}"><rect width="${c.fmt(w)}" height="${c.fmt(w)}" fill="${c.base}"/><rect x="0" y="0" width="${c.fmt(w)}" height="${c.fmt(w)}" fill="none" stroke="${c.line}" stroke-width="${c.fmt(c.thin * 0.6)}"/></pattern>`;
476
+ },
477
+ // Solid fill, no hatch.
478
+ none: (id, c) => `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${c.fmt(c.gap)}" height="${c.fmt(c.gap)}"><rect width="${c.fmt(c.gap)}" height="${c.fmt(c.gap)}" fill="${c.base}"/></pattern>`
479
+ };
480
+ function isKnownMaterial(name) {
481
+ return KNOWN_MATERIALS.includes(name);
482
+ }
483
+ function hatchPattern(material, c) {
484
+ return HATCHES[material](patternId(material), c);
485
+ }
486
+
395
487
  // src/elements/wall.ts
396
488
  var wall = {
397
489
  kind: "wall",
@@ -402,6 +494,11 @@ var wall = {
402
494
  const category = ctx.eatIdent().value;
403
495
  ctx.eatKeyword("thickness");
404
496
  const thickness = ctx.parseExpr();
497
+ let material;
498
+ if (ctx.isKeyword("material")) {
499
+ ctx.next();
500
+ material = ctx.eatIdent().value;
501
+ }
405
502
  ctx.eat("lcurly");
406
503
  const points = [];
407
504
  let closed = false;
@@ -419,7 +516,7 @@ var wall = {
419
516
  }
420
517
  ctx.eat("rcurly");
421
518
  if (points.length < 2) ctx.fail("A wall needs at least two points", kw);
422
- return { kind: "wall", id, category, thickness, points, closed, line: kw.line };
519
+ return { kind: "wall", id, category, thickness, material, points, closed, line: kw.line };
423
520
  },
424
521
  idPrefix: (node) => node.category || "wall",
425
522
  resolve(node, ctx) {
@@ -431,7 +528,18 @@ var wall = {
431
528
  if (thickness <= 0) {
432
529
  ctx.diag({ severity: "error", message: `Wall "${id}" must have a positive thickness`, code: "E_WALL_THICKNESS", span: n.span });
433
530
  }
434
- return { kind: "wall", id, category: n.category, thickness, points, closed: n.closed, span: n.span };
531
+ let material = DEFAULT_MATERIAL;
532
+ if (n.material !== void 0) {
533
+ if (isKnownMaterial(n.material)) material = n.material;
534
+ else
535
+ ctx.diag({
536
+ severity: "warning",
537
+ message: `Unknown wall material "${n.material}" (known: ${KNOWN_MATERIALS.join(", ")}); using the default hatch`,
538
+ code: "W_UNKNOWN_MATERIAL",
539
+ span: n.span
540
+ });
541
+ }
542
+ return { kind: "wall", id, category: n.category, thickness, material, points, closed: n.closed, span: n.span };
435
543
  },
436
544
  bounds(resolved) {
437
545
  const w = resolved;
@@ -873,7 +981,7 @@ register(dim);
873
981
  register(column);
874
982
 
875
983
  // src/parser.ts
876
- var SETTINGS = ["units", "grid", "scale", "north", "title", "let", "component"];
984
+ var SETTINGS = ["units", "grid", "scale", "north", "title", "theme", "let", "component"];
877
985
  var STATEMENT_STARTS = /* @__PURE__ */ new Set([...SETTINGS, ...registry.keys()]);
878
986
  var ParseError = class extends Error {
879
987
  constructor(message, span) {
@@ -1036,6 +1144,10 @@ var Parser = class {
1036
1144
  plan.title = n;
1037
1145
  break;
1038
1146
  }
1147
+ case "theme": {
1148
+ plan.theme = { ...plan.theme, ...this.parseTheme() };
1149
+ break;
1150
+ }
1039
1151
  case "let": {
1040
1152
  const n = this.parseLet();
1041
1153
  n.span = this.spanFrom(start);
@@ -1145,6 +1257,35 @@ var Parser = class {
1145
1257
  this.eat("rcurly");
1146
1258
  return node;
1147
1259
  }
1260
+ /** `theme { key: <value> … }` — colours (strings), `lineWeight` (number), `font` (string). */
1261
+ parseTheme() {
1262
+ this.eatKeyword("theme");
1263
+ this.eat("lcurly");
1264
+ const t = {};
1265
+ while (!this.isType("rcurly") && !this.isType("eof")) {
1266
+ const keyTok = this.eatIdent();
1267
+ if (this.isType("colon")) this.next();
1268
+ const resolved = resolveThemeKey(keyTok.value);
1269
+ if (!resolved) {
1270
+ this.diagnostics.push({
1271
+ severity: "warning",
1272
+ message: `Unknown theme key "${keyTok.value}"`,
1273
+ code: "W_UNKNOWN_THEME_KEY",
1274
+ span: { start: keyTok.start, end: keyTok.end }
1275
+ });
1276
+ if (this.isType("string") || this.isType("number")) this.next();
1277
+ else this.fail(`Expected a value for theme key "${keyTok.value}"`);
1278
+ continue;
1279
+ }
1280
+ if (isNumericThemeKey(resolved)) {
1281
+ t[resolved] = this.eatNumber();
1282
+ } else {
1283
+ t[resolved] = this.eatString();
1284
+ }
1285
+ }
1286
+ this.eat("rcurly");
1287
+ return t;
1288
+ }
1148
1289
  parseLet() {
1149
1290
  const kw = this.eatKeyword("let");
1150
1291
  const name = this.eatIdent().value;
@@ -1343,6 +1484,7 @@ function resolve(ast) {
1343
1484
  scale: ast.scale,
1344
1485
  north: ast.north,
1345
1486
  title: ast.title,
1487
+ theme: ast.theme,
1346
1488
  elements,
1347
1489
  walls
1348
1490
  };
@@ -1362,26 +1504,104 @@ var RENDER_PASSES = [
1362
1504
  "annotations"
1363
1505
  ];
1364
1506
 
1507
+ // src/geometry/union.ts
1508
+ function uniqSorted(values) {
1509
+ const out = [...new Set(values)].sort((a, b) => a - b);
1510
+ return out;
1511
+ }
1512
+ function rectUnionOutline(rects) {
1513
+ if (rects.length === 0) return [];
1514
+ const xs = uniqSorted(rects.flatMap((r) => [r.x0, r.x1]));
1515
+ const ys = uniqSorted(rects.flatMap((r) => [r.y0, r.y1]));
1516
+ const nx = xs.length - 1;
1517
+ const ny = ys.length - 1;
1518
+ const filled = (i, j) => {
1519
+ if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
1520
+ const cx = (xs[i] + xs[i + 1]) / 2;
1521
+ const cy = (ys[j] + ys[j + 1]) / 2;
1522
+ return rects.some((r) => cx > r.x0 && cx < r.x1 && cy > r.y0 && cy < r.y1);
1523
+ };
1524
+ const key = (x, y) => `${x},${y}`;
1525
+ const starts = /* @__PURE__ */ new Map();
1526
+ const pushEdge = (ax, ay, bx, by) => {
1527
+ const k = key(ax, ay);
1528
+ const list = starts.get(k);
1529
+ if (list) list.push({ x: bx, y: by });
1530
+ else starts.set(k, [{ x: bx, y: by }]);
1531
+ };
1532
+ for (let i = 0; i < nx; i++) {
1533
+ for (let j = 0; j < ny; j++) {
1534
+ if (!filled(i, j)) continue;
1535
+ const x0 = xs[i];
1536
+ const x1 = xs[i + 1];
1537
+ const y0 = ys[j];
1538
+ const y1 = ys[j + 1];
1539
+ if (!filled(i, j - 1)) pushEdge(x1, y0, x0, y0);
1540
+ if (!filled(i - 1, j)) pushEdge(x0, y0, x0, y1);
1541
+ if (!filled(i, j + 1)) pushEdge(x0, y1, x1, y1);
1542
+ if (!filled(i + 1, j)) pushEdge(x1, y1, x1, y0);
1543
+ }
1544
+ }
1545
+ const used = /* @__PURE__ */ new Set();
1546
+ const edgeKey = (a, b) => `${a.x},${a.y}->${b.x},${b.y}`;
1547
+ const loops = [];
1548
+ const takeNext = (from, prevDir) => {
1549
+ const list = starts.get(key(from.x, from.y));
1550
+ if (!list) return null;
1551
+ const candidates = list.filter((to) => !used.has(edgeKey(from, to)));
1552
+ if (candidates.length === 0) return null;
1553
+ if (candidates.length === 1 || !prevDir) return candidates[0];
1554
+ let best = candidates[0];
1555
+ let bestScore = -Infinity;
1556
+ for (const c of candidates) {
1557
+ const dir = { x: c.x - from.x, y: c.y - from.y };
1558
+ const cross = prevDir.x * dir.y - prevDir.y * dir.x;
1559
+ if (cross > bestScore) {
1560
+ bestScore = cross;
1561
+ best = c;
1562
+ }
1563
+ }
1564
+ return best;
1565
+ };
1566
+ for (const [startKey, ends] of starts) {
1567
+ for (const firstEnd of ends) {
1568
+ const [sx, sy] = startKey.split(",").map(Number);
1569
+ const start = { x: sx, y: sy };
1570
+ if (used.has(edgeKey(start, firstEnd))) continue;
1571
+ const loop = [start];
1572
+ let cur = start;
1573
+ let next = firstEnd;
1574
+ let dir = null;
1575
+ while (next) {
1576
+ used.add(edgeKey(cur, next));
1577
+ if (next.x === start.x && next.y === start.y) break;
1578
+ loop.push(next);
1579
+ dir = { x: next.x - cur.x, y: next.y - cur.y };
1580
+ cur = next;
1581
+ next = takeNext(cur, dir);
1582
+ }
1583
+ if (loop.length >= 4) loops.push(mergeCollinear(loop));
1584
+ }
1585
+ }
1586
+ return loops;
1587
+ }
1588
+ function mergeCollinear(loop) {
1589
+ const n = loop.length;
1590
+ const out = [];
1591
+ for (let i = 0; i < n; i++) {
1592
+ const prev = loop[(i - 1 + n) % n];
1593
+ const cur = loop[i];
1594
+ const next = loop[(i + 1) % n];
1595
+ const d1x = cur.x - prev.x;
1596
+ const d1y = cur.y - prev.y;
1597
+ const d2x = next.x - cur.x;
1598
+ const d2y = next.y - cur.y;
1599
+ if (d1x * d2y - d1y * d2x !== 0) out.push(cur);
1600
+ }
1601
+ return out.length >= 3 ? out : loop;
1602
+ }
1603
+
1365
1604
  // src/render.ts
1366
- var THEME = {
1367
- bg: "#ffffff",
1368
- pocheBase: "#e9e4db",
1369
- pocheHatch: "#b9b1a4",
1370
- wallStroke: "#1b1b1b",
1371
- roomFill: "#fbfaf7",
1372
- roomLabel: "#222222",
1373
- areaLabel: "#7a7a7a",
1374
- furnitureStroke: "#a8a29a",
1375
- furnitureFill: "#f4f2ee",
1376
- furnitureLabel: "#9a948c",
1377
- opening: "#ffffff",
1378
- doorLeaf: "#555555",
1379
- windowPane: "#3a6ea5",
1380
- dim: "#0E5484",
1381
- annotation: "#333333",
1382
- annotationMuted: "#888888",
1383
- column: "#4a4a4a"
1384
- };
1385
1605
  function fmt(v) {
1386
1606
  const r = Math.round(v * 100) / 100;
1387
1607
  return Object.is(r, -0) ? "0" : String(r);
@@ -1408,15 +1628,56 @@ function planBounds(ir) {
1408
1628
  }
1409
1629
  return b;
1410
1630
  }
1631
+ function allOrthogonal(walls) {
1632
+ return walls.every((w) => segmentsOfWall(w).every((s) => s.a.x === s.b.x || s.a.y === s.b.y));
1633
+ }
1634
+ function loopsToPath(loops) {
1635
+ return loops.map((loop) => "M " + loop.map(pt).join(" L ") + " Z").join(" ");
1636
+ }
1637
+ function materialsUsed(walls) {
1638
+ return [...new Set(walls.map((w) => w.material))].sort();
1639
+ }
1640
+ function renderWalls(walls, ctx) {
1641
+ if (walls.length === 0) return [];
1642
+ const ops = [];
1643
+ for (const mat of materialsUsed(walls)) {
1644
+ const group = walls.filter((w) => w.material === mat);
1645
+ if (!allOrthogonal(group)) {
1646
+ const def = registry.get("wall");
1647
+ ops.push(...group.flatMap((w) => def.render(w, ctx)));
1648
+ continue;
1649
+ }
1650
+ const rects = [];
1651
+ for (const w of group) {
1652
+ for (const s of segmentsOfWall(w)) {
1653
+ const corners = segmentRectangle(s.a, s.b, s.thickness);
1654
+ const xsv = corners.map((c) => c.x);
1655
+ const ysv = corners.map((c) => c.y);
1656
+ rects.push({ x0: Math.min(...xsv), y0: Math.min(...ysv), x1: Math.max(...xsv), y1: Math.max(...ysv) });
1657
+ }
1658
+ }
1659
+ const loops = rectUnionOutline(rects);
1660
+ if (loops.length === 0) continue;
1661
+ const d = loopsToPath(loops);
1662
+ ops.push({ pass: "wallFill", svg: `<path d="${d}" fill="url(#${patternId(mat)})" fill-rule="nonzero"/>` });
1663
+ ops.push({
1664
+ pass: "wallFace",
1665
+ svg: `<path d="${d}" fill="none" stroke="${ctx.theme.wallStroke}" stroke-width="${ctx.fmt(ctx.sizes.wallStroke)}" stroke-linejoin="miter"/>`
1666
+ });
1667
+ }
1668
+ return ops;
1669
+ }
1411
1670
  function render(ir, opts = {}) {
1671
+ const THEME = mergeTheme(DEFAULT_THEME, ir.theme, opts.theme);
1672
+ const lw = THEME.lineWeight;
1412
1673
  const b = planBounds(ir);
1413
1674
  const drawW = b.maxX - b.minX;
1414
1675
  const drawH = b.maxY - b.minY;
1415
1676
  const refDim = Math.max(drawW, drawH, 1);
1416
1677
  const sizes = {
1417
1678
  refDim,
1418
- wallStroke: refDim * 28e-4,
1419
- thin: refDim * 16e-4,
1679
+ wallStroke: refDim * 28e-4 * lw,
1680
+ thin: refDim * 16e-4 * lw,
1420
1681
  roomFont: refDim * 0.03,
1421
1682
  areaFont: refDim * 0.022,
1422
1683
  dimFont: refDim * 0.02,
@@ -1432,28 +1693,30 @@ function render(ir, opts = {}) {
1432
1693
  const out = [];
1433
1694
  const svgAttrs = opts.width ? `width="${fmt(opts.width)}" height="${fmt(opts.width * vbH / vbW)}"` : "";
1434
1695
  out.push(
1435
- `<svg xmlns="http://www.w3.org/2000/svg" ${svgAttrs} viewBox="${fmt(vbX)} ${fmt(vbY)} ${fmt(vbW)} ${fmt(vbH)}" font-family="Helvetica, Arial, sans-serif">`
1436
- );
1437
- out.push(
1438
- `<defs><pattern id="poche" patternUnits="userSpaceOnUse" width="${fmt(hatchGap)}" height="${fmt(hatchGap)}" patternTransform="rotate(45)"><rect width="${fmt(hatchGap)}" height="${fmt(hatchGap)}" fill="${THEME.pocheBase}"/><line x1="0" y1="0" x2="0" y2="${fmt(hatchGap)}" stroke="${THEME.pocheHatch}" stroke-width="${fmt(thin * 0.7)}"/></pattern></defs>`
1696
+ `<svg xmlns="http://www.w3.org/2000/svg" ${svgAttrs} viewBox="${fmt(vbX)} ${fmt(vbY)} ${fmt(vbW)} ${fmt(vbH)}" font-family="${xml(THEME.font)}">`
1439
1697
  );
1698
+ const hatchCtx = { fmt, gap: hatchGap, thin, base: THEME.pocheBase, line: THEME.pocheHatch };
1699
+ const patterns = materialsUsed(ir.walls).map((m) => hatchPattern(m, hatchCtx)).join("");
1700
+ out.push(`<defs>${patterns}</defs>`);
1440
1701
  out.push(`<rect x="${fmt(vbX)}" y="${fmt(vbY)}" width="${fmt(vbW)}" height="${fmt(vbH)}" fill="${THEME.bg}"/>`);
1441
1702
  const ctx = { fmt, pt, xml, theme: THEME, sizes, bounds: b };
1442
1703
  const ops = ir.elements.flatMap((el) => {
1704
+ if (el.kind === "wall") return [];
1443
1705
  const def = registry.get(el.kind);
1444
1706
  return def ? def.render(el, ctx) : [];
1445
1707
  });
1708
+ ops.push(...renderWalls(ir.walls, ctx));
1446
1709
  for (const pass of RENDER_PASSES) {
1447
1710
  for (const op of ops) if (op.pass === pass) out.push(op.svg);
1448
1711
  }
1449
- out.push(northArrow(ir, b, margin, refDim));
1450
- out.push(scaleBar(b, margin, refDim, thin));
1451
- const tb = titleBlock(ir, b, margin, refDim, thin);
1712
+ out.push(northArrow(ir, b, margin, refDim, THEME));
1713
+ out.push(scaleBar(b, margin, refDim, thin, THEME));
1714
+ const tb = titleBlock(ir, b, margin, refDim, thin, THEME);
1452
1715
  if (tb) out.push(tb);
1453
1716
  out.push("</svg>");
1454
1717
  return out.join("\n");
1455
1718
  }
1456
- function northArrow(ir, b, margin, refDim) {
1719
+ function northArrow(ir, b, margin, refDim, THEME) {
1457
1720
  const r = refDim * 0.045;
1458
1721
  const cx = b.maxX - r;
1459
1722
  const cy = b.minY - margin * 0.55;
@@ -1483,7 +1746,7 @@ function northArrow(ir, b, margin, refDim) {
1483
1746
  const ly = cy + ny * (r + fs * 0.8);
1484
1747
  return `<g><polygon points="${tri}" fill="${THEME.annotation}" transform="rotate(${fmt(deg)} ${fmt(cx)} ${fmt(cy)})"/><text x="${fmt(lx)}" y="${fmt(ly)}" font-size="${fmt(fs)}" fill="${THEME.annotation}" text-anchor="middle" dominant-baseline="central">N</text></g>`;
1485
1748
  }
1486
- function scaleBar(b, margin, refDim, thin) {
1749
+ function scaleBar(b, margin, refDim, thin, THEME) {
1487
1750
  const barLen = niceBarLength(refDim * 0.3);
1488
1751
  const x0 = b.minX;
1489
1752
  const y0 = b.maxY + margin * 0.55;
@@ -1503,7 +1766,7 @@ function scaleBar(b, margin, refDim, thin) {
1503
1766
  );
1504
1767
  return `<g>${parts.join("")}</g>`;
1505
1768
  }
1506
- function titleBlock(ir, b, margin, refDim, thin) {
1769
+ function titleBlock(ir, b, margin, refDim, thin, THEME) {
1507
1770
  const t = ir.title;
1508
1771
  if (!t && !ir.scale) return null;
1509
1772
  const boxW = refDim * 0.34;
@@ -1596,7 +1859,7 @@ function formatDiagnostic(source, d) {
1596
1859
  var cache = /* @__PURE__ */ new Map();
1597
1860
  var CACHE_MAX = 64;
1598
1861
  function compile(source, opts = {}) {
1599
- const key = JSON.stringify([source, opts.width ?? null]);
1862
+ const key = JSON.stringify([source, opts.width ?? null, opts.theme ?? null]);
1600
1863
  if (!opts.noCache) {
1601
1864
  const hit = cache.get(key);
1602
1865
  if (hit) return hit;
@@ -1636,4 +1899,4 @@ export {
1636
1899
  compile,
1637
1900
  clearCache
1638
1901
  };
1639
- //# sourceMappingURL=chunk-DHNWMOP7.js.map
1902
+ //# sourceMappingURL=chunk-GUNWYUR2.js.map