@chanmeng666/archlang 0.4.0 → 0.6.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.
- package/README.md +175 -169
- package/dist/{chunk-DHNWMOP7.js → chunk-PABYLU6Z.js} +530 -53
- package/dist/chunk-PABYLU6Z.js.map +1 -0
- package/dist/cli.js +52 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +164 -1
- package/dist/index.js +9 -3
- package/examples/themed.arch +40 -0
- package/package.json +9 -2
- package/dist/chunk-DHNWMOP7.js.map +0 -1
|
@@ -301,6 +301,67 @@ 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
|
+
function sanitizeTheme(theme) {
|
|
356
|
+
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
357
|
+
const out = { ...theme };
|
|
358
|
+
for (const k of Object.keys(out)) {
|
|
359
|
+
const v = out[k];
|
|
360
|
+
if (typeof v === "string") out[k] = esc(v);
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
|
|
304
365
|
// src/geometry.ts
|
|
305
366
|
var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
306
367
|
var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
@@ -366,30 +427,63 @@ function segmentsOfWall(w) {
|
|
|
366
427
|
}
|
|
367
428
|
return segs;
|
|
368
429
|
}
|
|
369
|
-
function
|
|
430
|
+
function hostInfoForWalls(walls, at, ref) {
|
|
370
431
|
const candidates = ref ? walls.filter((w) => w.id === ref || w.category === ref) : walls;
|
|
371
|
-
let
|
|
432
|
+
let host = null;
|
|
372
433
|
let bestDist = Infinity;
|
|
434
|
+
let onWall = false;
|
|
373
435
|
for (const w of candidates) {
|
|
436
|
+
const tol = w.thickness / 2 + Math.max(w.thickness, 1);
|
|
374
437
|
for (const s of segmentsOfWall(w)) {
|
|
375
438
|
const dist = distPointToSegment(at, s.a, s.b);
|
|
376
439
|
if (dist < bestDist) {
|
|
377
440
|
bestDist = dist;
|
|
378
|
-
|
|
441
|
+
host = s;
|
|
379
442
|
}
|
|
443
|
+
if (!onWall && dist <= tol) onWall = true;
|
|
380
444
|
}
|
|
381
445
|
}
|
|
382
|
-
return
|
|
446
|
+
return { host, onWall };
|
|
383
447
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
448
|
+
|
|
449
|
+
// src/hatches.ts
|
|
450
|
+
var KNOWN_MATERIALS = ["poche", "concrete", "brick", "insulation", "tile", "none"];
|
|
451
|
+
var DEFAULT_MATERIAL = "poche";
|
|
452
|
+
function patternId(material) {
|
|
453
|
+
return material === "poche" ? "poche" : `hatch-${material}`;
|
|
454
|
+
}
|
|
455
|
+
var HATCHES = {
|
|
456
|
+
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>`,
|
|
457
|
+
// Aggregate speckle.
|
|
458
|
+
concrete: (id, c) => {
|
|
459
|
+
const w = c.gap * 1.6;
|
|
460
|
+
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>`;
|
|
461
|
+
},
|
|
462
|
+
// Running-bond brick courses.
|
|
463
|
+
brick: (id, c) => {
|
|
464
|
+
const w = c.gap * 3;
|
|
465
|
+
const h = c.gap * 1.4;
|
|
466
|
+
const sw = c.fmt(c.thin * 0.6);
|
|
467
|
+
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>`;
|
|
468
|
+
},
|
|
469
|
+
// Cross-hatch batting.
|
|
470
|
+
insulation: (id, c) => {
|
|
471
|
+
const w = c.gap * 1.2;
|
|
472
|
+
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>`;
|
|
473
|
+
},
|
|
474
|
+
// Square tile grid.
|
|
475
|
+
tile: (id, c) => {
|
|
476
|
+
const w = c.gap * 1.8;
|
|
477
|
+
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>`;
|
|
478
|
+
},
|
|
479
|
+
// Solid fill, no hatch.
|
|
480
|
+
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>`
|
|
481
|
+
};
|
|
482
|
+
function isKnownMaterial(name) {
|
|
483
|
+
return KNOWN_MATERIALS.includes(name);
|
|
484
|
+
}
|
|
485
|
+
function hatchPattern(material, c) {
|
|
486
|
+
return HATCHES[material](patternId(material), c);
|
|
393
487
|
}
|
|
394
488
|
|
|
395
489
|
// src/elements/wall.ts
|
|
@@ -402,6 +496,11 @@ var wall = {
|
|
|
402
496
|
const category = ctx.eatIdent().value;
|
|
403
497
|
ctx.eatKeyword("thickness");
|
|
404
498
|
const thickness = ctx.parseExpr();
|
|
499
|
+
let material;
|
|
500
|
+
if (ctx.isKeyword("material")) {
|
|
501
|
+
ctx.next();
|
|
502
|
+
material = ctx.eatIdent().value;
|
|
503
|
+
}
|
|
405
504
|
ctx.eat("lcurly");
|
|
406
505
|
const points = [];
|
|
407
506
|
let closed = false;
|
|
@@ -419,7 +518,7 @@ var wall = {
|
|
|
419
518
|
}
|
|
420
519
|
ctx.eat("rcurly");
|
|
421
520
|
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 };
|
|
521
|
+
return { kind: "wall", id, category, thickness, material, points, closed, line: kw.line };
|
|
423
522
|
},
|
|
424
523
|
idPrefix: (node) => node.category || "wall",
|
|
425
524
|
resolve(node, ctx) {
|
|
@@ -431,7 +530,18 @@ var wall = {
|
|
|
431
530
|
if (thickness <= 0) {
|
|
432
531
|
ctx.diag({ severity: "error", message: `Wall "${id}" must have a positive thickness`, code: "E_WALL_THICKNESS", span: n.span });
|
|
433
532
|
}
|
|
434
|
-
|
|
533
|
+
let material = DEFAULT_MATERIAL;
|
|
534
|
+
if (n.material !== void 0) {
|
|
535
|
+
if (isKnownMaterial(n.material)) material = n.material;
|
|
536
|
+
else
|
|
537
|
+
ctx.diag({
|
|
538
|
+
severity: "warning",
|
|
539
|
+
message: `Unknown wall material "${n.material}" (known: ${KNOWN_MATERIALS.join(", ")}); using the default hatch`,
|
|
540
|
+
code: "W_UNKNOWN_MATERIAL",
|
|
541
|
+
span: n.span
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
return { kind: "wall", id, category: n.category, thickness, material, points, closed: n.closed, span: n.span };
|
|
435
545
|
},
|
|
436
546
|
bounds(resolved) {
|
|
437
547
|
const w = resolved;
|
|
@@ -873,7 +983,7 @@ register(dim);
|
|
|
873
983
|
register(column);
|
|
874
984
|
|
|
875
985
|
// src/parser.ts
|
|
876
|
-
var SETTINGS = ["units", "grid", "scale", "north", "title", "let", "component"];
|
|
986
|
+
var SETTINGS = ["units", "grid", "scale", "north", "title", "theme", "let", "component"];
|
|
877
987
|
var STATEMENT_STARTS = /* @__PURE__ */ new Set([...SETTINGS, ...registry.keys()]);
|
|
878
988
|
var ParseError = class extends Error {
|
|
879
989
|
constructor(message, span) {
|
|
@@ -1036,6 +1146,10 @@ var Parser = class {
|
|
|
1036
1146
|
plan.title = n;
|
|
1037
1147
|
break;
|
|
1038
1148
|
}
|
|
1149
|
+
case "theme": {
|
|
1150
|
+
plan.theme = { ...plan.theme, ...this.parseTheme() };
|
|
1151
|
+
break;
|
|
1152
|
+
}
|
|
1039
1153
|
case "let": {
|
|
1040
1154
|
const n = this.parseLet();
|
|
1041
1155
|
n.span = this.spanFrom(start);
|
|
@@ -1145,6 +1259,35 @@ var Parser = class {
|
|
|
1145
1259
|
this.eat("rcurly");
|
|
1146
1260
|
return node;
|
|
1147
1261
|
}
|
|
1262
|
+
/** `theme { key: <value> … }` — colours (strings), `lineWeight` (number), `font` (string). */
|
|
1263
|
+
parseTheme() {
|
|
1264
|
+
this.eatKeyword("theme");
|
|
1265
|
+
this.eat("lcurly");
|
|
1266
|
+
const t = {};
|
|
1267
|
+
while (!this.isType("rcurly") && !this.isType("eof")) {
|
|
1268
|
+
const keyTok = this.eatIdent();
|
|
1269
|
+
if (this.isType("colon")) this.next();
|
|
1270
|
+
const resolved = resolveThemeKey(keyTok.value);
|
|
1271
|
+
if (!resolved) {
|
|
1272
|
+
this.diagnostics.push({
|
|
1273
|
+
severity: "warning",
|
|
1274
|
+
message: `Unknown theme key "${keyTok.value}"`,
|
|
1275
|
+
code: "W_UNKNOWN_THEME_KEY",
|
|
1276
|
+
span: { start: keyTok.start, end: keyTok.end }
|
|
1277
|
+
});
|
|
1278
|
+
if (this.isType("string") || this.isType("number")) this.next();
|
|
1279
|
+
else this.fail(`Expected a value for theme key "${keyTok.value}"`);
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
if (isNumericThemeKey(resolved)) {
|
|
1283
|
+
t[resolved] = this.eatNumber();
|
|
1284
|
+
} else {
|
|
1285
|
+
t[resolved] = this.eatString();
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
this.eat("rcurly");
|
|
1289
|
+
return t;
|
|
1290
|
+
}
|
|
1148
1291
|
parseLet() {
|
|
1149
1292
|
const kw = this.eatKeyword("let");
|
|
1150
1293
|
const name = this.eatIdent().value;
|
|
@@ -1286,6 +1429,15 @@ function resolve(ast) {
|
|
|
1286
1429
|
let activeEnv = /* @__PURE__ */ new Map();
|
|
1287
1430
|
const evalNum = (e) => evalExpr(e, activeEnv, (d) => diagnostics.push(d));
|
|
1288
1431
|
const evalPt = (p) => ({ x: evalNum(p.x), y: evalNum(p.y) });
|
|
1432
|
+
let hiKey = "";
|
|
1433
|
+
let hiVal = null;
|
|
1434
|
+
const hostInfo = (at, ref) => {
|
|
1435
|
+
const key = `${at.x},${at.y},${ref ?? ""}`;
|
|
1436
|
+
if (key === hiKey && hiVal) return hiVal;
|
|
1437
|
+
hiKey = key;
|
|
1438
|
+
hiVal = hostInfoForWalls(walls, at, ref);
|
|
1439
|
+
return hiVal;
|
|
1440
|
+
};
|
|
1289
1441
|
const ctx = {
|
|
1290
1442
|
grid: g,
|
|
1291
1443
|
snap,
|
|
@@ -1294,8 +1446,8 @@ function resolve(ast) {
|
|
|
1294
1446
|
evalPt,
|
|
1295
1447
|
id: "",
|
|
1296
1448
|
walls,
|
|
1297
|
-
hostSegment: (at, ref) =>
|
|
1298
|
-
isOnWall: (at, ref) =>
|
|
1449
|
+
hostSegment: (at, ref) => hostInfo(at, ref).host,
|
|
1450
|
+
isOnWall: (at, ref) => hostInfo(at, ref).onWall,
|
|
1299
1451
|
diag: (d) => diagnostics.push(d)
|
|
1300
1452
|
};
|
|
1301
1453
|
for (const def of registryOrder) {
|
|
@@ -1343,6 +1495,7 @@ function resolve(ast) {
|
|
|
1343
1495
|
scale: ast.scale,
|
|
1344
1496
|
north: ast.north,
|
|
1345
1497
|
title: ast.title,
|
|
1498
|
+
theme: ast.theme,
|
|
1346
1499
|
elements,
|
|
1347
1500
|
walls
|
|
1348
1501
|
};
|
|
@@ -1362,26 +1515,104 @@ var RENDER_PASSES = [
|
|
|
1362
1515
|
"annotations"
|
|
1363
1516
|
];
|
|
1364
1517
|
|
|
1518
|
+
// src/geometry/union.ts
|
|
1519
|
+
function uniqSorted(values) {
|
|
1520
|
+
const out = [...new Set(values)].sort((a, b) => a - b);
|
|
1521
|
+
return out;
|
|
1522
|
+
}
|
|
1523
|
+
function rectUnionOutline(rects) {
|
|
1524
|
+
if (rects.length === 0) return [];
|
|
1525
|
+
const xs = uniqSorted(rects.flatMap((r) => [r.x0, r.x1]));
|
|
1526
|
+
const ys = uniqSorted(rects.flatMap((r) => [r.y0, r.y1]));
|
|
1527
|
+
const nx = xs.length - 1;
|
|
1528
|
+
const ny = ys.length - 1;
|
|
1529
|
+
const filled = (i, j) => {
|
|
1530
|
+
if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
|
|
1531
|
+
const cx = (xs[i] + xs[i + 1]) / 2;
|
|
1532
|
+
const cy = (ys[j] + ys[j + 1]) / 2;
|
|
1533
|
+
return rects.some((r) => cx > r.x0 && cx < r.x1 && cy > r.y0 && cy < r.y1);
|
|
1534
|
+
};
|
|
1535
|
+
const key = (x, y) => `${x},${y}`;
|
|
1536
|
+
const starts = /* @__PURE__ */ new Map();
|
|
1537
|
+
const pushEdge = (ax, ay, bx, by) => {
|
|
1538
|
+
const k = key(ax, ay);
|
|
1539
|
+
const list = starts.get(k);
|
|
1540
|
+
if (list) list.push({ x: bx, y: by });
|
|
1541
|
+
else starts.set(k, [{ x: bx, y: by }]);
|
|
1542
|
+
};
|
|
1543
|
+
for (let i = 0; i < nx; i++) {
|
|
1544
|
+
for (let j = 0; j < ny; j++) {
|
|
1545
|
+
if (!filled(i, j)) continue;
|
|
1546
|
+
const x0 = xs[i];
|
|
1547
|
+
const x1 = xs[i + 1];
|
|
1548
|
+
const y0 = ys[j];
|
|
1549
|
+
const y1 = ys[j + 1];
|
|
1550
|
+
if (!filled(i, j - 1)) pushEdge(x1, y0, x0, y0);
|
|
1551
|
+
if (!filled(i - 1, j)) pushEdge(x0, y0, x0, y1);
|
|
1552
|
+
if (!filled(i, j + 1)) pushEdge(x0, y1, x1, y1);
|
|
1553
|
+
if (!filled(i + 1, j)) pushEdge(x1, y1, x1, y0);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
const used = /* @__PURE__ */ new Set();
|
|
1557
|
+
const edgeKey = (a, b) => `${a.x},${a.y}->${b.x},${b.y}`;
|
|
1558
|
+
const loops = [];
|
|
1559
|
+
const takeNext = (from, prevDir) => {
|
|
1560
|
+
const list = starts.get(key(from.x, from.y));
|
|
1561
|
+
if (!list) return null;
|
|
1562
|
+
const candidates = list.filter((to) => !used.has(edgeKey(from, to)));
|
|
1563
|
+
if (candidates.length === 0) return null;
|
|
1564
|
+
if (candidates.length === 1 || !prevDir) return candidates[0];
|
|
1565
|
+
let best = candidates[0];
|
|
1566
|
+
let bestScore = -Infinity;
|
|
1567
|
+
for (const c of candidates) {
|
|
1568
|
+
const dir = { x: c.x - from.x, y: c.y - from.y };
|
|
1569
|
+
const cross = prevDir.x * dir.y - prevDir.y * dir.x;
|
|
1570
|
+
if (cross > bestScore) {
|
|
1571
|
+
bestScore = cross;
|
|
1572
|
+
best = c;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
return best;
|
|
1576
|
+
};
|
|
1577
|
+
for (const [startKey, ends] of starts) {
|
|
1578
|
+
for (const firstEnd of ends) {
|
|
1579
|
+
const [sx, sy] = startKey.split(",").map(Number);
|
|
1580
|
+
const start = { x: sx, y: sy };
|
|
1581
|
+
if (used.has(edgeKey(start, firstEnd))) continue;
|
|
1582
|
+
const loop = [start];
|
|
1583
|
+
let cur = start;
|
|
1584
|
+
let next = firstEnd;
|
|
1585
|
+
let dir = null;
|
|
1586
|
+
while (next) {
|
|
1587
|
+
used.add(edgeKey(cur, next));
|
|
1588
|
+
if (next.x === start.x && next.y === start.y) break;
|
|
1589
|
+
loop.push(next);
|
|
1590
|
+
dir = { x: next.x - cur.x, y: next.y - cur.y };
|
|
1591
|
+
cur = next;
|
|
1592
|
+
next = takeNext(cur, dir);
|
|
1593
|
+
}
|
|
1594
|
+
if (loop.length >= 4) loops.push(mergeCollinear(loop));
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return loops;
|
|
1598
|
+
}
|
|
1599
|
+
function mergeCollinear(loop) {
|
|
1600
|
+
const n = loop.length;
|
|
1601
|
+
const out = [];
|
|
1602
|
+
for (let i = 0; i < n; i++) {
|
|
1603
|
+
const prev = loop[(i - 1 + n) % n];
|
|
1604
|
+
const cur = loop[i];
|
|
1605
|
+
const next = loop[(i + 1) % n];
|
|
1606
|
+
const d1x = cur.x - prev.x;
|
|
1607
|
+
const d1y = cur.y - prev.y;
|
|
1608
|
+
const d2x = next.x - cur.x;
|
|
1609
|
+
const d2y = next.y - cur.y;
|
|
1610
|
+
if (d1x * d2y - d1y * d2x !== 0) out.push(cur);
|
|
1611
|
+
}
|
|
1612
|
+
return out.length >= 3 ? out : loop;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1365
1615
|
// 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
1616
|
function fmt(v) {
|
|
1386
1617
|
const r = Math.round(v * 100) / 100;
|
|
1387
1618
|
return Object.is(r, -0) ? "0" : String(r);
|
|
@@ -1408,15 +1639,56 @@ function planBounds(ir) {
|
|
|
1408
1639
|
}
|
|
1409
1640
|
return b;
|
|
1410
1641
|
}
|
|
1642
|
+
function allOrthogonal(walls) {
|
|
1643
|
+
return walls.every((w) => segmentsOfWall(w).every((s) => s.a.x === s.b.x || s.a.y === s.b.y));
|
|
1644
|
+
}
|
|
1645
|
+
function loopsToPath(loops) {
|
|
1646
|
+
return loops.map((loop) => "M " + loop.map(pt).join(" L ") + " Z").join(" ");
|
|
1647
|
+
}
|
|
1648
|
+
function materialsUsed(walls) {
|
|
1649
|
+
return [...new Set(walls.map((w) => w.material))].sort();
|
|
1650
|
+
}
|
|
1651
|
+
function renderWalls(walls, ctx) {
|
|
1652
|
+
if (walls.length === 0) return [];
|
|
1653
|
+
const ops = [];
|
|
1654
|
+
for (const mat of materialsUsed(walls)) {
|
|
1655
|
+
const group = walls.filter((w) => w.material === mat);
|
|
1656
|
+
if (!allOrthogonal(group)) {
|
|
1657
|
+
const def = registry.get("wall");
|
|
1658
|
+
ops.push(...group.flatMap((w) => def.render(w, ctx)));
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
const rects = [];
|
|
1662
|
+
for (const w of group) {
|
|
1663
|
+
for (const s of segmentsOfWall(w)) {
|
|
1664
|
+
const corners = segmentRectangle(s.a, s.b, s.thickness);
|
|
1665
|
+
const xsv = corners.map((c) => c.x);
|
|
1666
|
+
const ysv = corners.map((c) => c.y);
|
|
1667
|
+
rects.push({ x0: Math.min(...xsv), y0: Math.min(...ysv), x1: Math.max(...xsv), y1: Math.max(...ysv) });
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
const loops = rectUnionOutline(rects);
|
|
1671
|
+
if (loops.length === 0) continue;
|
|
1672
|
+
const d = loopsToPath(loops);
|
|
1673
|
+
ops.push({ pass: "wallFill", svg: `<path d="${d}" fill="url(#${patternId(mat)})" fill-rule="nonzero"/>` });
|
|
1674
|
+
ops.push({
|
|
1675
|
+
pass: "wallFace",
|
|
1676
|
+
svg: `<path d="${d}" fill="none" stroke="${ctx.theme.wallStroke}" stroke-width="${ctx.fmt(ctx.sizes.wallStroke)}" stroke-linejoin="miter"/>`
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
return ops;
|
|
1680
|
+
}
|
|
1411
1681
|
function render(ir, opts = {}) {
|
|
1682
|
+
const THEME = sanitizeTheme(mergeTheme(DEFAULT_THEME, ir.theme, opts.theme));
|
|
1683
|
+
const lw = THEME.lineWeight;
|
|
1412
1684
|
const b = planBounds(ir);
|
|
1413
1685
|
const drawW = b.maxX - b.minX;
|
|
1414
1686
|
const drawH = b.maxY - b.minY;
|
|
1415
1687
|
const refDim = Math.max(drawW, drawH, 1);
|
|
1416
1688
|
const sizes = {
|
|
1417
1689
|
refDim,
|
|
1418
|
-
wallStroke: refDim * 28e-4,
|
|
1419
|
-
thin: refDim * 16e-4,
|
|
1690
|
+
wallStroke: refDim * 28e-4 * lw,
|
|
1691
|
+
thin: refDim * 16e-4 * lw,
|
|
1420
1692
|
roomFont: refDim * 0.03,
|
|
1421
1693
|
areaFont: refDim * 0.022,
|
|
1422
1694
|
dimFont: refDim * 0.02,
|
|
@@ -1432,28 +1704,30 @@ function render(ir, opts = {}) {
|
|
|
1432
1704
|
const out = [];
|
|
1433
1705
|
const svgAttrs = opts.width ? `width="${fmt(opts.width)}" height="${fmt(opts.width * vbH / vbW)}"` : "";
|
|
1434
1706
|
out.push(
|
|
1435
|
-
`<svg xmlns="http://www.w3.org/2000/svg" ${svgAttrs} viewBox="${fmt(vbX)} ${fmt(vbY)} ${fmt(vbW)} ${fmt(vbH)}" font-family="
|
|
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>`
|
|
1707
|
+
`<svg xmlns="http://www.w3.org/2000/svg" ${svgAttrs} viewBox="${fmt(vbX)} ${fmt(vbY)} ${fmt(vbW)} ${fmt(vbH)}" font-family="${THEME.font}">`
|
|
1439
1708
|
);
|
|
1709
|
+
const hatchCtx = { fmt, gap: hatchGap, thin, base: THEME.pocheBase, line: THEME.pocheHatch };
|
|
1710
|
+
const patterns = materialsUsed(ir.walls).map((m) => hatchPattern(m, hatchCtx)).join("");
|
|
1711
|
+
out.push(`<defs>${patterns}</defs>`);
|
|
1440
1712
|
out.push(`<rect x="${fmt(vbX)}" y="${fmt(vbY)}" width="${fmt(vbW)}" height="${fmt(vbH)}" fill="${THEME.bg}"/>`);
|
|
1441
1713
|
const ctx = { fmt, pt, xml, theme: THEME, sizes, bounds: b };
|
|
1442
1714
|
const ops = ir.elements.flatMap((el) => {
|
|
1715
|
+
if (el.kind === "wall") return [];
|
|
1443
1716
|
const def = registry.get(el.kind);
|
|
1444
1717
|
return def ? def.render(el, ctx) : [];
|
|
1445
1718
|
});
|
|
1719
|
+
ops.push(...renderWalls(ir.walls, ctx));
|
|
1446
1720
|
for (const pass of RENDER_PASSES) {
|
|
1447
1721
|
for (const op of ops) if (op.pass === pass) out.push(op.svg);
|
|
1448
1722
|
}
|
|
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);
|
|
1723
|
+
out.push(northArrow(ir, b, margin, refDim, THEME));
|
|
1724
|
+
out.push(scaleBar(b, margin, refDim, thin, THEME));
|
|
1725
|
+
const tb = titleBlock(ir, b, margin, refDim, thin, THEME);
|
|
1452
1726
|
if (tb) out.push(tb);
|
|
1453
1727
|
out.push("</svg>");
|
|
1454
1728
|
return out.join("\n");
|
|
1455
1729
|
}
|
|
1456
|
-
function northArrow(ir, b, margin, refDim) {
|
|
1730
|
+
function northArrow(ir, b, margin, refDim, THEME) {
|
|
1457
1731
|
const r = refDim * 0.045;
|
|
1458
1732
|
const cx = b.maxX - r;
|
|
1459
1733
|
const cy = b.minY - margin * 0.55;
|
|
@@ -1483,7 +1757,7 @@ function northArrow(ir, b, margin, refDim) {
|
|
|
1483
1757
|
const ly = cy + ny * (r + fs * 0.8);
|
|
1484
1758
|
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
1759
|
}
|
|
1486
|
-
function scaleBar(b, margin, refDim, thin) {
|
|
1760
|
+
function scaleBar(b, margin, refDim, thin, THEME) {
|
|
1487
1761
|
const barLen = niceBarLength(refDim * 0.3);
|
|
1488
1762
|
const x0 = b.minX;
|
|
1489
1763
|
const y0 = b.maxY + margin * 0.55;
|
|
@@ -1503,7 +1777,7 @@ function scaleBar(b, margin, refDim, thin) {
|
|
|
1503
1777
|
);
|
|
1504
1778
|
return `<g>${parts.join("")}</g>`;
|
|
1505
1779
|
}
|
|
1506
|
-
function titleBlock(ir, b, margin, refDim, thin) {
|
|
1780
|
+
function titleBlock(ir, b, margin, refDim, thin, THEME) {
|
|
1507
1781
|
const t = ir.title;
|
|
1508
1782
|
if (!t && !ir.scale) return null;
|
|
1509
1783
|
const boxW = refDim * 0.34;
|
|
@@ -1565,8 +1839,8 @@ function lineEnd(source, offset) {
|
|
|
1565
1839
|
}
|
|
1566
1840
|
function formatDiagnostic(source, d) {
|
|
1567
1841
|
const codeTag = d.code ? `[${d.code}]` : "";
|
|
1568
|
-
const
|
|
1569
|
-
const lines = [
|
|
1842
|
+
const header2 = `${d.severity}${codeTag}: ${d.message}`;
|
|
1843
|
+
const lines = [header2];
|
|
1570
1844
|
if (d.span) {
|
|
1571
1845
|
const { line, col } = offsetToLineCol(source, d.span.start);
|
|
1572
1846
|
const ls = lineStart(source, d.span.start);
|
|
@@ -1592,11 +1866,211 @@ function formatDiagnostic(source, d) {
|
|
|
1592
1866
|
return lines.join("\n");
|
|
1593
1867
|
}
|
|
1594
1868
|
|
|
1869
|
+
// src/export/dxf.ts
|
|
1870
|
+
function num(v) {
|
|
1871
|
+
const r = Math.round(v * 1e4) / 1e4;
|
|
1872
|
+
return Object.is(r, -0) ? "0" : String(r);
|
|
1873
|
+
}
|
|
1874
|
+
var DxfBuilder = class {
|
|
1875
|
+
out = [];
|
|
1876
|
+
/** group-code / value pair. */
|
|
1877
|
+
pair(code, value) {
|
|
1878
|
+
this.out.push(String(code), String(value));
|
|
1879
|
+
}
|
|
1880
|
+
line(layer, a, b) {
|
|
1881
|
+
this.pair(0, "LINE");
|
|
1882
|
+
this.pair(8, layer);
|
|
1883
|
+
this.pair(10, num(a.x));
|
|
1884
|
+
this.pair(20, num(-a.y));
|
|
1885
|
+
this.pair(11, num(b.x));
|
|
1886
|
+
this.pair(21, num(-b.y));
|
|
1887
|
+
}
|
|
1888
|
+
arc(layer, center, radius, startDeg, endDeg) {
|
|
1889
|
+
this.pair(0, "ARC");
|
|
1890
|
+
this.pair(8, layer);
|
|
1891
|
+
this.pair(10, num(center.x));
|
|
1892
|
+
this.pair(20, num(-center.y));
|
|
1893
|
+
this.pair(40, num(radius));
|
|
1894
|
+
this.pair(50, num(startDeg));
|
|
1895
|
+
this.pair(51, num(endDeg));
|
|
1896
|
+
}
|
|
1897
|
+
text(layer, at, height, value) {
|
|
1898
|
+
this.pair(0, "TEXT");
|
|
1899
|
+
this.pair(8, layer);
|
|
1900
|
+
this.pair(10, num(at.x));
|
|
1901
|
+
this.pair(20, num(-at.y));
|
|
1902
|
+
this.pair(40, num(height));
|
|
1903
|
+
this.pair(1, value.replace(/\n/g, " "));
|
|
1904
|
+
}
|
|
1905
|
+
rect(layer, corners) {
|
|
1906
|
+
for (let i = 0; i < corners.length; i++) {
|
|
1907
|
+
this.line(layer, corners[i], corners[(i + 1) % corners.length]);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
toString() {
|
|
1911
|
+
return this.out.join("\n") + "\n";
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
var LAYERS = ["WALLS", "ROOMS", "DOORS", "WINDOWS", "FURNITURE", "COLUMNS", "DIMS", "LABELS"];
|
|
1915
|
+
function header() {
|
|
1916
|
+
const h = [];
|
|
1917
|
+
const p = (c, v) => h.push(String(c), String(v));
|
|
1918
|
+
p(0, "SECTION");
|
|
1919
|
+
p(2, "HEADER");
|
|
1920
|
+
p(9, "$ACADVER");
|
|
1921
|
+
p(1, "AC1009");
|
|
1922
|
+
p(0, "ENDSEC");
|
|
1923
|
+
p(0, "SECTION");
|
|
1924
|
+
p(2, "TABLES");
|
|
1925
|
+
p(0, "TABLE");
|
|
1926
|
+
p(2, "LAYER");
|
|
1927
|
+
p(70, LAYERS.length);
|
|
1928
|
+
for (const name of LAYERS) {
|
|
1929
|
+
p(0, "LAYER");
|
|
1930
|
+
p(2, name);
|
|
1931
|
+
p(70, 0);
|
|
1932
|
+
p(62, 7);
|
|
1933
|
+
p(6, "CONTINUOUS");
|
|
1934
|
+
}
|
|
1935
|
+
p(0, "ENDTAB");
|
|
1936
|
+
p(0, "ENDSEC");
|
|
1937
|
+
return h.join("\n") + "\n";
|
|
1938
|
+
}
|
|
1939
|
+
function emitDoor(b, dr) {
|
|
1940
|
+
const seg = dr.host;
|
|
1941
|
+
if (!seg) return;
|
|
1942
|
+
const d = unit(sub(seg.b, seg.a));
|
|
1943
|
+
const n = normal(d);
|
|
1944
|
+
const hw = dr.width / 2;
|
|
1945
|
+
const hinge = dr.hinge === "left" ? add(dr.at, mul(d, -hw)) : add(dr.at, mul(d, hw));
|
|
1946
|
+
const farJamb = dr.hinge === "left" ? add(dr.at, mul(d, hw)) : add(dr.at, mul(d, -hw));
|
|
1947
|
+
const leafDir = dr.swing === "in" ? n : mul(n, -1);
|
|
1948
|
+
const leafEnd = add(hinge, mul(leafDir, dr.width));
|
|
1949
|
+
b.line("DOORS", hinge, leafEnd);
|
|
1950
|
+
const deg = (p) => Math.atan2(-(p.y - hinge.y), p.x - hinge.x) * 180 / Math.PI;
|
|
1951
|
+
const a1 = deg(leafEnd);
|
|
1952
|
+
const a2 = deg(farJamb);
|
|
1953
|
+
const ccw = ((a2 - a1) % 360 + 360) % 360;
|
|
1954
|
+
if (ccw <= 180) b.arc("DOORS", hinge, dr.width, a1, a2);
|
|
1955
|
+
else b.arc("DOORS", hinge, dr.width, a2, a1);
|
|
1956
|
+
}
|
|
1957
|
+
function emitWindow(b, wn) {
|
|
1958
|
+
const seg = wn.host;
|
|
1959
|
+
if (!seg) return;
|
|
1960
|
+
const d = unit(sub(seg.b, seg.a));
|
|
1961
|
+
const n = normal(d);
|
|
1962
|
+
const hw = wn.width / 2;
|
|
1963
|
+
const h = seg.thickness / 2;
|
|
1964
|
+
const jA = add(wn.at, mul(d, -hw));
|
|
1965
|
+
const jB = add(wn.at, mul(d, hw));
|
|
1966
|
+
b.line("WINDOWS", add(jA, mul(n, h)), add(jB, mul(n, h)));
|
|
1967
|
+
b.line("WINDOWS", add(jA, mul(n, -h)), add(jB, mul(n, -h)));
|
|
1968
|
+
b.line("WINDOWS", jA, jB);
|
|
1969
|
+
}
|
|
1970
|
+
function emitDim(b, dm) {
|
|
1971
|
+
const dd = unit(sub(dm.to, dm.from));
|
|
1972
|
+
const dn = normal(dd);
|
|
1973
|
+
const p1 = add(dm.from, mul(dn, dm.offset));
|
|
1974
|
+
const p2 = add(dm.to, mul(dn, dm.offset));
|
|
1975
|
+
b.line("DIMS", p1, p2);
|
|
1976
|
+
if (dm.text) {
|
|
1977
|
+
const mid = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };
|
|
1978
|
+
b.text("DIMS", mid, 150, dm.text);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
function toDxf(ir) {
|
|
1982
|
+
const b = new DxfBuilder();
|
|
1983
|
+
b.pair(0, "SECTION");
|
|
1984
|
+
b.pair(2, "ENTITIES");
|
|
1985
|
+
const labelAt = (at, w, h) => ({ x: at.x + w / 2, y: at.y + h / 2 });
|
|
1986
|
+
for (const el of ir.elements) {
|
|
1987
|
+
switch (el.kind) {
|
|
1988
|
+
case "wall":
|
|
1989
|
+
for (const s of segmentsOfWall(el)) {
|
|
1990
|
+
const d = unit(sub(s.b, s.a));
|
|
1991
|
+
const n = normal(d);
|
|
1992
|
+
const off = s.thickness / 2;
|
|
1993
|
+
b.line("WALLS", add(s.a, mul(n, off)), add(s.b, mul(n, off)));
|
|
1994
|
+
b.line("WALLS", add(s.a, mul(n, -off)), add(s.b, mul(n, -off)));
|
|
1995
|
+
}
|
|
1996
|
+
break;
|
|
1997
|
+
case "room":
|
|
1998
|
+
b.rect("ROOMS", rectCorners(el.at.x, el.at.y, el.size.w, el.size.h));
|
|
1999
|
+
if (el.label) b.text("LABELS", labelAt(el.at, el.size.w, el.size.h), 200, el.label);
|
|
2000
|
+
break;
|
|
2001
|
+
case "furniture":
|
|
2002
|
+
b.rect("FURNITURE", rectCorners(el.at.x, el.at.y, el.size.w, el.size.h));
|
|
2003
|
+
if (el.label) b.text("LABELS", labelAt(el.at, el.size.w, el.size.h), 150, el.label);
|
|
2004
|
+
break;
|
|
2005
|
+
case "column":
|
|
2006
|
+
b.rect("COLUMNS", rectCorners(el.at.x, el.at.y, el.size.w, el.size.h));
|
|
2007
|
+
break;
|
|
2008
|
+
case "door":
|
|
2009
|
+
emitDoor(b, el);
|
|
2010
|
+
break;
|
|
2011
|
+
case "window":
|
|
2012
|
+
emitWindow(b, el);
|
|
2013
|
+
break;
|
|
2014
|
+
case "dim":
|
|
2015
|
+
emitDim(b, el);
|
|
2016
|
+
break;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
b.pair(0, "ENDSEC");
|
|
2020
|
+
const entities = b.toString();
|
|
2021
|
+
return header() + entities + "0\nEOF\n";
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// src/export/pdf.ts
|
|
2025
|
+
function svgSize(svg) {
|
|
2026
|
+
const w = /<svg[^>]*\bwidth="([\d.]+)/.exec(svg);
|
|
2027
|
+
const h = /<svg[^>]*\bheight="([\d.]+)/.exec(svg);
|
|
2028
|
+
if (w && h) return { width: parseFloat(w[1]), height: parseFloat(h[1]) };
|
|
2029
|
+
const vb = /<svg[^>]*\bviewBox="[\d.\-]+ [\d.\-]+ ([\d.]+) ([\d.]+)"/.exec(svg);
|
|
2030
|
+
if (vb) return { width: parseFloat(vb[1]), height: parseFloat(vb[2]) };
|
|
2031
|
+
return { width: 800, height: 600 };
|
|
2032
|
+
}
|
|
2033
|
+
async function toPdf(svg) {
|
|
2034
|
+
let PDFDocument;
|
|
2035
|
+
let SVGtoPDF;
|
|
2036
|
+
try {
|
|
2037
|
+
PDFDocument = (await import("pdfkit")).default;
|
|
2038
|
+
SVGtoPDF = (await import("svg-to-pdfkit")).default;
|
|
2039
|
+
} catch {
|
|
2040
|
+
throw new Error(
|
|
2041
|
+
"PDF export needs the optional dependencies 'pdfkit' and 'svg-to-pdfkit'. Install them: npm install pdfkit svg-to-pdfkit"
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
const { width, height } = svgSize(svg);
|
|
2045
|
+
const doc = new PDFDocument({ size: [width, height], margin: 0 });
|
|
2046
|
+
const chunks = [];
|
|
2047
|
+
const done = new Promise((resolve2, reject) => {
|
|
2048
|
+
doc.on("data", (c) => chunks.push(c));
|
|
2049
|
+
doc.on("end", () => resolve2());
|
|
2050
|
+
doc.on("error", (e) => reject(e));
|
|
2051
|
+
});
|
|
2052
|
+
SVGtoPDF(doc, svg, 0, 0, { width, height, assumePt: true });
|
|
2053
|
+
doc.end();
|
|
2054
|
+
await done;
|
|
2055
|
+
return concat(chunks);
|
|
2056
|
+
}
|
|
2057
|
+
function concat(chunks) {
|
|
2058
|
+
let total = 0;
|
|
2059
|
+
for (const c of chunks) total += c.length;
|
|
2060
|
+
const out = new Uint8Array(total);
|
|
2061
|
+
let offset = 0;
|
|
2062
|
+
for (const c of chunks) {
|
|
2063
|
+
out.set(c, offset);
|
|
2064
|
+
offset += c.length;
|
|
2065
|
+
}
|
|
2066
|
+
return out;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
1595
2069
|
// src/index.ts
|
|
1596
2070
|
var cache = /* @__PURE__ */ new Map();
|
|
1597
2071
|
var CACHE_MAX = 64;
|
|
1598
2072
|
function compile(source, opts = {}) {
|
|
1599
|
-
const key = JSON.stringify([source, opts.width ?? null]);
|
|
2073
|
+
const key = JSON.stringify([source, opts.width ?? null, opts.theme ?? null]);
|
|
1600
2074
|
if (!opts.noCache) {
|
|
1601
2075
|
const hit = cache.get(key);
|
|
1602
2076
|
if (hit) return hit;
|
|
@@ -1631,9 +2105,12 @@ function clearCache() {
|
|
|
1631
2105
|
}
|
|
1632
2106
|
|
|
1633
2107
|
export {
|
|
2108
|
+
resolve,
|
|
1634
2109
|
offsetToLineCol,
|
|
1635
2110
|
formatDiagnostic,
|
|
2111
|
+
toDxf,
|
|
2112
|
+
toPdf,
|
|
1636
2113
|
compile,
|
|
1637
2114
|
clearCache
|
|
1638
2115
|
};
|
|
1639
|
-
//# sourceMappingURL=chunk-
|
|
2116
|
+
//# sourceMappingURL=chunk-PABYLU6Z.js.map
|