schematex 0.9.1 → 0.9.3
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 +21 -0
- package/dist/ai/ai-sdk.cjs +24 -24
- package/dist/ai/ai-sdk.d.cts +3 -3
- package/dist/ai/ai-sdk.d.ts +3 -3
- package/dist/ai/ai-sdk.js +19 -19
- package/dist/ai/index.cjs +33 -33
- package/dist/ai/index.d.cts +4 -4
- package/dist/ai/index.d.ts +4 -4
- package/dist/ai/index.js +20 -20
- package/dist/{api-OED2jUVj.d.ts → api-B7C7qGiG.d.cts} +2 -2
- package/dist/{api-BDMaX1cT.d.cts → api-DXOhuK3e.d.ts} +2 -2
- package/dist/browser.cjs +25 -25
- package/dist/browser.d.cts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +19 -19
- package/dist/{chunk-JN6FHUC6.js → chunk-4CLAD7VZ.js} +3 -3
- package/dist/{chunk-JN6FHUC6.js.map → chunk-4CLAD7VZ.js.map} +1 -1
- package/dist/{chunk-YTGOLTLJ.js → chunk-6GHXO3WQ.js} +4 -4
- package/dist/{chunk-YTGOLTLJ.js.map → chunk-6GHXO3WQ.js.map} +1 -1
- package/dist/{chunk-T5KHNJ67.cjs → chunk-6W6RFZND.cjs} +6 -6
- package/dist/{chunk-T5KHNJ67.cjs.map → chunk-6W6RFZND.cjs.map} +1 -1
- package/dist/{chunk-KF3DFASA.js → chunk-7P4C5DMD.js} +3 -3
- package/dist/chunk-7P4C5DMD.js.map +1 -0
- package/dist/{chunk-KGOZBABH.cjs → chunk-AEZVCGH4.cjs} +12 -12
- package/dist/{chunk-KGOZBABH.cjs.map → chunk-AEZVCGH4.cjs.map} +1 -1
- package/dist/{chunk-QYC6WZEM.cjs → chunk-AHSSFGDJ.cjs} +3064 -511
- package/dist/chunk-AHSSFGDJ.cjs.map +1 -0
- package/dist/{chunk-FJVQGBPU.cjs → chunk-ATE7LD6I.cjs} +5 -5
- package/dist/{chunk-FJVQGBPU.cjs.map → chunk-ATE7LD6I.cjs.map} +1 -1
- package/dist/{chunk-VOJGLBE5.js → chunk-BP6MLXJU.js} +2890 -338
- package/dist/chunk-BP6MLXJU.js.map +1 -0
- package/dist/{chunk-YMFYPB5Y.cjs → chunk-BXS53MLV.cjs} +4 -4
- package/dist/{chunk-YMFYPB5Y.cjs.map → chunk-BXS53MLV.cjs.map} +1 -1
- package/dist/{chunk-B5AQ3CG3.cjs → chunk-C2BNO3CI.cjs} +16 -13
- package/dist/chunk-C2BNO3CI.cjs.map +1 -0
- package/dist/{chunk-NZT5P2XZ.cjs → chunk-CAAMBDEN.cjs} +347 -2
- package/dist/chunk-CAAMBDEN.cjs.map +1 -0
- package/dist/{chunk-UK6HF6PE.js → chunk-D4QZ6UDO.js} +342 -3
- package/dist/chunk-D4QZ6UDO.js.map +1 -0
- package/dist/{chunk-DHI7YAQJ.cjs → chunk-FFAJQ36U.cjs} +15 -15
- package/dist/{chunk-DHI7YAQJ.cjs.map → chunk-FFAJQ36U.cjs.map} +1 -1
- package/dist/{chunk-LENRV7ZJ.cjs → chunk-IOD2EFMX.cjs} +5 -5
- package/dist/chunk-IOD2EFMX.cjs.map +1 -0
- package/dist/{chunk-JIUC4DRS.cjs → chunk-IU26USXA.cjs} +6 -6
- package/dist/chunk-IU26USXA.cjs.map +1 -0
- package/dist/{chunk-Z5ML4QYG.js → chunk-JCJWSW5Y.js} +406 -4
- package/dist/chunk-JCJWSW5Y.js.map +1 -0
- package/dist/{chunk-DX44TBFZ.js → chunk-JEQGWH5N.js} +20 -28
- package/dist/chunk-JEQGWH5N.js.map +1 -0
- package/dist/{chunk-NXU4XKLY.cjs → chunk-JHJJT5H5.cjs} +9 -7
- package/dist/chunk-JHJJT5H5.cjs.map +1 -0
- package/dist/{chunk-AJJYWXZB.cjs → chunk-JYAL26WQ.cjs} +4 -4
- package/dist/chunk-JYAL26WQ.cjs.map +1 -0
- package/dist/{chunk-2R4UXKCT.cjs → chunk-LFJE64RD.cjs} +408 -6
- package/dist/chunk-LFJE64RD.cjs.map +1 -0
- package/dist/{chunk-IXRPRMHI.js → chunk-MTIZIHWE.js} +9 -7
- package/dist/chunk-MTIZIHWE.js.map +1 -0
- package/dist/{chunk-NFZMNKOR.cjs → chunk-MZWVJFTV.cjs} +15 -15
- package/dist/{chunk-NFZMNKOR.cjs.map → chunk-MZWVJFTV.cjs.map} +1 -1
- package/dist/{chunk-6L46VIXI.cjs → chunk-O6A2GJLI.cjs} +20 -28
- package/dist/chunk-O6A2GJLI.cjs.map +1 -0
- package/dist/{chunk-K2D6VFLP.js → chunk-OND4N5ZZ.js} +78 -22
- package/dist/chunk-OND4N5ZZ.js.map +1 -0
- package/dist/{chunk-J34HDRFY.js → chunk-PUD7PIY5.js} +4 -4
- package/dist/{chunk-J34HDRFY.js.map → chunk-PUD7PIY5.js.map} +1 -1
- package/dist/{chunk-6NUAGU6O.js → chunk-PZCYFT2A.js} +3 -3
- package/dist/{chunk-6NUAGU6O.js.map → chunk-PZCYFT2A.js.map} +1 -1
- package/dist/{chunk-EBX4KCYW.js → chunk-QHZEGWE7.js} +4 -4
- package/dist/{chunk-EBX4KCYW.js.map → chunk-QHZEGWE7.js.map} +1 -1
- package/dist/{chunk-ABCMTAOZ.js → chunk-TDSGQT7R.js} +7 -4
- package/dist/chunk-TDSGQT7R.js.map +1 -0
- package/dist/{chunk-47SGK5R6.cjs → chunk-TFNH2NLJ.cjs} +5 -5
- package/dist/{chunk-47SGK5R6.cjs.map → chunk-TFNH2NLJ.cjs.map} +1 -1
- package/dist/{chunk-CDK7KDIW.js → chunk-U4AJLMHC.js} +4 -4
- package/dist/{chunk-CDK7KDIW.js.map → chunk-U4AJLMHC.js.map} +1 -1
- package/dist/{chunk-UOM3EDE6.js → chunk-UNLLWCQK.js} +3 -3
- package/dist/{chunk-UOM3EDE6.js.map → chunk-UNLLWCQK.js.map} +1 -1
- package/dist/{chunk-D34VGLSE.cjs → chunk-VG5LP5A4.cjs} +5 -5
- package/dist/{chunk-D34VGLSE.cjs.map → chunk-VG5LP5A4.cjs.map} +1 -1
- package/dist/{chunk-Q2YRJHFB.cjs → chunk-VOFND6ZQ.cjs} +7 -7
- package/dist/chunk-VOFND6ZQ.cjs.map +1 -0
- package/dist/{chunk-JGCKW5RS.js → chunk-X3GEGVW2.js} +5 -5
- package/dist/chunk-X3GEGVW2.js.map +1 -0
- package/dist/{chunk-GDZNTILD.js → chunk-XAYD5NVJ.js} +4 -4
- package/dist/chunk-XAYD5NVJ.js.map +1 -0
- package/dist/{chunk-2SZJQVPN.cjs → chunk-Y4OBXYGW.cjs} +87 -30
- package/dist/chunk-Y4OBXYGW.cjs.map +1 -0
- package/dist/{chunk-E7LXMEKX.js → chunk-ZQECHIBI.js} +3 -3
- package/dist/{chunk-E7LXMEKX.js.map → chunk-ZQECHIBI.js.map} +1 -1
- package/dist/{chunk-E3CAJGJM.js → chunk-ZUK4BY55.js} +6 -6
- package/dist/chunk-ZUK4BY55.js.map +1 -0
- package/dist/{diagnostics-hObcaaFC.d.ts → diagnostics-fyjWjeDa.d.cts} +1 -1
- package/dist/{diagnostics-hObcaaFC.d.cts → diagnostics-fyjWjeDa.d.ts} +1 -1
- package/dist/diagrams/blockdiagram/index.cjs +6 -5
- package/dist/diagrams/blockdiagram/index.d.cts +2 -2
- package/dist/diagrams/blockdiagram/index.d.ts +2 -2
- package/dist/diagrams/blockdiagram/index.js +2 -1
- package/dist/diagrams/circuit/index.cjs +9 -9
- package/dist/diagrams/circuit/index.d.cts +1 -1
- package/dist/diagrams/circuit/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.js +2 -2
- package/dist/diagrams/ecomap/index.cjs +7 -7
- package/dist/diagrams/ecomap/index.d.cts +1 -1
- package/dist/diagrams/ecomap/index.d.ts +1 -1
- package/dist/diagrams/ecomap/index.js +2 -2
- package/dist/diagrams/entity/index.cjs +6 -6
- package/dist/diagrams/entity/index.d.cts +1 -1
- package/dist/diagrams/entity/index.d.ts +1 -1
- package/dist/diagrams/entity/index.js +2 -2
- package/dist/diagrams/fishbone/index.cjs +8 -8
- package/dist/diagrams/fishbone/index.d.cts +1 -1
- package/dist/diagrams/fishbone/index.d.ts +1 -1
- package/dist/diagrams/fishbone/index.js +2 -2
- package/dist/diagrams/flowchart/index.cjs +8 -8
- package/dist/diagrams/flowchart/index.d.cts +2 -2
- package/dist/diagrams/flowchart/index.d.ts +2 -2
- package/dist/diagrams/flowchart/index.js +2 -2
- package/dist/diagrams/genogram/index.cjs +9 -9
- package/dist/diagrams/genogram/index.d.cts +1 -1
- package/dist/diagrams/genogram/index.d.ts +1 -1
- package/dist/diagrams/genogram/index.js +2 -2
- package/dist/diagrams/ladder/index.cjs +6 -6
- package/dist/diagrams/ladder/index.d.cts +1 -1
- package/dist/diagrams/ladder/index.d.ts +1 -1
- package/dist/diagrams/ladder/index.js +2 -2
- package/dist/diagrams/logic/index.cjs +8 -8
- package/dist/diagrams/logic/index.d.cts +1 -1
- package/dist/diagrams/logic/index.d.ts +1 -1
- package/dist/diagrams/logic/index.js +2 -2
- package/dist/diagrams/orgchart/index.cjs +8 -8
- package/dist/diagrams/orgchart/index.d.cts +1 -1
- package/dist/diagrams/orgchart/index.d.ts +1 -1
- package/dist/diagrams/orgchart/index.js +2 -2
- package/dist/diagrams/pedigree/index.cjs +7 -7
- package/dist/diagrams/pedigree/index.d.cts +1 -1
- package/dist/diagrams/pedigree/index.d.ts +1 -1
- package/dist/diagrams/pedigree/index.js +2 -2
- package/dist/diagrams/phylo/index.cjs +7 -7
- package/dist/diagrams/phylo/index.d.cts +1 -1
- package/dist/diagrams/phylo/index.d.ts +1 -1
- package/dist/diagrams/phylo/index.js +2 -2
- package/dist/diagrams/sld/index.cjs +8 -8
- package/dist/diagrams/sld/index.d.cts +1 -1
- package/dist/diagrams/sld/index.d.ts +1 -1
- package/dist/diagrams/sld/index.js +2 -2
- package/dist/diagrams/sociogram/index.cjs +6 -6
- package/dist/diagrams/sociogram/index.d.cts +1 -1
- package/dist/diagrams/sociogram/index.d.ts +1 -1
- package/dist/diagrams/sociogram/index.js +2 -2
- package/dist/diagrams/timing/index.cjs +4 -4
- package/dist/diagrams/timing/index.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +1 -1
- package/dist/diagrams/timing/index.js +1 -1
- package/dist/diagrams/venn/index.cjs +9 -9
- package/dist/diagrams/venn/index.d.cts +1 -1
- package/dist/diagrams/venn/index.d.ts +1 -1
- package/dist/diagrams/venn/index.js +2 -2
- package/dist/{index-Cq8y1aaa.d.ts → index-CFaKjWPy.d.ts} +1 -1
- package/dist/{index-BiXWjQht.d.cts → index-DZNmJWGg.d.cts} +1 -1
- package/dist/index.cjs +113 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +49 -23
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +19 -19
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +18 -18
- package/dist/{tools-D5dkAqNy.d.cts → tools-BDNih0Mb.d.ts} +3 -3
- package/dist/{tools-BbTuTWs_.d.ts → tools-C23t_WeN.d.cts} +3 -3
- package/package.json +1 -1
- package/dist/chunk-2R4UXKCT.cjs.map +0 -1
- package/dist/chunk-2SZJQVPN.cjs.map +0 -1
- package/dist/chunk-6L46VIXI.cjs.map +0 -1
- package/dist/chunk-ABCMTAOZ.js.map +0 -1
- package/dist/chunk-AJJYWXZB.cjs.map +0 -1
- package/dist/chunk-B5AQ3CG3.cjs.map +0 -1
- package/dist/chunk-DX44TBFZ.js.map +0 -1
- package/dist/chunk-E3CAJGJM.js.map +0 -1
- package/dist/chunk-GDZNTILD.js.map +0 -1
- package/dist/chunk-IXRPRMHI.js.map +0 -1
- package/dist/chunk-JGCKW5RS.js.map +0 -1
- package/dist/chunk-JIUC4DRS.cjs.map +0 -1
- package/dist/chunk-K2D6VFLP.js.map +0 -1
- package/dist/chunk-KF3DFASA.js.map +0 -1
- package/dist/chunk-LENRV7ZJ.cjs.map +0 -1
- package/dist/chunk-NXU4XKLY.cjs.map +0 -1
- package/dist/chunk-NZT5P2XZ.cjs.map +0 -1
- package/dist/chunk-Q2YRJHFB.cjs.map +0 -1
- package/dist/chunk-QYC6WZEM.cjs.map +0 -1
- package/dist/chunk-UK6HF6PE.js.map +0 -1
- package/dist/chunk-VOJGLBE5.js.map +0 -1
- package/dist/chunk-Z5ML4QYG.js.map +0 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { orgchart } from './chunk-
|
|
2
|
-
import { circuit } from './chunk-
|
|
3
|
-
import { blockdiagram } from './chunk-
|
|
4
|
-
import { ladder } from './chunk-
|
|
5
|
-
import { sld } from './chunk-
|
|
6
|
-
import { entity } from './chunk-
|
|
7
|
-
import { fishbone } from './chunk-
|
|
8
|
-
import { venn } from './chunk-
|
|
9
|
-
import { flowchart, layoutFlowchart } from './chunk-
|
|
10
|
-
import { genogram } from './chunk-
|
|
11
|
-
import { ecomap } from './chunk-
|
|
12
|
-
import { pedigree } from './chunk-
|
|
1
|
+
import { orgchart } from './chunk-ZUK4BY55.js';
|
|
2
|
+
import { circuit } from './chunk-QHZEGWE7.js';
|
|
3
|
+
import { blockdiagram } from './chunk-JEQGWH5N.js';
|
|
4
|
+
import { ladder } from './chunk-X3GEGVW2.js';
|
|
5
|
+
import { sld } from './chunk-7P4C5DMD.js';
|
|
6
|
+
import { entity } from './chunk-XAYD5NVJ.js';
|
|
7
|
+
import { fishbone } from './chunk-6GHXO3WQ.js';
|
|
8
|
+
import { venn } from './chunk-UNLLWCQK.js';
|
|
9
|
+
import { flowchart, layoutFlowchart } from './chunk-PUD7PIY5.js';
|
|
10
|
+
import { genogram } from './chunk-PZCYFT2A.js';
|
|
11
|
+
import { ecomap, estimateTextWidth } from './chunk-OND4N5ZZ.js';
|
|
12
|
+
import { pedigree } from './chunk-4CLAD7VZ.js';
|
|
13
13
|
import { parseFrontmatter } from './chunk-2KTQ75LN.js';
|
|
14
|
-
import { phylo } from './chunk-
|
|
15
|
-
import { sociogram } from './chunk-
|
|
16
|
-
import { timing } from './chunk-
|
|
17
|
-
import { logic } from './chunk-
|
|
18
|
-
import { resolveBaseTheme, resolveTimelineTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme } from './chunk-
|
|
14
|
+
import { phylo } from './chunk-ZQECHIBI.js';
|
|
15
|
+
import { sociogram } from './chunk-TDSGQT7R.js';
|
|
16
|
+
import { timing } from './chunk-MTIZIHWE.js';
|
|
17
|
+
import { logic } from './chunk-U4AJLMHC.js';
|
|
18
|
+
import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme, TITLE } from './chunk-D4QZ6UDO.js';
|
|
19
19
|
import { QUOTE_PAIRS, stripQuotes, isOpenQuote, extractQuotedString, matchQuotedTitle } from './chunk-TO6PNBT3.js';
|
|
20
20
|
import { el, escapeXml, group, rect, text, line, path, circle, polygon, title, desc, svgRoot, defs, multilineText } from './chunk-SYYBKDL7.js';
|
|
21
21
|
|
|
@@ -1253,7 +1253,7 @@ var CLASS_PALETTE = [
|
|
|
1253
1253
|
function buildCss2(t) {
|
|
1254
1254
|
return `
|
|
1255
1255
|
.lt-dtree { font-family: system-ui, -apple-system, sans-serif; }
|
|
1256
|
-
.lt-dtree-title { font:
|
|
1256
|
+
.lt-dtree-title { font: 700 16px sans-serif; fill: ${t.text}; }
|
|
1257
1257
|
.lt-dtree-edge { fill: none; stroke: ${t.stroke}; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
|
|
1258
1258
|
.lt-dtree-edge-optimal { fill: none; stroke: ${t.positive}; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
|
|
1259
1259
|
.lt-dtree-edge-leader { fill: none; stroke: ${t.stroke}; stroke-width: 1; stroke-dasharray: 2 2; opacity: 0.55; }
|
|
@@ -1585,7 +1585,7 @@ function renderDecisionTree(ast, config) {
|
|
|
1585
1585
|
children.push(desc(`Decision tree (${ast.mode} mode) with ${layout.nodes.length} nodes and ${layout.edges.length} edges`));
|
|
1586
1586
|
children.push(el("style", {}, buildCss2(t)));
|
|
1587
1587
|
if (ast.title) {
|
|
1588
|
-
children.push(text({ x:
|
|
1588
|
+
children.push(text({ x: width / 2, y: 24, class: "lt-dtree-title", "text-anchor": "middle" }, ast.title));
|
|
1589
1589
|
}
|
|
1590
1590
|
const inner = [];
|
|
1591
1591
|
for (const e of layout.edges) {
|
|
@@ -2283,11 +2283,11 @@ function layoutSwimlane(ast) {
|
|
|
2283
2283
|
];
|
|
2284
2284
|
let labelY = candidates[0];
|
|
2285
2285
|
for (const y of candidates) {
|
|
2286
|
-
const
|
|
2287
|
-
const collide = labelBoxes.some((b) => Math.abs(b.y -
|
|
2286
|
+
const box2 = { x1: x - labelW / 2, x2: x + labelW / 2, y };
|
|
2287
|
+
const collide = labelBoxes.some((b) => Math.abs(b.y - box2.y) < 13 && b.x1 < box2.x2 && b.x2 > box2.x1);
|
|
2288
2288
|
if (!collide) {
|
|
2289
2289
|
labelY = y;
|
|
2290
|
-
labelBoxes.push(
|
|
2290
|
+
labelBoxes.push(box2);
|
|
2291
2291
|
break;
|
|
2292
2292
|
}
|
|
2293
2293
|
}
|
|
@@ -2448,10 +2448,10 @@ function layoutGantt(ast) {
|
|
|
2448
2448
|
let labelY = pinZoneTop + 12;
|
|
2449
2449
|
let step = 0;
|
|
2450
2450
|
while (step < 4) {
|
|
2451
|
-
const
|
|
2452
|
-
const collide = pinBoxes.some((b) => Math.abs(b.y -
|
|
2451
|
+
const box2 = { x1: x - labelW / 2, x2: x + labelW / 2, y: labelY };
|
|
2452
|
+
const collide = pinBoxes.some((b) => Math.abs(b.y - box2.y) < 14 && b.x1 < box2.x2 && b.x2 > box2.x1);
|
|
2453
2453
|
if (!collide) {
|
|
2454
|
-
pinBoxes.push(
|
|
2454
|
+
pinBoxes.push(box2);
|
|
2455
2455
|
break;
|
|
2456
2456
|
}
|
|
2457
2457
|
labelY += 16;
|
|
@@ -4124,40 +4124,42 @@ function shiftPathY(d, dy) {
|
|
|
4124
4124
|
|
|
4125
4125
|
// src/diagrams/state/renderer.ts
|
|
4126
4126
|
var ARROW_MARKER_ID = "lt-state-arrow";
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
.lt-state-
|
|
4130
|
-
.lt-state-
|
|
4131
|
-
.lt-state-
|
|
4127
|
+
function buildStyle(t) {
|
|
4128
|
+
return `
|
|
4129
|
+
.lt-state-body { fill: ${t.stateFill}; stroke: ${t.stateStroke}; stroke-width: 1.6; }
|
|
4130
|
+
.lt-state-name { font: 600 12px system-ui, sans-serif; fill: ${t.stateText}; }
|
|
4131
|
+
.lt-state-div { stroke: ${t.stateStroke}; stroke-width: 1; }
|
|
4132
|
+
.lt-state-activity { font: 11px ui-monospace, monospace; fill: ${t.activityText}; }
|
|
4132
4133
|
|
|
4133
|
-
.lt-composite-body { fill:
|
|
4134
|
-
.lt-composite-title { font: 600 12px system-ui, sans-serif; fill:
|
|
4135
|
-
.lt-composite-titlebar { fill:
|
|
4136
|
-
.lt-region-div { stroke:
|
|
4134
|
+
.lt-composite-body { fill: ${t.compositeFill}; stroke: ${t.stateStroke}; stroke-width: 1.6; }
|
|
4135
|
+
.lt-composite-title { font: 600 12px system-ui, sans-serif; fill: ${t.stateText}; }
|
|
4136
|
+
.lt-composite-titlebar { fill: ${t.compositeTitlebar}; stroke: ${t.stateStroke}; stroke-width: 1; }
|
|
4137
|
+
.lt-region-div { stroke: ${t.regionDiv}; stroke-width: 1; stroke-dasharray: 6 4; }
|
|
4137
4138
|
|
|
4138
|
-
.lt-ps-initial { fill:
|
|
4139
|
-
.lt-ps-final-outer { fill:
|
|
4140
|
-
.lt-ps-final-inner { fill:
|
|
4141
|
-
.lt-ps-choice { fill:
|
|
4142
|
-
.lt-ps-junction { fill:
|
|
4143
|
-
.lt-ps-bar { fill:
|
|
4144
|
-
.lt-ps-history-body { fill:
|
|
4145
|
-
.lt-ps-history-text { font: 600 11px serif; fill:
|
|
4146
|
-
.lt-ps-terminate { stroke:
|
|
4147
|
-
.lt-ps-entrypoint { fill:
|
|
4148
|
-
.lt-ps-exitpoint { fill:
|
|
4139
|
+
.lt-ps-initial { fill: ${t.psInk}; }
|
|
4140
|
+
.lt-ps-final-outer { fill: ${t.stateFill}; stroke: ${t.psInk}; stroke-width: 1.6; }
|
|
4141
|
+
.lt-ps-final-inner { fill: ${t.psInk}; }
|
|
4142
|
+
.lt-ps-choice { fill: ${t.stateFill}; stroke: ${t.psInk}; stroke-width: 1.6; }
|
|
4143
|
+
.lt-ps-junction { fill: ${t.psInk}; }
|
|
4144
|
+
.lt-ps-bar { fill: ${t.psInk}; }
|
|
4145
|
+
.lt-ps-history-body { fill: ${t.stateFill}; stroke: ${t.psInk}; stroke-width: 1.6; }
|
|
4146
|
+
.lt-ps-history-text { font: 600 11px serif; fill: ${t.psInk}; }
|
|
4147
|
+
.lt-ps-terminate { stroke: ${t.psInk}; stroke-width: 2; }
|
|
4148
|
+
.lt-ps-entrypoint { fill: ${t.stateFill}; stroke: ${t.psInk}; stroke-width: 1.6; }
|
|
4149
|
+
.lt-ps-exitpoint { fill: ${t.stateFill}; stroke: ${t.psInk}; stroke-width: 1.6; }
|
|
4149
4150
|
|
|
4150
|
-
.lt-transition { stroke:
|
|
4151
|
-
.lt-transition-label { font: 11px system-ui, sans-serif; fill:
|
|
4152
|
-
.lt-transition-label-bg { fill:
|
|
4151
|
+
.lt-transition { stroke: ${t.transitionStroke}; stroke-width: 1.4; fill: none; }
|
|
4152
|
+
.lt-transition-label { font: 11px system-ui, sans-serif; fill: ${t.transitionLabel}; }
|
|
4153
|
+
.lt-transition-label-bg { fill: ${t.labelBg}; opacity: 0.92; }
|
|
4153
4154
|
|
|
4154
|
-
.lt-note-body { fill:
|
|
4155
|
-
.lt-note-text { font: 11px system-ui, sans-serif; fill:
|
|
4156
|
-
.lt-note-leader { stroke:
|
|
4155
|
+
.lt-note-body { fill: ${t.noteFill}; stroke: ${t.noteStroke}; stroke-width: 1; }
|
|
4156
|
+
.lt-note-text { font: 11px system-ui, sans-serif; fill: ${t.noteText}; }
|
|
4157
|
+
.lt-note-leader { stroke: ${t.noteStroke}; stroke-width: 1; stroke-dasharray: 3 3; fill: none; }
|
|
4157
4158
|
|
|
4158
|
-
.lt-title { font:
|
|
4159
|
+
.lt-title { font: 700 16px system-ui, sans-serif; fill: ${t.stateText}; }
|
|
4159
4160
|
`;
|
|
4160
|
-
|
|
4161
|
+
}
|
|
4162
|
+
function renderArrowMarker(t) {
|
|
4161
4163
|
return el(
|
|
4162
4164
|
"marker",
|
|
4163
4165
|
{
|
|
@@ -4169,7 +4171,7 @@ function renderArrowMarker() {
|
|
|
4169
4171
|
orient: "auto",
|
|
4170
4172
|
markerUnits: "strokeWidth"
|
|
4171
4173
|
},
|
|
4172
|
-
[polygon({ points: "0,0 10,3 0,6", fill:
|
|
4174
|
+
[polygon({ points: "0,0 10,3 0,6", fill: t.transitionStroke })]
|
|
4173
4175
|
);
|
|
4174
4176
|
}
|
|
4175
4177
|
function activityText2(a) {
|
|
@@ -4400,12 +4402,12 @@ function renderNote(n) {
|
|
|
4400
4402
|
}
|
|
4401
4403
|
return group({ class: "lt-note", "data-target": n.note.target }, parts);
|
|
4402
4404
|
}
|
|
4403
|
-
function renderStateDiagram(ast,
|
|
4405
|
+
function renderStateDiagram(ast, config) {
|
|
4404
4406
|
const layout = layoutStateDiagram(ast);
|
|
4405
|
-
return renderLayout(layout);
|
|
4407
|
+
return renderLayout(layout, resolveStateTheme(config?.theme ?? "default"));
|
|
4406
4408
|
}
|
|
4407
|
-
function renderLayout(layout) {
|
|
4408
|
-
const titleNode = layout.title ? text({ x:
|
|
4409
|
+
function renderLayout(layout, t) {
|
|
4410
|
+
const titleNode = layout.title ? text({ x: layout.width / 2, y: 22, class: "lt-title", "text-anchor": "middle" }, layout.title) : "";
|
|
4409
4411
|
return svgRoot(
|
|
4410
4412
|
{
|
|
4411
4413
|
width: layout.width,
|
|
@@ -4417,7 +4419,7 @@ function renderLayout(layout) {
|
|
|
4417
4419
|
[
|
|
4418
4420
|
el("title", {}, escapeXml(`State Diagram${layout.title ? " \u2014 " + layout.title : ""}`)),
|
|
4419
4421
|
el("desc", {}, "UML 2.5 / Harel statechart rendered by Schematex"),
|
|
4420
|
-
defs([renderArrowMarker(), el("style", {},
|
|
4422
|
+
defs([renderArrowMarker(t), el("style", {}, buildStyle(t))]),
|
|
4421
4423
|
titleNode,
|
|
4422
4424
|
// Composite clusters first so simple-state bodies sit on top.
|
|
4423
4425
|
group({ class: "lt-clusters" }, layout.clusters.map(renderComposite)),
|
|
@@ -4437,7 +4439,7 @@ var state = {
|
|
|
4437
4439
|
parse: parseStateDiagram,
|
|
4438
4440
|
render(text2, config) {
|
|
4439
4441
|
const ast = parseStateDiagram(text2);
|
|
4440
|
-
return renderStateDiagram(ast);
|
|
4442
|
+
return renderStateDiagram(ast, config);
|
|
4441
4443
|
}
|
|
4442
4444
|
};
|
|
4443
4445
|
|
|
@@ -5787,7 +5789,7 @@ function resolveAnchor(id, port, fallback, equipById, instById) {
|
|
|
5787
5789
|
}
|
|
5788
5790
|
|
|
5789
5791
|
// src/diagrams/pid/renderer.ts
|
|
5790
|
-
var
|
|
5792
|
+
var STYLE = `
|
|
5791
5793
|
.lt-pid-equip { fill: #ffffff; stroke: #1d1d1d; stroke-width: 1.6; }
|
|
5792
5794
|
.lt-pid-equip-tag { font: 600 11px system-ui, sans-serif; fill: #1d1d1d; }
|
|
5793
5795
|
.lt-pid-tray-line { stroke: #555; stroke-width: 1; fill: none; }
|
|
@@ -5813,7 +5815,7 @@ var STYLE2 = `
|
|
|
5813
5815
|
.lt-pid-line-tag-bg { fill: #ffffff; stroke: #1d1d1d; stroke-width: 0.6; }
|
|
5814
5816
|
.lt-pid-line-tag-text { font: 9px ui-monospace, monospace; fill: #1d1d1d; }
|
|
5815
5817
|
|
|
5816
|
-
.lt-pid-title { font:
|
|
5818
|
+
.lt-pid-title { font: 700 16px system-ui, sans-serif; fill: #1d1d1d; }
|
|
5817
5819
|
|
|
5818
5820
|
.lt-pid-unknown-box { fill: none; stroke: #c0392b; stroke-width: 1.6; stroke-dasharray: 5 3; }
|
|
5819
5821
|
.lt-pid-unknown-mark { font: 700 18px system-ui, sans-serif; fill: #c0392b; }
|
|
@@ -5988,7 +5990,7 @@ function renderLayout2(layout) {
|
|
|
5988
5990
|
}
|
|
5989
5991
|
}
|
|
5990
5992
|
}
|
|
5991
|
-
const titleNode = layout.title ? text({ x:
|
|
5993
|
+
const titleNode = layout.title ? text({ x: layout.width / 2, y: 22, class: "lt-pid-title", "text-anchor": "middle" }, layout.title) : "";
|
|
5992
5994
|
return svgRoot(
|
|
5993
5995
|
{
|
|
5994
5996
|
width: layout.width,
|
|
@@ -6014,7 +6016,7 @@ function renderLayout2(layout) {
|
|
|
6014
6016
|
},
|
|
6015
6017
|
[polygon({ points: "0,0 10,3 0,6", fill: "#1d1d1d" })]
|
|
6016
6018
|
),
|
|
6017
|
-
el("style", {},
|
|
6019
|
+
el("style", {}, STYLE)
|
|
6018
6020
|
]),
|
|
6019
6021
|
titleNode,
|
|
6020
6022
|
// Z-order: process pipes behind equipment; signal lines + instruments above.
|
|
@@ -7386,7 +7388,7 @@ function headerHeightFor(lineCount) {
|
|
|
7386
7388
|
function buildCss3(t) {
|
|
7387
7389
|
return `
|
|
7388
7390
|
.prisma { font-family: system-ui, -apple-system, sans-serif; }
|
|
7389
|
-
.prisma-title { font:
|
|
7391
|
+
.prisma-title { font: 700 16px sans-serif; fill: ${t.text}; }
|
|
7390
7392
|
.prisma-subtitle { font: 400 12px sans-serif; fill: ${t.textMuted}; }
|
|
7391
7393
|
|
|
7392
7394
|
.prisma-stage { stroke-width: 1.1; stroke: #90a4ae; }
|
|
@@ -7451,10 +7453,10 @@ function arrowMarker(t) {
|
|
|
7451
7453
|
)
|
|
7452
7454
|
]);
|
|
7453
7455
|
}
|
|
7454
|
-
function classForBox(
|
|
7455
|
-
if (
|
|
7456
|
-
const base =
|
|
7457
|
-
return `${base} prisma-stage-${
|
|
7456
|
+
function classForBox(box2) {
|
|
7457
|
+
if (box2.variant === "previous") return "prisma-stage-previous";
|
|
7458
|
+
const base = box2.variant === "exclusion" ? "prisma-exclusion" : "prisma-stage";
|
|
7459
|
+
return `${base} prisma-stage-${box2.stage}`;
|
|
7458
7460
|
}
|
|
7459
7461
|
function classForLine(style) {
|
|
7460
7462
|
switch (style) {
|
|
@@ -7538,22 +7540,22 @@ function renderHeader(h) {
|
|
|
7538
7540
|
);
|
|
7539
7541
|
return group({ "data-header": h.column }, [r6, ...lines]);
|
|
7540
7542
|
}
|
|
7541
|
-
function renderBox(
|
|
7543
|
+
function renderBox(box2) {
|
|
7542
7544
|
const r6 = rect({
|
|
7543
|
-
x:
|
|
7544
|
-
y:
|
|
7545
|
-
width:
|
|
7546
|
-
height:
|
|
7545
|
+
x: box2.x,
|
|
7546
|
+
y: box2.y,
|
|
7547
|
+
width: box2.width,
|
|
7548
|
+
height: box2.height,
|
|
7547
7549
|
rx: PRISMA_CONST.BOX_RADIUS,
|
|
7548
7550
|
ry: PRISMA_CONST.BOX_RADIUS,
|
|
7549
|
-
class: classForBox(
|
|
7550
|
-
"data-role":
|
|
7551
|
+
class: classForBox(box2),
|
|
7552
|
+
"data-role": box2.role
|
|
7551
7553
|
});
|
|
7552
|
-
const innerLeft =
|
|
7553
|
-
const innerRight =
|
|
7554
|
-
const centerX =
|
|
7554
|
+
const innerLeft = box2.x + PRISMA_CONST.BOX_PAD_X;
|
|
7555
|
+
const innerRight = box2.x + box2.width - PRISMA_CONST.BOX_PAD_X;
|
|
7556
|
+
const centerX = box2.x + box2.width / 2;
|
|
7555
7557
|
let widestIndented = 0;
|
|
7556
|
-
for (const line2 of
|
|
7558
|
+
for (const line2 of box2.lines) {
|
|
7557
7559
|
if ((line2.indent ?? 0) > 0) {
|
|
7558
7560
|
const w = approxLineWidth(line2);
|
|
7559
7561
|
if (w > widestIndented) widestIndented = w;
|
|
@@ -7563,9 +7565,9 @@ function renderBox(box) {
|
|
|
7563
7565
|
innerLeft,
|
|
7564
7566
|
Math.min(centerX - widestIndented / 2, innerRight - widestIndented)
|
|
7565
7567
|
);
|
|
7566
|
-
let textY =
|
|
7568
|
+
let textY = box2.y + PRISMA_CONST.BOX_PAD_Y;
|
|
7567
7569
|
const textEls = [];
|
|
7568
|
-
for (const line2 of
|
|
7570
|
+
for (const line2 of box2.lines) {
|
|
7569
7571
|
const lh = lineHeightForLine(line2.style);
|
|
7570
7572
|
const baseline = textY + Math.round(lh * 0.75);
|
|
7571
7573
|
const indented = (line2.indent ?? 0) > 0;
|
|
@@ -7593,9 +7595,9 @@ function renderBox(box) {
|
|
|
7593
7595
|
}
|
|
7594
7596
|
return group(
|
|
7595
7597
|
{
|
|
7596
|
-
"data-prisma-role":
|
|
7597
|
-
"data-prisma-variant":
|
|
7598
|
-
"data-prisma-stage":
|
|
7598
|
+
"data-prisma-role": box2.role,
|
|
7599
|
+
"data-prisma-variant": box2.variant,
|
|
7600
|
+
"data-prisma-stage": box2.stage
|
|
7599
7601
|
},
|
|
7600
7602
|
[r6, ...textEls]
|
|
7601
7603
|
);
|
|
@@ -8269,7 +8271,7 @@ function ellipsePerimeterPoint(cx, cy, rx, ry, tx, ty) {
|
|
|
8269
8271
|
function isWideGlyph(cp) {
|
|
8270
8272
|
return cp >= 12288 && cp <= 40959 || cp >= 44032 && cp <= 55203 || cp >= 65280 && cp <= 65519;
|
|
8271
8273
|
}
|
|
8272
|
-
function
|
|
8274
|
+
function estimateTextWidth2(s, charW) {
|
|
8273
8275
|
let w = 0;
|
|
8274
8276
|
for (const ch of s) {
|
|
8275
8277
|
w += isWideGlyph(ch.codePointAt(0) ?? 0) ? charW * 1.7 : charW;
|
|
@@ -8278,10 +8280,10 @@ function estimateTextWidth(s, charW) {
|
|
|
8278
8280
|
}
|
|
8279
8281
|
function sizeEllipse(uc) {
|
|
8280
8282
|
const C2 = USECASE_CONST;
|
|
8281
|
-
const nameW =
|
|
8283
|
+
const nameW = estimateTextWidth2(uc.name, C2.CHAR_W_NAME);
|
|
8282
8284
|
let widest = nameW;
|
|
8283
8285
|
if (uc.stereotype) {
|
|
8284
|
-
widest = Math.max(widest,
|
|
8286
|
+
widest = Math.max(widest, estimateTextWidth2(`\xAB${uc.stereotype}\xBB`, C2.CHAR_W_EXT));
|
|
8285
8287
|
}
|
|
8286
8288
|
let stack = 0;
|
|
8287
8289
|
if (uc.stereotype) stack += C2.STEREO_LH;
|
|
@@ -8291,9 +8293,9 @@ function sizeEllipse(uc) {
|
|
|
8291
8293
|
stack += 8;
|
|
8292
8294
|
stack += C2.EXTPOINT_LH;
|
|
8293
8295
|
stack += uc.extensionPoints.length * C2.EXTPOINT_LH;
|
|
8294
|
-
widest = Math.max(widest,
|
|
8296
|
+
widest = Math.max(widest, estimateTextWidth2(C2.EXT_HEADER, C2.CHAR_W_EXT));
|
|
8295
8297
|
for (const ep of uc.extensionPoints) {
|
|
8296
|
-
widest = Math.max(widest,
|
|
8298
|
+
widest = Math.max(widest, estimateTextWidth2(ep, C2.CHAR_W_EXT) + 16);
|
|
8297
8299
|
}
|
|
8298
8300
|
}
|
|
8299
8301
|
const rx = Math.max(C2.MIN_RX, widest / 2 * Math.SQRT2 + C2.ELLIPSE_PAD_X);
|
|
@@ -8526,7 +8528,7 @@ function layoutUsecase(ast) {
|
|
|
8526
8528
|
const y = cy - h / 2;
|
|
8527
8529
|
const anchorX = side === "left" ? x + w : x;
|
|
8528
8530
|
const anchorY = isRect ? cy : cy - 6;
|
|
8529
|
-
const
|
|
8531
|
+
const box2 = {
|
|
8530
8532
|
actor: a,
|
|
8531
8533
|
x,
|
|
8532
8534
|
y,
|
|
@@ -8536,8 +8538,8 @@ function layoutUsecase(ast) {
|
|
|
8536
8538
|
anchorX,
|
|
8537
8539
|
anchorY
|
|
8538
8540
|
};
|
|
8539
|
-
actorBoxes.push(
|
|
8540
|
-
actorById2.set(a.id,
|
|
8541
|
+
actorBoxes.push(box2);
|
|
8542
|
+
actorById2.set(a.id, box2);
|
|
8541
8543
|
cy += C2.ACTOR_PITCH;
|
|
8542
8544
|
}
|
|
8543
8545
|
}
|
|
@@ -8549,7 +8551,7 @@ function layoutUsecase(ast) {
|
|
|
8549
8551
|
let right = b.x + b.width;
|
|
8550
8552
|
const isRect = b.actor.kind === "external" || b.actor.kind === "system";
|
|
8551
8553
|
if (!isRect) {
|
|
8552
|
-
const labelW =
|
|
8554
|
+
const labelW = estimateTextWidth2(b.actor.name, 6.4);
|
|
8553
8555
|
const cx = b.x + b.width / 2;
|
|
8554
8556
|
left = Math.min(left, cx - labelW / 2);
|
|
8555
8557
|
right = Math.max(right, cx + labelW / 2);
|
|
@@ -10124,7 +10126,7 @@ function layoutNetwork(ast, schedule) {
|
|
|
10124
10126
|
y = cursor;
|
|
10125
10127
|
cursor += C2.BOX_H + C2.V_GAP;
|
|
10126
10128
|
}
|
|
10127
|
-
const
|
|
10129
|
+
const box2 = {
|
|
10128
10130
|
id,
|
|
10129
10131
|
task: t,
|
|
10130
10132
|
computed,
|
|
@@ -10135,8 +10137,8 @@ function layoutNetwork(ast, schedule) {
|
|
|
10135
10137
|
milestone: t.milestone,
|
|
10136
10138
|
rank: r6
|
|
10137
10139
|
};
|
|
10138
|
-
boxes.push(
|
|
10139
|
-
boxById.set(id,
|
|
10140
|
+
boxes.push(box2);
|
|
10141
|
+
boxById.set(id, box2);
|
|
10140
10142
|
}
|
|
10141
10143
|
}
|
|
10142
10144
|
const edges = [];
|
|
@@ -10343,7 +10345,7 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10343
10345
|
const w = t.milestone ? C2.MS_W : C2.BOX_W;
|
|
10344
10346
|
const x = colX(r6) + (C2.BOX_W - w) / 2;
|
|
10345
10347
|
const y = y0 + idx * (C2.BOX_H + C2.V_GAP);
|
|
10346
|
-
const
|
|
10348
|
+
const box2 = {
|
|
10347
10349
|
id,
|
|
10348
10350
|
task: t,
|
|
10349
10351
|
computed: schedule.computed.get(id),
|
|
@@ -10354,8 +10356,8 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10354
10356
|
milestone: t.milestone,
|
|
10355
10357
|
rank: r6
|
|
10356
10358
|
};
|
|
10357
|
-
boxes.push(
|
|
10358
|
-
boxById.set(id,
|
|
10359
|
+
boxes.push(box2);
|
|
10360
|
+
boxById.set(id, box2);
|
|
10359
10361
|
});
|
|
10360
10362
|
}
|
|
10361
10363
|
}
|
|
@@ -10425,7 +10427,7 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10425
10427
|
const iv = intervalById.get(t.id);
|
|
10426
10428
|
const lane = laneOf.get(t.id);
|
|
10427
10429
|
const y = topPad + lane * (C2.TS_BOX_H + C2.TS_LANE_GAP);
|
|
10428
|
-
const
|
|
10430
|
+
const box2 = {
|
|
10429
10431
|
id: t.id,
|
|
10430
10432
|
task: t,
|
|
10431
10433
|
computed: schedule.computed.get(t.id),
|
|
@@ -10436,8 +10438,8 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10436
10438
|
milestone: t.milestone,
|
|
10437
10439
|
rank: 0
|
|
10438
10440
|
};
|
|
10439
|
-
boxes.push(
|
|
10440
|
-
boxById.set(t.id,
|
|
10441
|
+
boxes.push(box2);
|
|
10442
|
+
boxById.set(t.id, box2);
|
|
10441
10443
|
}
|
|
10442
10444
|
const edges = [];
|
|
10443
10445
|
for (const t of ast.tasks) {
|
|
@@ -13514,6 +13516,7 @@ var DET = "sx-net-detail";
|
|
|
13514
13516
|
var GLY = "sx-net-glyph";
|
|
13515
13517
|
var GLYL = "sx-net-glyph-line";
|
|
13516
13518
|
var ITX = "sx-net-icontext";
|
|
13519
|
+
var ITAG = "sx-net-icontag";
|
|
13517
13520
|
var CLOUD = "sx-net-cloud-body";
|
|
13518
13521
|
var CTX = "sx-net-cloudtext";
|
|
13519
13522
|
var r2 = (n) => Math.round(n * 100) / 100;
|
|
@@ -13557,7 +13560,7 @@ function switchBox(b, glyph) {
|
|
|
13557
13560
|
}
|
|
13558
13561
|
function poeSwitch(b) {
|
|
13559
13562
|
const cx = b.x + b.w / 2;
|
|
13560
|
-
return group({}, [switchBox(b, "straight"), text({ class:
|
|
13563
|
+
return group({}, [switchBox(b, "straight"), text({ class: ITAG, x: r2(cx), y: r2(b.y + b.h * 0.78 + 8), "text-anchor": "middle" }, "PoE")]);
|
|
13561
13564
|
}
|
|
13562
13565
|
function firewall(b) {
|
|
13563
13566
|
const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
|
|
@@ -13612,7 +13615,7 @@ function serverFarm(b, d) {
|
|
|
13612
13615
|
for (let i = 2; i >= 0; i--) {
|
|
13613
13616
|
parts.push(server({ x: b.x + i * off, y: b.y - i * off * 0.5, w: b.w - 2 * off, h: b.h - off }));
|
|
13614
13617
|
}
|
|
13615
|
-
if (d.count) parts.push(text({ class:
|
|
13618
|
+
if (d.count) parts.push(text({ class: ITAG, x: r2(b.x + b.w - 6), y: r2(b.y + b.h - 2), "text-anchor": "end" }, `\xD7${d.count}`));
|
|
13616
13619
|
return group({}, parts);
|
|
13617
13620
|
}
|
|
13618
13621
|
function pc(b) {
|
|
@@ -13750,9 +13753,9 @@ function drawDeviceIcon(d, b) {
|
|
|
13750
13753
|
case "router":
|
|
13751
13754
|
return router(b);
|
|
13752
13755
|
case "gateway":
|
|
13753
|
-
return group({}, [router(b), text({ class:
|
|
13756
|
+
return group({}, [router(b), text({ class: ITAG, x: r2(b.x + b.w / 2), y: r2(b.y + b.h - 1), "text-anchor": "middle" }, "GW")]);
|
|
13754
13757
|
case "vpngw":
|
|
13755
|
-
return group({}, [router(b), text({ class:
|
|
13758
|
+
return group({}, [router(b), text({ class: ITAG, x: r2(b.x + b.w / 2), y: r2(b.y + b.h - 1), "text-anchor": "middle" }, "VPN")]);
|
|
13756
13759
|
case "switch":
|
|
13757
13760
|
return switchBox(b, "straight");
|
|
13758
13761
|
case "l3switch":
|
|
@@ -13861,26 +13864,26 @@ function labelText(d) {
|
|
|
13861
13864
|
function deviceFootprint(d) {
|
|
13862
13865
|
return iconSize(d.kind);
|
|
13863
13866
|
}
|
|
13864
|
-
function effBox(
|
|
13865
|
-
const labelW = Math.max(
|
|
13867
|
+
function effBox(box2) {
|
|
13868
|
+
const labelW = Math.max(box2.w, labelText(box2.device).length * NET_CONST.CHAR_W + 6);
|
|
13866
13869
|
const half = labelW / 2;
|
|
13867
13870
|
return {
|
|
13868
|
-
left:
|
|
13869
|
-
top:
|
|
13870
|
-
right:
|
|
13871
|
-
bottom:
|
|
13872
|
-
};
|
|
13873
|
-
}
|
|
13874
|
-
function edgePoint(
|
|
13875
|
-
const dx = tx -
|
|
13876
|
-
const dy = ty -
|
|
13877
|
-
if (dx === 0 && dy === 0) return { x:
|
|
13878
|
-
const hw =
|
|
13879
|
-
const hh =
|
|
13871
|
+
left: box2.cx - half,
|
|
13872
|
+
top: box2.y,
|
|
13873
|
+
right: box2.cx + half,
|
|
13874
|
+
bottom: box2.y + box2.h + labelExtra(box2.device)
|
|
13875
|
+
};
|
|
13876
|
+
}
|
|
13877
|
+
function edgePoint(box2, tx, ty) {
|
|
13878
|
+
const dx = tx - box2.cx;
|
|
13879
|
+
const dy = ty - box2.cy;
|
|
13880
|
+
if (dx === 0 && dy === 0) return { x: box2.cx, y: box2.cy };
|
|
13881
|
+
const hw = box2.w / 2;
|
|
13882
|
+
const hh = box2.h / 2;
|
|
13880
13883
|
const sx = dx !== 0 ? hw / Math.abs(dx) : Infinity;
|
|
13881
13884
|
const sy = dy !== 0 ? hh / Math.abs(dy) : Infinity;
|
|
13882
13885
|
const s = Math.min(sx, sy);
|
|
13883
|
-
return { x:
|
|
13886
|
+
return { x: box2.cx + dx * s, y: box2.cy + dy * s };
|
|
13884
13887
|
}
|
|
13885
13888
|
function placeBanded(ast, ranks) {
|
|
13886
13889
|
const lr = ast.direction === "lr";
|
|
@@ -14232,16 +14235,54 @@ function layoutNetwork2(ast) {
|
|
|
14232
14235
|
depth: gb.depth
|
|
14233
14236
|
});
|
|
14234
14237
|
}
|
|
14238
|
+
const labelClearsDevices = (x, y, halfW) => {
|
|
14239
|
+
const margin = 6;
|
|
14240
|
+
for (const b of boxes) {
|
|
14241
|
+
const e = effBox(b);
|
|
14242
|
+
if (x + halfW >= e.left - margin && x - halfW <= e.right + margin && y + 5 >= e.top - margin && y - 5 <= e.bottom + margin) {
|
|
14243
|
+
return false;
|
|
14244
|
+
}
|
|
14245
|
+
}
|
|
14246
|
+
return true;
|
|
14247
|
+
};
|
|
14248
|
+
const placedLabels = [];
|
|
14249
|
+
const labelClearsPlaced = (x, y) => placedLabels.every((p) => Math.abs(p.x - x) > 90 || Math.abs(p.y - y) > 16);
|
|
14235
14250
|
const linkGeoms = links.map((link) => {
|
|
14236
14251
|
const a = boxById.get(link.from);
|
|
14237
14252
|
const b = boxById.get(link.to);
|
|
14238
14253
|
const p1 = edgePoint(a, b.cx, b.cy);
|
|
14239
14254
|
const p2 = edgePoint(b, a.cx, a.cy);
|
|
14255
|
+
const hasAnnotation = Boolean(
|
|
14256
|
+
link.label || link.speed || link.mode || link.vlans && link.vlans.length
|
|
14257
|
+
);
|
|
14258
|
+
let labelT = 0.5;
|
|
14259
|
+
if (hasAnnotation) {
|
|
14260
|
+
const annLen = (link.mode ? link.mode.length + 3 : 0) + (link.vlans && link.vlans.length ? 6 + link.vlans.join(",").length + 3 : 0) + (link.speed ? link.speed.length + 3 : 0) + (link.label ? link.label.length : 0);
|
|
14261
|
+
const halfW = annLen * 5.4 / 2;
|
|
14262
|
+
const candidates = [0.5, 0.38, 0.62, 0.28, 0.72, 0.2, 0.8, 0.14, 0.86];
|
|
14263
|
+
const at = (t) => ({
|
|
14264
|
+
x: p1.x + (p2.x - p1.x) * t,
|
|
14265
|
+
y: p1.y + (p2.y - p1.y) * t
|
|
14266
|
+
});
|
|
14267
|
+
const strict = candidates.find((t) => {
|
|
14268
|
+
const p = at(t);
|
|
14269
|
+
return labelClearsDevices(p.x, p.y, halfW) && labelClearsPlaced(p.x, p.y);
|
|
14270
|
+
});
|
|
14271
|
+
const relaxed = strict ?? candidates.find((t) => {
|
|
14272
|
+
const p = at(t);
|
|
14273
|
+
return labelClearsDevices(p.x, p.y, halfW);
|
|
14274
|
+
});
|
|
14275
|
+
labelT = relaxed ?? 0.5;
|
|
14276
|
+
placedLabels.push({
|
|
14277
|
+
x: p1.x + (p2.x - p1.x) * labelT,
|
|
14278
|
+
y: p1.y + (p2.y - p1.y) * labelT
|
|
14279
|
+
});
|
|
14280
|
+
}
|
|
14240
14281
|
return {
|
|
14241
14282
|
link,
|
|
14242
14283
|
points: [p1, p2],
|
|
14243
|
-
labelX:
|
|
14244
|
-
labelY:
|
|
14284
|
+
labelX: p1.x + (p2.x - p1.x) * labelT,
|
|
14285
|
+
labelY: p1.y + (p2.y - p1.y) * labelT
|
|
14245
14286
|
};
|
|
14246
14287
|
});
|
|
14247
14288
|
const width = maxX - minX + 2 * NET_CONST.PAD;
|
|
@@ -14269,11 +14310,12 @@ function buildCss8(t) {
|
|
|
14269
14310
|
.sx-net-glyph { fill: ${t.deviceAccent}; stroke: none; }
|
|
14270
14311
|
.sx-net-glyph-line { fill: none; stroke: ${t.deviceAccent}; stroke-width: 1.4; }
|
|
14271
14312
|
.sx-net-icontext { font: 700 8px sans-serif; fill: ${t.deviceAccent}; }
|
|
14313
|
+
.sx-net-icontag { font: 700 8px sans-serif; fill: ${t.subLabel}; paint-order: stroke; stroke: ${t.bg}; stroke-width: 2.5px; stroke-linejoin: round; }
|
|
14272
14314
|
.sx-net-cloud-body { fill: ${t.cloudFill}; stroke: ${t.cloudStroke}; stroke-width: 2; }
|
|
14273
14315
|
.sx-net-cloudtext { font: 600 13px sans-serif; fill: ${t.text}; }
|
|
14274
14316
|
.sx-net-bus { stroke: ${t.deviceStroke}; stroke-width: 4; stroke-linecap: round; }
|
|
14275
|
-
.sx-net-label { font: 12px sans-serif; fill: ${t.label}; }
|
|
14276
|
-
.sx-net-sublabel { font: 10px sans-serif; fill: ${t.subLabel}; }
|
|
14317
|
+
.sx-net-label { font: 12px sans-serif; fill: ${t.label}; paint-order: stroke; stroke: ${t.bg}; stroke-width: 3px; stroke-linejoin: round; }
|
|
14318
|
+
.sx-net-sublabel { font: 10px sans-serif; fill: ${t.subLabel}; paint-order: stroke; stroke: ${t.bg}; stroke-width: 3px; stroke-linejoin: round; }
|
|
14277
14319
|
.sx-net-link { fill: none; stroke-width: 2; }
|
|
14278
14320
|
.sx-net-link-wireless, .sx-net-link-vpn { stroke-dasharray: 5 4; }
|
|
14279
14321
|
.sx-net-link-lag { stroke-width: 3; }
|
|
@@ -16208,7 +16250,7 @@ function renderUmlClassLayout(layout, config) {
|
|
|
16208
16250
|
.sx-umlclass-edge-label { fill: ${theme.edgeLabel}; font-size: ${FONT_SIZE.small + 1}px; font-weight: 500; }
|
|
16209
16251
|
.sx-umlclass-edge-name { fill: ${theme.edgeLabel}; font-size: ${FONT_SIZE.label}px; font-style: italic; }
|
|
16210
16252
|
.sx-umlclass-edge-name-halo { fill: ${theme.bg}; stroke: ${theme.bg}; stroke-width: ${UMLCLASS_CONST.EDGE_LABEL_HALO}; stroke-linejoin: round; }
|
|
16211
|
-
.sx-umlclass-title { fill: ${theme.nameText}; font-size: ${FONT_SIZE.title}px; font-weight:
|
|
16253
|
+
.sx-umlclass-title { fill: ${theme.nameText}; font-size: ${FONT_SIZE.title}px; font-weight: 700; }
|
|
16212
16254
|
.sx-umlclass-package { fill: ${theme.packageFill}; stroke: ${theme.packageStroke}; stroke-width: 1; }
|
|
16213
16255
|
.sx-umlclass-package-label { fill: ${theme.packageLabel}; font-size: ${FONT_SIZE.label}px; font-weight: 600; }
|
|
16214
16256
|
`.trim()
|
|
@@ -17516,7 +17558,7 @@ function layoutFaultTree(ast) {
|
|
|
17516
17558
|
bump(g.cx + g.width / 2, g.cy + g.height / 2);
|
|
17517
17559
|
if (g.cond) bump(g.cond.x + g.cond.w / 2, g.cond.y + g.cond.h / 2);
|
|
17518
17560
|
}
|
|
17519
|
-
for (const
|
|
17561
|
+
for (const box2 of cutSetBoxes) bump(box2.x + box2.width, box2.y + box2.height);
|
|
17520
17562
|
for (const t of transfers) bump(t.x + 22, t.y + 30);
|
|
17521
17563
|
if (ast.title) bump(C2.CANVAS_PAD + ast.title.length * 8.5, 0);
|
|
17522
17564
|
if (ast.analysis.probability) {
|
|
@@ -17592,23 +17634,23 @@ function renderFaultTreeLayout(layout, config) {
|
|
|
17592
17634
|
const inner = [];
|
|
17593
17635
|
if (ast.title) {
|
|
17594
17636
|
inner.push(
|
|
17595
|
-
text({ x:
|
|
17637
|
+
text({ x: layout.width / 2, y: 22, class: "sx-ft-title", "font-family": fontFamily, "text-anchor": "middle" }, ast.title)
|
|
17596
17638
|
);
|
|
17597
17639
|
}
|
|
17598
|
-
for (const
|
|
17640
|
+
for (const box2 of layout.cutSetBoxes) {
|
|
17599
17641
|
inner.push(
|
|
17600
17642
|
rect({
|
|
17601
|
-
x:
|
|
17602
|
-
y:
|
|
17603
|
-
width:
|
|
17604
|
-
height:
|
|
17643
|
+
x: box2.x,
|
|
17644
|
+
y: box2.y,
|
|
17645
|
+
width: box2.width,
|
|
17646
|
+
height: box2.height,
|
|
17605
17647
|
rx: 6,
|
|
17606
17648
|
class: "sx-ft-cutset",
|
|
17607
|
-
"data-cutset":
|
|
17608
|
-
"data-cutset-index": String(
|
|
17609
|
-
"data-order": String(
|
|
17610
|
-
...
|
|
17611
|
-
...
|
|
17649
|
+
"data-cutset": box2.cutSet.events.join(","),
|
|
17650
|
+
"data-cutset-index": String(box2.index),
|
|
17651
|
+
"data-order": String(box2.cutSet.order),
|
|
17652
|
+
...box2.cutSet.isSpof ? { "data-spof": "true" } : {},
|
|
17653
|
+
...box2.cutSet.prob !== void 0 ? { "data-cutset-prob": String(box2.cutSet.prob) } : {}
|
|
17612
17654
|
})
|
|
17613
17655
|
);
|
|
17614
17656
|
}
|
|
@@ -18289,7 +18331,7 @@ function renderBowtieLayout(layout, config) {
|
|
|
18289
18331
|
];
|
|
18290
18332
|
const inner = [];
|
|
18291
18333
|
if (ast.title) {
|
|
18292
|
-
inner.push(text({ x:
|
|
18334
|
+
inner.push(text({ x: layout.width / 2, y: BOWTIE_CONST.PAGE_PAD + 6, class: "sx-bowtie-title", "font-family": fontFamily, "text-anchor": "middle" }, ast.title));
|
|
18293
18335
|
}
|
|
18294
18336
|
if (layout.hazardTie) {
|
|
18295
18337
|
inner.push(line({ x1: layout.hazardTie.x, y1: layout.hazardTie.y1, x2: layout.hazardTie.x, y2: layout.hazardTie.y2, class: "sx-bowtie-line" }));
|
|
@@ -18917,7 +18959,7 @@ function renderEventTreeLayout(layout, config) {
|
|
|
18917
18959
|
];
|
|
18918
18960
|
const inner = [];
|
|
18919
18961
|
if (ast.title) {
|
|
18920
|
-
inner.push(text({ x:
|
|
18962
|
+
inner.push(text({ x: layout.width / 2, y: EVENTTREE_CONST.CANVAS_PAD + 4, class: "sx-et-title", "font-family": fontFamily, "text-anchor": "middle" }, ast.title));
|
|
18921
18963
|
}
|
|
18922
18964
|
for (const h of layout.headers) {
|
|
18923
18965
|
inner.push(
|
|
@@ -20111,7 +20153,7 @@ function renderHeaderBlock(layout) {
|
|
|
20111
20153
|
const x0 = FMEA_CONST.CANVAS_PAD;
|
|
20112
20154
|
let y = FMEA_CONST.CANVAS_PAD + 14;
|
|
20113
20155
|
if (ast.title) {
|
|
20114
|
-
out.push(text({ x:
|
|
20156
|
+
out.push(text({ x: layout.width / 2, y, class: "sx-fmea-title", "text-anchor": "middle" }, ast.title));
|
|
20115
20157
|
y += FMEA_CONST.TITLE_H - 8;
|
|
20116
20158
|
}
|
|
20117
20159
|
const metaEntries = Object.entries(ast.metadata);
|
|
@@ -20894,7 +20936,7 @@ function renderCausalLoopLayout(layout, config) {
|
|
|
20894
20936
|
if (ast.title) {
|
|
20895
20937
|
inner.push(
|
|
20896
20938
|
text(
|
|
20897
|
-
{ x:
|
|
20939
|
+
{ x: layout.width / 2, y: 20, class: "sx-cld-title", "font-family": fontFamily, "text-anchor": "middle" },
|
|
20898
20940
|
ast.title
|
|
20899
20941
|
)
|
|
20900
20942
|
);
|
|
@@ -21693,16 +21735,16 @@ function layoutMarkov(ast) {
|
|
|
21693
21735
|
const tag = classOf?.[st.id];
|
|
21694
21736
|
const isAbsorbing = tag === "absorbing";
|
|
21695
21737
|
const pi = piMap?.[st.id] ?? perClassPi[st.id];
|
|
21696
|
-
const
|
|
21738
|
+
const box2 = {
|
|
21697
21739
|
state: st,
|
|
21698
21740
|
cx: c.x,
|
|
21699
21741
|
cy: c.y,
|
|
21700
21742
|
r: C2.STATE_R,
|
|
21701
21743
|
isAbsorbing
|
|
21702
21744
|
};
|
|
21703
|
-
if (tag)
|
|
21704
|
-
if (pi !== void 0)
|
|
21705
|
-
return
|
|
21745
|
+
if (tag) box2.classTag = tag;
|
|
21746
|
+
if (pi !== void 0) box2.pi = pi;
|
|
21747
|
+
return box2;
|
|
21706
21748
|
});
|
|
21707
21749
|
const arcKey = (a, b) => `${a}\0${b}`;
|
|
21708
21750
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -22721,7 +22763,7 @@ function renderGitGraphLayout(layout, config) {
|
|
|
22721
22763
|
const width = layout.width + pad * 2;
|
|
22722
22764
|
const height = layout.height + pad * 2;
|
|
22723
22765
|
const a11y = ast.title ?? "Git commit graph";
|
|
22724
|
-
const styleBlock =
|
|
22766
|
+
const styleBlock = buildStyle2(pal, ast.showBranches);
|
|
22725
22767
|
const children = [
|
|
22726
22768
|
title(a11y),
|
|
22727
22769
|
desc(summarise6(layout)),
|
|
@@ -22937,7 +22979,7 @@ function renderCommitLabel(id, x, y, rotate, fontFamily) {
|
|
|
22937
22979
|
id
|
|
22938
22980
|
);
|
|
22939
22981
|
}
|
|
22940
|
-
function
|
|
22982
|
+
function buildStyle2(pal, showBranches) {
|
|
22941
22983
|
const laneStyle = showBranches ? `
|
|
22942
22984
|
.sx-gg-lane { stroke-width: ${STROKE_WIDTH.thick}; stroke-linecap: round; opacity: 0.9; }
|
|
22943
22985
|
.sx-gg-pill { stroke: none; }
|
|
@@ -23559,7 +23601,8 @@ function layoutEpc(ast) {
|
|
|
23559
23601
|
const shift = (maxCrossExtent - rankWidths[r6]) / 2 + EPC_CONST.CANVAS_PAD;
|
|
23560
23602
|
for (const id of ranks[r6]) crossPos.set(id, crossPos.get(id) + shift);
|
|
23561
23603
|
}
|
|
23562
|
-
const
|
|
23604
|
+
const backMargin = backSet.size > 0 ? EPC_CONST.BACK_MARGIN : 0;
|
|
23605
|
+
const crossExtent = maxCrossExtent + EPC_CONST.CANVAS_PAD * 2 + backMargin;
|
|
23563
23606
|
const flaggedIds = /* @__PURE__ */ new Set();
|
|
23564
23607
|
for (const v of analysis.violations) {
|
|
23565
23608
|
if (v.severity === "error") for (const id of v.nodes) flaggedIds.add(id);
|
|
@@ -23840,7 +23883,7 @@ function renderEpcLayout(layout, config) {
|
|
|
23840
23883
|
];
|
|
23841
23884
|
const inner = [];
|
|
23842
23885
|
if (ast.title) {
|
|
23843
|
-
inner.push(text({ x:
|
|
23886
|
+
inner.push(text({ x: layout.width / 2, y: 22, class: "sx-epc-title", "font-family": fontFamily, "text-anchor": "middle" }, ast.title));
|
|
23844
23887
|
}
|
|
23845
23888
|
for (const e of layout.edges) inner.push(renderEdge6(e));
|
|
23846
23889
|
for (const n of layout.nodes) inner.push(renderNode3(n));
|
|
@@ -24120,13 +24163,13 @@ function parseFunction2(ast, rest, lineNo) {
|
|
|
24120
24163
|
ast.warnings.push(`Line ${lineNo}: box "${id}" redeclared \u2014 keeping first declaration.`);
|
|
24121
24164
|
return;
|
|
24122
24165
|
}
|
|
24123
|
-
const
|
|
24166
|
+
const box2 = {
|
|
24124
24167
|
id,
|
|
24125
24168
|
name,
|
|
24126
24169
|
// analysis assigns the real number; explicit wins if given.
|
|
24127
24170
|
number: explicitNumber ?? 0
|
|
24128
24171
|
};
|
|
24129
|
-
ast.boxes.push(
|
|
24172
|
+
ast.boxes.push(box2);
|
|
24130
24173
|
}
|
|
24131
24174
|
function parseRoleArrow(ast, role, rest, lineNo) {
|
|
24132
24175
|
const idM = /^([A-Za-z_]\w*)/.exec(rest);
|
|
@@ -24398,8 +24441,8 @@ function layoutIdef0(astIn) {
|
|
|
24398
24441
|
const C2 = IDEF0_CONST;
|
|
24399
24442
|
const ox = C2.MARGIN;
|
|
24400
24443
|
const oy = C2.MARGIN + C2.TITLE_H;
|
|
24401
|
-
const boxes = ast.boxes.map((
|
|
24402
|
-
box,
|
|
24444
|
+
const boxes = ast.boxes.map((box2, idx) => ({
|
|
24445
|
+
box: box2,
|
|
24403
24446
|
x: ox + idx * C2.STEP_X,
|
|
24404
24447
|
y: oy + idx * C2.STEP_Y,
|
|
24405
24448
|
width: C2.BOX_W,
|
|
@@ -24578,7 +24621,7 @@ function renderIdef0Layout(layout, config) {
|
|
|
24578
24621
|
const inner = [];
|
|
24579
24622
|
if (ast.title) {
|
|
24580
24623
|
inner.push(
|
|
24581
|
-
text({ x:
|
|
24624
|
+
text({ x: layout.width / 2, y: 24, class: "sx-idef0-title", "font-family": fontFamily, "text-anchor": "middle" }, ast.title)
|
|
24582
24625
|
);
|
|
24583
24626
|
}
|
|
24584
24627
|
for (const a of layout.arrows) {
|
|
@@ -25399,7 +25442,7 @@ function renderThreatModelLayout(layout, config) {
|
|
|
25399
25442
|
if (layout.ast.title) {
|
|
25400
25443
|
inner.push(
|
|
25401
25444
|
text(
|
|
25402
|
-
{ x:
|
|
25445
|
+
{ x: layout.width / 2, y: 24, class: "sx-tm-title", "font-family": fontFamily, "text-anchor": "middle" },
|
|
25403
25446
|
layout.ast.title
|
|
25404
25447
|
)
|
|
25405
25448
|
);
|
|
@@ -28317,7 +28360,8 @@ function layoutSipoc(ast) {
|
|
|
28317
28360
|
var QFD_CELL = 46;
|
|
28318
28361
|
var QFD_WHAT_LABEL_W = 190;
|
|
28319
28362
|
var QFD_WEIGHT_W = 46;
|
|
28320
|
-
var
|
|
28363
|
+
var QFD_HOW_FONT = 11.5;
|
|
28364
|
+
var QFD_HOW_ANGLE_SIN = Math.sin(60 * Math.PI / 180);
|
|
28321
28365
|
var QFD_FOOTER_H = 64;
|
|
28322
28366
|
var QFD_PAD = 24;
|
|
28323
28367
|
function layoutQfd(ast) {
|
|
@@ -28329,7 +28373,11 @@ function layoutQfd(ast) {
|
|
|
28329
28373
|
const roofH = Math.ceil(cellW / 2 * cols) + 8;
|
|
28330
28374
|
const whatLabelW = QFD_WHAT_LABEL_W;
|
|
28331
28375
|
const weightW = QFD_WEIGHT_W;
|
|
28332
|
-
const
|
|
28376
|
+
const maxHowW = Math.max(
|
|
28377
|
+
0,
|
|
28378
|
+
...(ast.qfd?.hows ?? []).map((h) => estimateTextWidth(h.label, QFD_HOW_FONT))
|
|
28379
|
+
);
|
|
28380
|
+
const howLabelH = Math.max(48, Math.min(220, Math.ceil(maxHowW * QFD_HOW_ANGLE_SIN) + 26));
|
|
28333
28381
|
const footerH = QFD_FOOTER_H;
|
|
28334
28382
|
const gridX0 = QFD_PAD + whatLabelW + weightW;
|
|
28335
28383
|
const gridY0 = QFD_PAD + titleH + roofH + howLabelH;
|
|
@@ -28466,76 +28514,78 @@ var HEAT_RAMP = [
|
|
|
28466
28514
|
"#ef4444",
|
|
28467
28515
|
"#b91c1c"
|
|
28468
28516
|
];
|
|
28469
|
-
|
|
28517
|
+
function buildMatrixCss(t) {
|
|
28518
|
+
return `
|
|
28470
28519
|
.sx-matrix { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
28471
|
-
.sx-matrix-title { font:
|
|
28472
|
-
.sx-matrix-grid { stroke:
|
|
28473
|
-
.sx-matrix-mid { stroke:
|
|
28474
|
-
.sx-matrix-plot-border { stroke:
|
|
28475
|
-
.sx-matrix-axis-label { font: 500 12px sans-serif; fill:
|
|
28476
|
-
.sx-matrix-axis-end { font: 500 11px sans-serif; fill:
|
|
28477
|
-
.sx-matrix-quad-annot { font: 600 13px sans-serif; fill:
|
|
28478
|
-
.sx-matrix-quad-desc { font: 400 10.5px sans-serif; fill:
|
|
28479
|
-
.sx-matrix-corr-header { font: 600 11.5px sans-serif; fill:
|
|
28480
|
-
.sx-matrix-corr-rowlabel { font: 500 11.5px sans-serif; fill:
|
|
28481
|
-
.sx-matrix-corr-margin { font: 500 11px sans-serif; fill:
|
|
28482
|
-
.sx-matrix-corr-margin-best { font: 700 11.5px sans-serif; fill:
|
|
28483
|
-
.sx-matrix-corr-grid { stroke:
|
|
28484
|
-
.sx-matrix-corr-rowbg-a { fill:
|
|
28485
|
-
.sx-matrix-corr-rowbg-b { fill:
|
|
28486
|
-
.sx-matrix-cell-label { font: 500 12px sans-serif; fill:
|
|
28487
|
-
.sx-matrix-cell-title { font: 600 13px sans-serif; fill:
|
|
28488
|
-
.sx-matrix-cell-subtitle { font: 400 11px sans-serif; fill:
|
|
28489
|
-
.sx-matrix-cell-item { font: 500 12px sans-serif; fill:
|
|
28490
|
-
.sx-matrix-cell-value { font: 600 18px sans-serif; fill:
|
|
28520
|
+
.sx-matrix-title { font: 700 16px sans-serif; fill: ${t.inkStrong}; }
|
|
28521
|
+
.sx-matrix-grid { stroke: ${t.gridFaint}; stroke-width: 1; fill: none; }
|
|
28522
|
+
.sx-matrix-mid { stroke: ${t.gridStrong}; stroke-width: 1.2; stroke-dasharray: 4 3; fill: none; }
|
|
28523
|
+
.sx-matrix-plot-border { stroke: ${t.inkMuted}; stroke-width: 1.2; fill: none; }
|
|
28524
|
+
.sx-matrix-axis-label { font: 500 12px sans-serif; fill: ${t.inkMuted}; }
|
|
28525
|
+
.sx-matrix-axis-end { font: 500 11px sans-serif; fill: ${t.inkFaint}; }
|
|
28526
|
+
.sx-matrix-quad-annot { font: 600 13px sans-serif; fill: ${t.inkMuted}; opacity: 0.75; }
|
|
28527
|
+
.sx-matrix-quad-desc { font: 400 10.5px sans-serif; fill: ${t.inkFaint}; opacity: 0.85; }
|
|
28528
|
+
.sx-matrix-corr-header { font: 600 11.5px sans-serif; fill: ${t.ink}; text-anchor: middle; }
|
|
28529
|
+
.sx-matrix-corr-rowlabel { font: 500 11.5px sans-serif; fill: ${t.ink}; text-anchor: end; dominant-baseline: central; }
|
|
28530
|
+
.sx-matrix-corr-margin { font: 500 11px sans-serif; fill: ${t.inkMuted}; text-anchor: middle; dominant-baseline: central; }
|
|
28531
|
+
.sx-matrix-corr-margin-best { font: 700 11.5px sans-serif; fill: ${t.inkStrong}; text-anchor: middle; dominant-baseline: central; }
|
|
28532
|
+
.sx-matrix-corr-grid { stroke: ${t.grid}; stroke-width: 0.8; fill: none; }
|
|
28533
|
+
.sx-matrix-corr-rowbg-a { fill: ${t.surfaceTint}; }
|
|
28534
|
+
.sx-matrix-corr-rowbg-b { fill: ${t.surface}; }
|
|
28535
|
+
.sx-matrix-cell-label { font: 500 12px sans-serif; fill: ${t.ink}; text-anchor: middle; }
|
|
28536
|
+
.sx-matrix-cell-title { font: 600 13px sans-serif; fill: ${t.inkStrong}; }
|
|
28537
|
+
.sx-matrix-cell-subtitle { font: 400 11px sans-serif; fill: ${t.inkFaint}; }
|
|
28538
|
+
.sx-matrix-cell-item { font: 500 12px sans-serif; fill: ${t.ink}; }
|
|
28539
|
+
.sx-matrix-cell-value { font: 600 18px sans-serif; fill: ${t.inkStrong}; text-anchor: middle; }
|
|
28491
28540
|
.sx-matrix-bubble { stroke-width: 1.5; }
|
|
28492
|
-
.sx-matrix-label { font: 500 11px sans-serif; fill:
|
|
28493
|
-
.sx-matrix-leader { stroke:
|
|
28494
|
-
.sx-matrix-legend-text { font: 500 11px sans-serif; fill:
|
|
28495
|
-
.sx-matrix-offchart { fill:
|
|
28496
|
-
.sx-sipoc-header { font: 700 13px sans-serif; fill:
|
|
28497
|
-
.sx-sipoc-headbox { stroke:
|
|
28498
|
-
.sx-sipoc-cell { fill:
|
|
28499
|
-
.sx-sipoc-cell-alt { fill:
|
|
28500
|
-
.sx-sipoc-process { fill:
|
|
28501
|
-
.sx-sipoc-item { font: 500 12px sans-serif; fill:
|
|
28502
|
-
.sx-sipoc-step { font: 600 12px sans-serif; fill:
|
|
28503
|
-
.sx-qfd-grid { stroke:
|
|
28504
|
-
.sx-qfd-cellbg { fill:
|
|
28505
|
-
.sx-qfd-cellbg-alt { fill:
|
|
28506
|
-
.sx-qfd-what { font: 500 12px sans-serif; fill:
|
|
28507
|
-
.sx-qfd-how { font: 500 11.5px sans-serif; fill:
|
|
28508
|
-
.sx-qfd-weight { font: 600 12px sans-serif; fill:
|
|
28509
|
-
.sx-qfd-weight-head { font: 600 10px sans-serif; fill:
|
|
28510
|
-
.sx-qfd-rel-strong { fill:
|
|
28511
|
-
.sx-qfd-rel-medium { fill:
|
|
28512
|
-
.sx-qfd-rel-weak { fill: none; stroke:
|
|
28513
|
-
.sx-qfd-roof-cell { fill:
|
|
28514
|
-
.sx-qfd-roof-cell-filled { fill:
|
|
28541
|
+
.sx-matrix-label { font: 500 11px sans-serif; fill: ${t.inkStrong}; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
|
|
28542
|
+
.sx-matrix-leader { stroke: ${t.gridStrong}; stroke-width: 0.6; opacity: 0.7; fill: none; }
|
|
28543
|
+
.sx-matrix-legend-text { font: 500 11px sans-serif; fill: ${t.inkMuted}; }
|
|
28544
|
+
.sx-matrix-offchart { fill: ${t.warnDeep}; }
|
|
28545
|
+
.sx-sipoc-header { font: 700 13px sans-serif; fill: ${t.onHeader}; text-anchor: middle; dominant-baseline: central; }
|
|
28546
|
+
.sx-sipoc-headbox { stroke: ${t.surface}; stroke-width: 1; }
|
|
28547
|
+
.sx-sipoc-cell { fill: ${t.surface}; stroke: ${t.gridMid}; stroke-width: 1; }
|
|
28548
|
+
.sx-sipoc-cell-alt { fill: ${t.surfaceAlt}; stroke: ${t.gridMid}; stroke-width: 1; }
|
|
28549
|
+
.sx-sipoc-process { fill: ${t.accentTint}; stroke: ${t.gridMid}; stroke-width: 1; }
|
|
28550
|
+
.sx-sipoc-item { font: 500 12px sans-serif; fill: ${t.ink}; text-anchor: middle; dominant-baseline: central; }
|
|
28551
|
+
.sx-sipoc-step { font: 600 12px sans-serif; fill: ${t.accentDeep}; text-anchor: middle; dominant-baseline: central; }
|
|
28552
|
+
.sx-qfd-grid { stroke: ${t.gridMid}; stroke-width: 1; fill: none; }
|
|
28553
|
+
.sx-qfd-cellbg { fill: ${t.surface}; }
|
|
28554
|
+
.sx-qfd-cellbg-alt { fill: ${t.surfaceAlt}; }
|
|
28555
|
+
.sx-qfd-what { font: 500 12px sans-serif; fill: ${t.ink}; text-anchor: end; dominant-baseline: central; }
|
|
28556
|
+
.sx-qfd-how { font: 500 11.5px sans-serif; fill: ${t.ink}; }
|
|
28557
|
+
.sx-qfd-weight { font: 600 12px sans-serif; fill: ${t.inkStrong}; text-anchor: middle; dominant-baseline: central; }
|
|
28558
|
+
.sx-qfd-weight-head { font: 600 10px sans-serif; fill: ${t.inkMuted}; text-anchor: middle; dominant-baseline: central; }
|
|
28559
|
+
.sx-qfd-rel-strong { fill: ${t.accent}; }
|
|
28560
|
+
.sx-qfd-rel-medium { fill: ${t.accentSoft}; stroke: ${t.accent}; stroke-width: 1.4; }
|
|
28561
|
+
.sx-qfd-rel-weak { fill: none; stroke: ${t.accent}; stroke-width: 1.4; }
|
|
28562
|
+
.sx-qfd-roof-cell { fill: ${t.surfaceAlt}; stroke: ${t.gridStrong}; stroke-width: 0.8; }
|
|
28563
|
+
.sx-qfd-roof-cell-filled { fill: ${t.roofFilled}; stroke: ${t.inkFaint}; stroke-width: 0.9; }
|
|
28515
28564
|
.sx-qfd-corr { font: 700 13px sans-serif; text-anchor: middle; dominant-baseline: central; }
|
|
28516
|
-
.sx-qfd-corr-strong-pos { fill:
|
|
28517
|
-
.sx-qfd-corr-pos { fill:
|
|
28518
|
-
.sx-qfd-corr-neg { fill:
|
|
28519
|
-
.sx-qfd-corr-strong-neg { fill:
|
|
28520
|
-
.sx-qfd-imp-band { fill:
|
|
28521
|
-
.sx-qfd-imp-head { font: 600 11px sans-serif; fill:
|
|
28522
|
-
.sx-qfd-imp-value { font: 700 13px sans-serif; fill:
|
|
28523
|
-
.sx-qfd-imp-value-top { font: 800 14px sans-serif; fill:
|
|
28524
|
-
.sx-qfd-dir { font: 700 13px sans-serif; fill:
|
|
28525
|
-
.sx-punnett-corner { fill:
|
|
28526
|
-
.sx-punnett-cornerline { stroke:
|
|
28527
|
-
.sx-punnett-corner-p1 { font: 600 11px sans-serif; fill:
|
|
28528
|
-
.sx-punnett-corner-p2 { font: 600 11px sans-serif; fill:
|
|
28529
|
-
.sx-punnett-header { fill:
|
|
28530
|
-
.sx-punnett-gamete { font: 700 14px ui-monospace, "SF Mono", Menlo, monospace; fill:
|
|
28531
|
-
.sx-punnett-cell { stroke:
|
|
28532
|
-
.sx-punnett-genotype { font: 700 15px ui-monospace, "SF Mono", Menlo, monospace; fill:
|
|
28533
|
-
.sx-punnett-footer-head { font: 700 13px sans-serif; fill:
|
|
28534
|
-
.sx-punnett-legend { font: 500 12.5px sans-serif; fill:
|
|
28535
|
-
.sx-punnett-geno-ratio { font: 500 12px sans-serif; fill:
|
|
28536
|
-
.sx-punnett-hint { font: 500 13px sans-serif; fill:
|
|
28565
|
+
.sx-qfd-corr-strong-pos { fill: ${t.positiveDeep}; }
|
|
28566
|
+
.sx-qfd-corr-pos { fill: ${t.positive}; }
|
|
28567
|
+
.sx-qfd-corr-neg { fill: ${t.negative}; }
|
|
28568
|
+
.sx-qfd-corr-strong-neg { fill: ${t.negativeDeep}; }
|
|
28569
|
+
.sx-qfd-imp-band { fill: ${t.accentTint}; stroke: ${t.gridMid}; stroke-width: 1; }
|
|
28570
|
+
.sx-qfd-imp-head { font: 600 11px sans-serif; fill: ${t.accentDeep}; text-anchor: end; dominant-baseline: central; }
|
|
28571
|
+
.sx-qfd-imp-value { font: 700 13px sans-serif; fill: ${t.accentDeep}; text-anchor: middle; dominant-baseline: central; }
|
|
28572
|
+
.sx-qfd-imp-value-top { font: 800 14px sans-serif; fill: ${t.negative}; text-anchor: middle; dominant-baseline: central; }
|
|
28573
|
+
.sx-qfd-dir { font: 700 13px sans-serif; fill: ${t.inkMuted}; text-anchor: middle; dominant-baseline: central; }
|
|
28574
|
+
.sx-punnett-corner { fill: ${t.cornerFill}; stroke: ${t.gridStrong}; stroke-width: 1; }
|
|
28575
|
+
.sx-punnett-cornerline { stroke: ${t.gridMid}; stroke-width: 1; }
|
|
28576
|
+
.sx-punnett-corner-p1 { font: 600 11px sans-serif; fill: ${t.accentDeep}; dominant-baseline: hanging; }
|
|
28577
|
+
.sx-punnett-corner-p2 { font: 600 11px sans-serif; fill: ${t.accentDeep}; }
|
|
28578
|
+
.sx-punnett-header { fill: ${t.headerFill}; stroke: ${t.gridStrong}; stroke-width: 1; }
|
|
28579
|
+
.sx-punnett-gamete { font: 700 14px ui-monospace, "SF Mono", Menlo, monospace; fill: ${t.inkStrong}; text-anchor: middle; dominant-baseline: central; }
|
|
28580
|
+
.sx-punnett-cell { stroke: ${t.gridStrong}; stroke-width: 1; }
|
|
28581
|
+
.sx-punnett-genotype { font: 700 15px ui-monospace, "SF Mono", Menlo, monospace; fill: ${t.inkStrong}; text-anchor: middle; dominant-baseline: central; }
|
|
28582
|
+
.sx-punnett-footer-head { font: 700 13px sans-serif; fill: ${t.inkStrong}; }
|
|
28583
|
+
.sx-punnett-legend { font: 500 12.5px sans-serif; fill: ${t.ink}; dominant-baseline: central; }
|
|
28584
|
+
.sx-punnett-geno-ratio { font: 500 12px sans-serif; fill: ${t.inkMuted}; }
|
|
28585
|
+
.sx-punnett-hint { font: 500 13px sans-serif; fill: ${t.inkFaint}; text-anchor: middle; }
|
|
28537
28586
|
`.trim();
|
|
28538
|
-
|
|
28587
|
+
}
|
|
28588
|
+
function axisArrow(t) {
|
|
28539
28589
|
return el(
|
|
28540
28590
|
"marker",
|
|
28541
28591
|
{
|
|
@@ -28547,7 +28597,7 @@ function axisArrow() {
|
|
|
28547
28597
|
markerHeight: 8,
|
|
28548
28598
|
orient: "auto-start-reverse"
|
|
28549
28599
|
},
|
|
28550
|
-
[el("path", { d: "M0,0 L10,5 L0,10 z", fill:
|
|
28600
|
+
[el("path", { d: "M0,0 L10,5 L0,10 z", fill: t.border })]
|
|
28551
28601
|
);
|
|
28552
28602
|
}
|
|
28553
28603
|
function bubbleFill(p, categories) {
|
|
@@ -29334,7 +29384,8 @@ function wrapToLines(textStr, maxChars, maxLines) {
|
|
|
29334
29384
|
kept[maxLines - 1] = kept[maxLines - 1].replace(/\s+\S*$/, "") + "\u2026";
|
|
29335
29385
|
return kept;
|
|
29336
29386
|
}
|
|
29337
|
-
function renderSipocAST(ast) {
|
|
29387
|
+
function renderSipocAST(ast, config) {
|
|
29388
|
+
const t = resolveMatrixTheme(config?.theme ?? "default");
|
|
29338
29389
|
const sipoc = ast.sipoc ?? { suppliers: [], inputs: [], process: [], outputs: [], customers: [] };
|
|
29339
29390
|
const lay = layoutSipoc(ast);
|
|
29340
29391
|
const nodes = [];
|
|
@@ -29416,7 +29467,7 @@ function renderSipocAST(ast) {
|
|
|
29416
29467
|
desc(
|
|
29417
29468
|
`SIPOC scoping table \u2014 ${sipoc.suppliers.length} supplier(s), ${sipoc.inputs.length} input(s), ${sipoc.process.length} process step(s), ${sipoc.outputs.length} output(s), ${sipoc.customers.length} customer(s)`
|
|
29418
29469
|
),
|
|
29419
|
-
defs([el("style", {},
|
|
29470
|
+
defs([el("style", {}, buildMatrixCss(t))]),
|
|
29420
29471
|
...nodes
|
|
29421
29472
|
]
|
|
29422
29473
|
);
|
|
@@ -29455,7 +29506,8 @@ function renderQfdRelationshipSymbol(strength, cx, cy, r6) {
|
|
|
29455
29506
|
class: "sx-qfd-rel-weak"
|
|
29456
29507
|
});
|
|
29457
29508
|
}
|
|
29458
|
-
function renderQfdAST(ast) {
|
|
29509
|
+
function renderQfdAST(ast, config) {
|
|
29510
|
+
const t = resolveMatrixTheme(config?.theme ?? "default");
|
|
29459
29511
|
const qfd = ast.qfd ?? { whats: [], hows: [], relationships: [], roof: [], normalize: false };
|
|
29460
29512
|
const lay = layoutQfd(ast);
|
|
29461
29513
|
const importance = computeQfdImportance(qfd);
|
|
@@ -29645,7 +29697,7 @@ function renderQfdAST(ast) {
|
|
|
29645
29697
|
desc(
|
|
29646
29698
|
`QFD House of Quality \u2014 ${qfd.whats.length} customer requirement(s), ${qfd.hows.length} engineering characteristic(s), ${qfd.relationships.length} relationship(s); technical importance computed per column`
|
|
29647
29699
|
),
|
|
29648
|
-
defs([el("style", {},
|
|
29700
|
+
defs([el("style", {}, buildMatrixCss(t))]),
|
|
29649
29701
|
...nodes
|
|
29650
29702
|
]
|
|
29651
29703
|
);
|
|
@@ -29659,7 +29711,8 @@ function genotypeText(parent, genes) {
|
|
|
29659
29711
|
return a === dom ? a + b : b === dom ? b + a : a + b;
|
|
29660
29712
|
}).join("");
|
|
29661
29713
|
}
|
|
29662
|
-
function renderPunnettAST(ast) {
|
|
29714
|
+
function renderPunnettAST(ast, config) {
|
|
29715
|
+
const t = resolveMatrixTheme(config?.theme ?? "default");
|
|
29663
29716
|
const pd = ast.punnett;
|
|
29664
29717
|
const lay = layoutPunnett(ast);
|
|
29665
29718
|
const svgWrap = (body, descText2) => svgRoot(
|
|
@@ -29675,7 +29728,7 @@ function renderPunnettAST(ast) {
|
|
|
29675
29728
|
[
|
|
29676
29729
|
title(ast.title ? `Punnett square \u2014 ${escapeXml(ast.title)}` : "Punnett square"),
|
|
29677
29730
|
desc(descText2),
|
|
29678
|
-
defs([el("style", {},
|
|
29731
|
+
defs([el("style", {}, buildMatrixCss(t))]),
|
|
29679
29732
|
...body
|
|
29680
29733
|
]
|
|
29681
29734
|
);
|
|
@@ -29770,10 +29823,11 @@ function renderPunnettAST(ast) {
|
|
|
29770
29823
|
const descText = `Punnett square \u2014 ${pd.genes.length === 1 ? "monohybrid" : pd.genes.length === 2 ? "dihybrid" : `${pd.genes.length}-gene`} cross ${genotypeText(pd.parent1, pd.genes)} \xD7 ${genotypeText(pd.parent2, pd.genes)}; phenotype ratio ${footer.phenotypeRatio}`;
|
|
29771
29824
|
return svgWrap(nodes, descText);
|
|
29772
29825
|
}
|
|
29773
|
-
function renderMatrixAST(ast) {
|
|
29774
|
-
if (ast.mode === "sipoc") return renderSipocAST(ast);
|
|
29775
|
-
if (ast.mode === "qfd") return renderQfdAST(ast);
|
|
29776
|
-
if (ast.mode === "punnett") return renderPunnettAST(ast);
|
|
29826
|
+
function renderMatrixAST(ast, config) {
|
|
29827
|
+
if (ast.mode === "sipoc") return renderSipocAST(ast, config);
|
|
29828
|
+
if (ast.mode === "qfd") return renderQfdAST(ast, config);
|
|
29829
|
+
if (ast.mode === "punnett") return renderPunnettAST(ast, config);
|
|
29830
|
+
const t = resolveMatrixTheme(config?.theme ?? "default");
|
|
29777
29831
|
const lay = layoutMatrix(ast);
|
|
29778
29832
|
const needsLegendSpace = lay.categories.length > 0 || ast.mode === "correlation";
|
|
29779
29833
|
const extraWidth = needsLegendSpace && lay.plot.x0 + lay.plot.w + 140 > lay.canvasWidth ? 160 : 0;
|
|
@@ -29807,14 +29861,14 @@ function renderMatrixAST(ast) {
|
|
|
29807
29861
|
desc(
|
|
29808
29862
|
`Matrix diagram${ast.template ? ` (${ast.template} template)` : ""}, ${ast.mode} mode, ${ast.points.length} point(s)`
|
|
29809
29863
|
),
|
|
29810
|
-
defs([el("style", {},
|
|
29864
|
+
defs([el("style", {}, buildMatrixCss(t)), axisArrow(t)]),
|
|
29811
29865
|
...body
|
|
29812
29866
|
]
|
|
29813
29867
|
);
|
|
29814
29868
|
}
|
|
29815
|
-
function renderMatrix(text2) {
|
|
29869
|
+
function renderMatrix(text2, config) {
|
|
29816
29870
|
const ast = parseMatrix(text2);
|
|
29817
|
-
return renderMatrixAST(ast);
|
|
29871
|
+
return renderMatrixAST(ast, config);
|
|
29818
29872
|
}
|
|
29819
29873
|
|
|
29820
29874
|
// src/diagrams/matrix/index.ts
|
|
@@ -29825,8 +29879,8 @@ var matrix = {
|
|
|
29825
29879
|
return first.startsWith("matrix");
|
|
29826
29880
|
},
|
|
29827
29881
|
parse: parseMatrix,
|
|
29828
|
-
render(text2) {
|
|
29829
|
-
return renderMatrix(text2);
|
|
29882
|
+
render(text2, config) {
|
|
29883
|
+
return renderMatrix(text2, config);
|
|
29830
29884
|
}
|
|
29831
29885
|
};
|
|
29832
29886
|
|
|
@@ -32026,7 +32080,7 @@ var WIRE_COLOR_MAP = {
|
|
|
32026
32080
|
function buildCss12(t) {
|
|
32027
32081
|
return `
|
|
32028
32082
|
.lt-bb { font-family: system-ui, -apple-system, sans-serif; }
|
|
32029
|
-
.lt-bb-title { font:
|
|
32083
|
+
.lt-bb-title { font: 700 16px sans-serif; fill: ${t.text}; }
|
|
32030
32084
|
.lt-bb-substrate { fill: #e7d8b6; stroke: #b08c4f; stroke-width: 1.5; }
|
|
32031
32085
|
.lt-bb-rail-pos { fill: #fde2e2; stroke: #dc2626; stroke-width: 0.6; }
|
|
32032
32086
|
.lt-bb-rail-neg { fill: #dde7fa; stroke: #2563eb; stroke-width: 0.6; }
|
|
@@ -32173,7 +32227,7 @@ function renderBreadboardLayout(layout, config) {
|
|
|
32173
32227
|
const theme = resolveBaseTheme(config?.theme ?? "default");
|
|
32174
32228
|
const css = buildCss12(theme);
|
|
32175
32229
|
const titleStr = layout.ast.title ?? "Breadboard";
|
|
32176
|
-
const titleNode = layout.ast.title ? text({ x:
|
|
32230
|
+
const titleNode = layout.ast.title ? text({ x: layout.width / 2, y: 22, class: "lt-bb-title", "text-anchor": "middle" }, layout.ast.title) : "";
|
|
32177
32231
|
const substrate = renderSubstrate(layout.substrate);
|
|
32178
32232
|
const parts = layout.parts.map(renderPart).join("\n");
|
|
32179
32233
|
const wires = layout.wires.map(renderWire).join("\n");
|
|
@@ -33036,26 +33090,19 @@ function routeMessageFlow(f, objCenter, poolByLabel) {
|
|
|
33036
33090
|
|
|
33037
33091
|
// src/diagrams/bpmn/renderer.ts
|
|
33038
33092
|
var FONT_FAMILY = "system-ui, -apple-system, 'Segoe UI', sans-serif";
|
|
33039
|
-
var STROKE = "#1f2937";
|
|
33040
|
-
var STROKE_LIGHT = "#94a3b8";
|
|
33041
|
-
var POOL_FILL = "#ffffff";
|
|
33042
|
-
var LANE_FILL = "#fafbfc";
|
|
33043
|
-
var TASK_FILL = "#ffffff";
|
|
33044
|
-
var GATEWAY_FILL = "#fffbeb";
|
|
33045
|
-
var EVENT_FILL = "#ffffff";
|
|
33046
|
-
var LABEL_BAND_FILL = "#f1f5f9";
|
|
33047
33093
|
function renderBpmn(textInput, config) {
|
|
33048
33094
|
const ast = parseBpmn(textInput);
|
|
33049
33095
|
const layout = layoutBpmn(ast);
|
|
33050
|
-
return renderBpmnLayout(layout);
|
|
33096
|
+
return renderBpmnLayout(layout, config);
|
|
33051
33097
|
}
|
|
33052
|
-
function renderBpmnLayout(layout,
|
|
33098
|
+
function renderBpmnLayout(layout, config) {
|
|
33053
33099
|
const { width, height, ast } = layout;
|
|
33100
|
+
const t = resolveBpmnTheme(config?.theme ?? "default");
|
|
33054
33101
|
const out = [];
|
|
33055
33102
|
out.push(title(ast.title ?? "BPMN diagram"));
|
|
33056
33103
|
out.push(desc(`BPMN ${ast.direction} \u2014 ${ast.pools.length} pool(s), ${layout.objects.length} flow object(s).`));
|
|
33057
|
-
out.push(buildDefs());
|
|
33058
|
-
for (const pl2 of layout.pools) out.push(renderPool(pl2));
|
|
33104
|
+
out.push(buildDefs(t));
|
|
33105
|
+
for (const pl2 of layout.pools) out.push(renderPool(pl2, t));
|
|
33059
33106
|
for (const lan of layout.lanes) {
|
|
33060
33107
|
out.push(
|
|
33061
33108
|
group({ class: "schematex-bpmn-lane" }, [
|
|
@@ -33064,8 +33111,8 @@ function renderBpmnLayout(layout, _config) {
|
|
|
33064
33111
|
y: lan.y,
|
|
33065
33112
|
width: lan.width - lan.labelHeight,
|
|
33066
33113
|
height: lan.height,
|
|
33067
|
-
fill:
|
|
33068
|
-
stroke:
|
|
33114
|
+
fill: t.laneFill,
|
|
33115
|
+
stroke: t.bpmnStroke,
|
|
33069
33116
|
"stroke-width": 1
|
|
33070
33117
|
}),
|
|
33071
33118
|
rect({
|
|
@@ -33073,8 +33120,8 @@ function renderBpmnLayout(layout, _config) {
|
|
|
33073
33120
|
y: lan.y,
|
|
33074
33121
|
width: lan.labelHeight,
|
|
33075
33122
|
height: lan.height,
|
|
33076
|
-
fill:
|
|
33077
|
-
stroke:
|
|
33123
|
+
fill: t.labelBandFill,
|
|
33124
|
+
stroke: t.bpmnStroke,
|
|
33078
33125
|
"stroke-width": 1
|
|
33079
33126
|
}),
|
|
33080
33127
|
text(
|
|
@@ -33086,15 +33133,15 @@ function renderBpmnLayout(layout, _config) {
|
|
|
33086
33133
|
"dominant-baseline": "middle",
|
|
33087
33134
|
"font-family": FONT_FAMILY,
|
|
33088
33135
|
"font-size": 12,
|
|
33089
|
-
fill:
|
|
33136
|
+
fill: t.bpmnText
|
|
33090
33137
|
},
|
|
33091
33138
|
lan.lane.label
|
|
33092
33139
|
)
|
|
33093
33140
|
])
|
|
33094
33141
|
);
|
|
33095
33142
|
}
|
|
33096
|
-
for (const fl of layout.flows) out.push(renderFlow2(fl));
|
|
33097
|
-
for (const ol of layout.objects) out.push(renderObject(ol));
|
|
33143
|
+
for (const fl of layout.flows) out.push(renderFlow2(fl, t));
|
|
33144
|
+
for (const ol of layout.objects) out.push(renderObject(ol, t));
|
|
33098
33145
|
return svgRoot(
|
|
33099
33146
|
{
|
|
33100
33147
|
width,
|
|
@@ -33105,7 +33152,7 @@ function renderBpmnLayout(layout, _config) {
|
|
|
33105
33152
|
out
|
|
33106
33153
|
);
|
|
33107
33154
|
}
|
|
33108
|
-
function buildDefs() {
|
|
33155
|
+
function buildDefs(t) {
|
|
33109
33156
|
return defs([
|
|
33110
33157
|
// Sequence flow — filled triangle.
|
|
33111
33158
|
el(
|
|
@@ -33122,7 +33169,7 @@ function buildDefs() {
|
|
|
33122
33169
|
[
|
|
33123
33170
|
el("path", {
|
|
33124
33171
|
d: "M 0 0 L 10 5 L 0 10 z",
|
|
33125
|
-
fill:
|
|
33172
|
+
fill: t.flowStroke
|
|
33126
33173
|
})
|
|
33127
33174
|
]
|
|
33128
33175
|
),
|
|
@@ -33141,8 +33188,8 @@ function buildDefs() {
|
|
|
33141
33188
|
[
|
|
33142
33189
|
el("path", {
|
|
33143
33190
|
d: "M 0 0 L 10 5 L 0 10 z",
|
|
33144
|
-
fill:
|
|
33145
|
-
stroke:
|
|
33191
|
+
fill: t.poolFill,
|
|
33192
|
+
stroke: t.msgFlowStroke,
|
|
33146
33193
|
"stroke-width": 1
|
|
33147
33194
|
})
|
|
33148
33195
|
]
|
|
@@ -33160,12 +33207,12 @@ function buildDefs() {
|
|
|
33160
33207
|
orient: "auto"
|
|
33161
33208
|
},
|
|
33162
33209
|
[
|
|
33163
|
-
el("circle", { cx: 5, cy: 5, r: 3, fill:
|
|
33210
|
+
el("circle", { cx: 5, cy: 5, r: 3, fill: t.poolFill, stroke: t.msgFlowStroke })
|
|
33164
33211
|
]
|
|
33165
33212
|
)
|
|
33166
33213
|
]);
|
|
33167
33214
|
}
|
|
33168
|
-
function renderPool(pl2) {
|
|
33215
|
+
function renderPool(pl2, t) {
|
|
33169
33216
|
const labelCx = pl2.labelX + pl2.labelWidth / 2;
|
|
33170
33217
|
const labelCy = pl2.labelY + pl2.height / 2;
|
|
33171
33218
|
if (pl2.pool.blackbox) {
|
|
@@ -33175,8 +33222,8 @@ function renderPool(pl2) {
|
|
|
33175
33222
|
y: pl2.y,
|
|
33176
33223
|
width: pl2.width,
|
|
33177
33224
|
height: pl2.height,
|
|
33178
|
-
fill:
|
|
33179
|
-
stroke:
|
|
33225
|
+
fill: t.poolFill,
|
|
33226
|
+
stroke: t.bpmnStroke,
|
|
33180
33227
|
"stroke-width": 1.5
|
|
33181
33228
|
}),
|
|
33182
33229
|
rect({
|
|
@@ -33184,8 +33231,8 @@ function renderPool(pl2) {
|
|
|
33184
33231
|
y: pl2.y,
|
|
33185
33232
|
width: pl2.labelWidth,
|
|
33186
33233
|
height: pl2.height,
|
|
33187
|
-
fill:
|
|
33188
|
-
stroke:
|
|
33234
|
+
fill: t.labelBandFill,
|
|
33235
|
+
stroke: t.bpmnStroke,
|
|
33189
33236
|
"stroke-width": 1
|
|
33190
33237
|
}),
|
|
33191
33238
|
text(
|
|
@@ -33198,7 +33245,7 @@ function renderPool(pl2) {
|
|
|
33198
33245
|
"font-family": FONT_FAMILY,
|
|
33199
33246
|
"font-size": 13,
|
|
33200
33247
|
"font-weight": "bold",
|
|
33201
|
-
fill:
|
|
33248
|
+
fill: t.bpmnText
|
|
33202
33249
|
},
|
|
33203
33250
|
pl2.pool.label
|
|
33204
33251
|
)
|
|
@@ -33210,8 +33257,8 @@ function renderPool(pl2) {
|
|
|
33210
33257
|
y: pl2.y,
|
|
33211
33258
|
width: pl2.width,
|
|
33212
33259
|
height: pl2.height,
|
|
33213
|
-
fill:
|
|
33214
|
-
stroke:
|
|
33260
|
+
fill: t.poolFill,
|
|
33261
|
+
stroke: t.bpmnStroke,
|
|
33215
33262
|
"stroke-width": 1.5
|
|
33216
33263
|
}),
|
|
33217
33264
|
rect({
|
|
@@ -33219,8 +33266,8 @@ function renderPool(pl2) {
|
|
|
33219
33266
|
y: pl2.y,
|
|
33220
33267
|
width: pl2.labelWidth,
|
|
33221
33268
|
height: pl2.height,
|
|
33222
|
-
fill:
|
|
33223
|
-
stroke:
|
|
33269
|
+
fill: t.labelBandFill,
|
|
33270
|
+
stroke: t.bpmnStroke,
|
|
33224
33271
|
"stroke-width": 1
|
|
33225
33272
|
}),
|
|
33226
33273
|
text(
|
|
@@ -33233,19 +33280,19 @@ function renderPool(pl2) {
|
|
|
33233
33280
|
"font-family": FONT_FAMILY,
|
|
33234
33281
|
"font-size": 13,
|
|
33235
33282
|
"font-weight": "bold",
|
|
33236
|
-
fill:
|
|
33283
|
+
fill: t.bpmnText
|
|
33237
33284
|
},
|
|
33238
33285
|
pl2.pool.label
|
|
33239
33286
|
)
|
|
33240
33287
|
]);
|
|
33241
33288
|
}
|
|
33242
|
-
function renderObject(ol) {
|
|
33289
|
+
function renderObject(ol, t) {
|
|
33243
33290
|
const o = ol.obj;
|
|
33244
|
-
if ("gatewayKind" in o) return renderGateway(ol);
|
|
33245
|
-
if ("marker" in o) return renderActivity(ol);
|
|
33246
|
-
return renderEvent2(ol);
|
|
33291
|
+
if ("gatewayKind" in o) return renderGateway(ol, t);
|
|
33292
|
+
if ("marker" in o) return renderActivity(ol, t);
|
|
33293
|
+
return renderEvent2(ol, t);
|
|
33247
33294
|
}
|
|
33248
|
-
function renderActivity(ol) {
|
|
33295
|
+
function renderActivity(ol, t) {
|
|
33249
33296
|
const a = ol.obj;
|
|
33250
33297
|
const isSubproc = a.kind === "subprocess-collapsed";
|
|
33251
33298
|
const cx = ol.x + ol.width / 2;
|
|
@@ -33258,8 +33305,8 @@ function renderActivity(ol) {
|
|
|
33258
33305
|
height: ol.height,
|
|
33259
33306
|
rx: 10,
|
|
33260
33307
|
ry: 10,
|
|
33261
|
-
fill:
|
|
33262
|
-
stroke:
|
|
33308
|
+
fill: t.taskFill,
|
|
33309
|
+
stroke: t.taskStroke,
|
|
33263
33310
|
"stroke-width": 1.5
|
|
33264
33311
|
}),
|
|
33265
33312
|
multilineText(
|
|
@@ -33270,14 +33317,14 @@ function renderActivity(ol) {
|
|
|
33270
33317
|
"dominant-baseline": "middle",
|
|
33271
33318
|
"font-family": FONT_FAMILY,
|
|
33272
33319
|
"font-size": 12,
|
|
33273
|
-
fill:
|
|
33320
|
+
fill: t.bpmnText
|
|
33274
33321
|
},
|
|
33275
33322
|
a.label,
|
|
33276
33323
|
14
|
|
33277
33324
|
)
|
|
33278
33325
|
];
|
|
33279
33326
|
if (a.kind === "task" && a.marker !== "abstract") {
|
|
33280
|
-
children.push(taskMarker(ol.x + 6, ol.y + 6, a.marker));
|
|
33327
|
+
children.push(taskMarker(ol.x + 6, ol.y + 6, a.marker, t));
|
|
33281
33328
|
}
|
|
33282
33329
|
if (isSubproc) {
|
|
33283
33330
|
const mx = cx;
|
|
@@ -33289,53 +33336,53 @@ function renderActivity(ol) {
|
|
|
33289
33336
|
width: 12,
|
|
33290
33337
|
height: 12,
|
|
33291
33338
|
fill: "none",
|
|
33292
|
-
stroke:
|
|
33339
|
+
stroke: t.bpmnStroke,
|
|
33293
33340
|
"stroke-width": 1
|
|
33294
33341
|
}),
|
|
33295
33342
|
el("path", {
|
|
33296
33343
|
d: `M ${mx - 4} ${my} L ${mx + 4} ${my} M ${mx} ${my - 4} L ${mx} ${my + 4}`,
|
|
33297
|
-
stroke:
|
|
33344
|
+
stroke: t.bpmnStroke,
|
|
33298
33345
|
"stroke-width": 1
|
|
33299
33346
|
})
|
|
33300
33347
|
);
|
|
33301
33348
|
}
|
|
33302
33349
|
return group({ class: `schematex-bpmn-task marker-${a.marker}` }, children);
|
|
33303
33350
|
}
|
|
33304
|
-
function taskMarker(x, y, marker) {
|
|
33351
|
+
function taskMarker(x, y, marker, t) {
|
|
33305
33352
|
const cx = x + 7;
|
|
33306
33353
|
const cy = y + 7;
|
|
33307
33354
|
if (marker === "user") {
|
|
33308
33355
|
return el("g", { class: "marker-user" }, [
|
|
33309
|
-
el("circle", { cx, cy: cy - 1, r: 2.5, fill: "none", stroke:
|
|
33356
|
+
el("circle", { cx, cy: cy - 1, r: 2.5, fill: "none", stroke: t.bpmnStroke, "stroke-width": 1 }),
|
|
33310
33357
|
el("path", {
|
|
33311
33358
|
d: `M ${cx - 5} ${cy + 6} Q ${cx} ${cy + 1} ${cx + 5} ${cy + 6}`,
|
|
33312
33359
|
fill: "none",
|
|
33313
|
-
stroke:
|
|
33360
|
+
stroke: t.bpmnStroke,
|
|
33314
33361
|
"stroke-width": 1
|
|
33315
33362
|
})
|
|
33316
33363
|
]);
|
|
33317
33364
|
}
|
|
33318
33365
|
if (marker === "service") {
|
|
33319
33366
|
return el("g", { class: "marker-service" }, [
|
|
33320
|
-
el("circle", { cx, cy, r: 4, fill: "none", stroke:
|
|
33321
|
-
el("circle", { cx, cy, r: 1.5, fill:
|
|
33367
|
+
el("circle", { cx, cy, r: 4, fill: "none", stroke: t.bpmnStroke, "stroke-width": 1 }),
|
|
33368
|
+
el("circle", { cx, cy, r: 1.5, fill: t.bpmnStroke }),
|
|
33322
33369
|
el("path", {
|
|
33323
33370
|
d: `M ${cx} ${cy - 6} L ${cx} ${cy - 4} M ${cx} ${cy + 4} L ${cx} ${cy + 6} M ${cx - 6} ${cy} L ${cx - 4} ${cy} M ${cx + 4} ${cy} L ${cx + 6} ${cy}`,
|
|
33324
|
-
stroke:
|
|
33371
|
+
stroke: t.bpmnStroke,
|
|
33325
33372
|
"stroke-width": 1
|
|
33326
33373
|
})
|
|
33327
33374
|
]);
|
|
33328
33375
|
}
|
|
33329
33376
|
if (marker === "send") {
|
|
33330
33377
|
return el("g", { class: "marker-send" }, [
|
|
33331
|
-
el("rect", { x: cx - 5, y: cy - 3, width: 10, height: 7, fill:
|
|
33332
|
-
el("path", { d: `M ${cx - 5} ${cy - 3} L ${cx} ${cy + 1} L ${cx + 5} ${cy - 3}`, stroke:
|
|
33378
|
+
el("rect", { x: cx - 5, y: cy - 3, width: 10, height: 7, fill: t.bpmnStroke }),
|
|
33379
|
+
el("path", { d: `M ${cx - 5} ${cy - 3} L ${cx} ${cy + 1} L ${cx + 5} ${cy - 3}`, stroke: t.poolFill, "stroke-width": 1, fill: "none" })
|
|
33333
33380
|
]);
|
|
33334
33381
|
}
|
|
33335
33382
|
if (marker === "receive") {
|
|
33336
33383
|
return el("g", { class: "marker-receive" }, [
|
|
33337
|
-
el("rect", { x: cx - 5, y: cy - 3, width: 10, height: 7, fill: "none", stroke:
|
|
33338
|
-
el("path", { d: `M ${cx - 5} ${cy - 3} L ${cx} ${cy + 1} L ${cx + 5} ${cy - 3}`, stroke:
|
|
33384
|
+
el("rect", { x: cx - 5, y: cy - 3, width: 10, height: 7, fill: "none", stroke: t.bpmnStroke, "stroke-width": 1 }),
|
|
33385
|
+
el("path", { d: `M ${cx - 5} ${cy - 3} L ${cx} ${cy + 1} L ${cx + 5} ${cy - 3}`, stroke: t.bpmnStroke, "stroke-width": 1, fill: "none" })
|
|
33339
33386
|
]);
|
|
33340
33387
|
}
|
|
33341
33388
|
if (marker === "manual") {
|
|
@@ -33343,7 +33390,7 @@ function taskMarker(x, y, marker) {
|
|
|
33343
33390
|
el("path", {
|
|
33344
33391
|
d: `M ${cx - 3} ${cy + 4} L ${cx - 3} ${cy} L ${cx - 1.5} ${cy} L ${cx - 1.5} ${cy - 4} L ${cx} ${cy - 4} L ${cx} ${cy} L ${cx + 1.5} ${cy} L ${cx + 1.5} ${cy + 1} L ${cx + 3} ${cy + 1} L ${cx + 3} ${cy + 4} z`,
|
|
33345
33392
|
fill: "none",
|
|
33346
|
-
stroke:
|
|
33393
|
+
stroke: t.bpmnStroke,
|
|
33347
33394
|
"stroke-width": 1
|
|
33348
33395
|
})
|
|
33349
33396
|
]);
|
|
@@ -33353,19 +33400,19 @@ function taskMarker(x, y, marker) {
|
|
|
33353
33400
|
el("path", {
|
|
33354
33401
|
d: `M ${cx - 4} ${cy - 5} Q ${cx - 6} ${cy} ${cx - 4} ${cy + 5} L ${cx + 4} ${cy + 5} Q ${cx + 2} ${cy} ${cx + 4} ${cy - 5} z`,
|
|
33355
33402
|
fill: "none",
|
|
33356
|
-
stroke:
|
|
33403
|
+
stroke: t.bpmnStroke,
|
|
33357
33404
|
"stroke-width": 1
|
|
33358
33405
|
}),
|
|
33359
33406
|
el("path", {
|
|
33360
33407
|
d: `M ${cx - 2} ${cy - 2} L ${cx + 2} ${cy - 2} M ${cx - 2} ${cy} L ${cx + 2} ${cy} M ${cx - 2} ${cy + 2} L ${cx + 2} ${cy + 2}`,
|
|
33361
|
-
stroke:
|
|
33408
|
+
stroke: t.bpmnStroke,
|
|
33362
33409
|
"stroke-width": 0.8
|
|
33363
33410
|
})
|
|
33364
33411
|
]);
|
|
33365
33412
|
}
|
|
33366
33413
|
return "";
|
|
33367
33414
|
}
|
|
33368
|
-
function renderGateway(ol) {
|
|
33415
|
+
function renderGateway(ol, t) {
|
|
33369
33416
|
const g = ol.obj;
|
|
33370
33417
|
const cx = ol.x + ol.width / 2;
|
|
33371
33418
|
const cy = ol.y + ol.height / 2;
|
|
@@ -33377,7 +33424,7 @@ function renderGateway(ol) {
|
|
|
33377
33424
|
inner.push(
|
|
33378
33425
|
el("path", {
|
|
33379
33426
|
d: `M ${cx - a} ${cy - a} L ${cx + a} ${cy + a} M ${cx + a} ${cy - a} L ${cx - a} ${cy + a}`,
|
|
33380
|
-
stroke:
|
|
33427
|
+
stroke: t.gatewayGlyph,
|
|
33381
33428
|
"stroke-width": 2.5,
|
|
33382
33429
|
"stroke-linecap": "round"
|
|
33383
33430
|
})
|
|
@@ -33387,7 +33434,7 @@ function renderGateway(ol) {
|
|
|
33387
33434
|
inner.push(
|
|
33388
33435
|
el("path", {
|
|
33389
33436
|
d: `M ${cx - a} ${cy} L ${cx + a} ${cy} M ${cx} ${cy - a} L ${cx} ${cy + a}`,
|
|
33390
|
-
stroke:
|
|
33437
|
+
stroke: t.gatewayGlyph,
|
|
33391
33438
|
"stroke-width": 2.5,
|
|
33392
33439
|
"stroke-linecap": "round"
|
|
33393
33440
|
})
|
|
@@ -33399,7 +33446,7 @@ function renderGateway(ol) {
|
|
|
33399
33446
|
cy,
|
|
33400
33447
|
r: r6 * 0.45,
|
|
33401
33448
|
fill: "none",
|
|
33402
|
-
stroke:
|
|
33449
|
+
stroke: t.gatewayGlyph,
|
|
33403
33450
|
"stroke-width": 2
|
|
33404
33451
|
})
|
|
33405
33452
|
);
|
|
@@ -33410,7 +33457,7 @@ function renderGateway(ol) {
|
|
|
33410
33457
|
cy,
|
|
33411
33458
|
r: r6 * 0.55,
|
|
33412
33459
|
fill: "none",
|
|
33413
|
-
stroke:
|
|
33460
|
+
stroke: t.gatewayGlyph,
|
|
33414
33461
|
"stroke-width": 1
|
|
33415
33462
|
})
|
|
33416
33463
|
);
|
|
@@ -33424,7 +33471,7 @@ function renderGateway(ol) {
|
|
|
33424
33471
|
el("polygon", {
|
|
33425
33472
|
points: pts.join(" "),
|
|
33426
33473
|
fill: "none",
|
|
33427
|
-
stroke:
|
|
33474
|
+
stroke: t.gatewayGlyph,
|
|
33428
33475
|
"stroke-width": 1.2
|
|
33429
33476
|
})
|
|
33430
33477
|
);
|
|
@@ -33437,22 +33484,22 @@ function renderGateway(ol) {
|
|
|
33437
33484
|
"text-anchor": "middle",
|
|
33438
33485
|
"font-family": FONT_FAMILY,
|
|
33439
33486
|
"font-size": 11,
|
|
33440
|
-
fill:
|
|
33487
|
+
fill: t.bpmnText
|
|
33441
33488
|
},
|
|
33442
33489
|
labelStr
|
|
33443
33490
|
) : "";
|
|
33444
33491
|
return group({ class: `schematex-bpmn-gateway kind-${g.gatewayKind}` }, [
|
|
33445
33492
|
el("polygon", {
|
|
33446
33493
|
points,
|
|
33447
|
-
fill:
|
|
33448
|
-
stroke:
|
|
33494
|
+
fill: t.gatewayFill,
|
|
33495
|
+
stroke: t.gatewayStroke,
|
|
33449
33496
|
"stroke-width": 1.5
|
|
33450
33497
|
}),
|
|
33451
33498
|
...inner,
|
|
33452
33499
|
labelEl
|
|
33453
33500
|
]);
|
|
33454
33501
|
}
|
|
33455
|
-
function renderEvent2(ol) {
|
|
33502
|
+
function renderEvent2(ol, t) {
|
|
33456
33503
|
const e = ol.obj;
|
|
33457
33504
|
const cx = ol.x + ol.width / 2;
|
|
33458
33505
|
const cy = ol.y + ol.height / 2;
|
|
@@ -33460,14 +33507,16 @@ function renderEvent2(ol) {
|
|
|
33460
33507
|
const isEnd = e.kind === "end";
|
|
33461
33508
|
const isIntermediate = e.kind === "intermediate";
|
|
33462
33509
|
const strokeW = isEnd ? 3 : 1.2;
|
|
33510
|
+
const fill = isEnd ? t.endFill : isIntermediate ? t.intermediateFill : t.startFill;
|
|
33511
|
+
const ring = isEnd ? t.endStroke : isIntermediate ? t.intermediateStroke : t.startStroke;
|
|
33463
33512
|
const children = [];
|
|
33464
33513
|
children.push(
|
|
33465
33514
|
el("circle", {
|
|
33466
33515
|
cx,
|
|
33467
33516
|
cy,
|
|
33468
33517
|
r: r6,
|
|
33469
|
-
fill
|
|
33470
|
-
stroke:
|
|
33518
|
+
fill,
|
|
33519
|
+
stroke: ring,
|
|
33471
33520
|
"stroke-width": strokeW
|
|
33472
33521
|
})
|
|
33473
33522
|
);
|
|
@@ -33478,16 +33527,16 @@ function renderEvent2(ol) {
|
|
|
33478
33527
|
cy,
|
|
33479
33528
|
r: r6 - 3,
|
|
33480
33529
|
fill: "none",
|
|
33481
|
-
stroke:
|
|
33530
|
+
stroke: ring,
|
|
33482
33531
|
"stroke-width": 1.2
|
|
33483
33532
|
})
|
|
33484
33533
|
);
|
|
33485
33534
|
}
|
|
33486
33535
|
const filled = e.throwCatch === "throw" && (isIntermediate || isEnd);
|
|
33487
33536
|
if (e.trigger === "message") {
|
|
33488
|
-
children.push(messageGlyph(cx, cy, r6 * 0.55, filled));
|
|
33537
|
+
children.push(messageGlyph(cx, cy, r6 * 0.55, filled, t));
|
|
33489
33538
|
} else if (e.trigger === "timer") {
|
|
33490
|
-
children.push(timerGlyph(cx, cy, r6 * 0.55));
|
|
33539
|
+
children.push(timerGlyph(cx, cy, r6 * 0.55, t));
|
|
33491
33540
|
}
|
|
33492
33541
|
if (e.label) {
|
|
33493
33542
|
children.push(
|
|
@@ -33498,7 +33547,7 @@ function renderEvent2(ol) {
|
|
|
33498
33547
|
"text-anchor": "middle",
|
|
33499
33548
|
"font-family": FONT_FAMILY,
|
|
33500
33549
|
"font-size": 11,
|
|
33501
|
-
fill:
|
|
33550
|
+
fill: t.bpmnText
|
|
33502
33551
|
},
|
|
33503
33552
|
e.label
|
|
33504
33553
|
)
|
|
@@ -33506,7 +33555,7 @@ function renderEvent2(ol) {
|
|
|
33506
33555
|
}
|
|
33507
33556
|
return group({ class: `schematex-bpmn-event kind-${e.kind} trigger-${e.trigger}` }, children);
|
|
33508
33557
|
}
|
|
33509
|
-
function messageGlyph(cx, cy, size, filled) {
|
|
33558
|
+
function messageGlyph(cx, cy, size, filled, t) {
|
|
33510
33559
|
const w = size;
|
|
33511
33560
|
const h = size * 0.7;
|
|
33512
33561
|
const x = cx - w / 2;
|
|
@@ -33517,19 +33566,19 @@ function messageGlyph(cx, cy, size, filled) {
|
|
|
33517
33566
|
y,
|
|
33518
33567
|
width: w,
|
|
33519
33568
|
height: h,
|
|
33520
|
-
fill: filled ?
|
|
33521
|
-
stroke:
|
|
33569
|
+
fill: filled ? t.bpmnStroke : t.poolFill,
|
|
33570
|
+
stroke: t.bpmnStroke,
|
|
33522
33571
|
"stroke-width": 1
|
|
33523
33572
|
}),
|
|
33524
33573
|
el("path", {
|
|
33525
33574
|
d: `M ${x} ${y} L ${cx} ${y + h * 0.55} L ${x + w} ${y}`,
|
|
33526
|
-
stroke: filled ?
|
|
33575
|
+
stroke: filled ? t.poolFill : t.bpmnStroke,
|
|
33527
33576
|
"stroke-width": 1,
|
|
33528
33577
|
fill: "none"
|
|
33529
33578
|
})
|
|
33530
33579
|
]);
|
|
33531
33580
|
}
|
|
33532
|
-
function timerGlyph(cx, cy, size) {
|
|
33581
|
+
function timerGlyph(cx, cy, size, t) {
|
|
33533
33582
|
const r6 = size / 2;
|
|
33534
33583
|
const ticks = [];
|
|
33535
33584
|
for (let k = 0; k < 12; k++) {
|
|
@@ -33541,18 +33590,18 @@ function timerGlyph(cx, cy, size) {
|
|
|
33541
33590
|
ticks.push(`M ${t1x} ${t1y} L ${t2x} ${t2y}`);
|
|
33542
33591
|
}
|
|
33543
33592
|
return el("g", { class: "trigger-timer" }, [
|
|
33544
|
-
el("circle", { cx, cy, r: r6, fill: "none", stroke:
|
|
33545
|
-
el("path", { d: ticks.join(" "), stroke:
|
|
33593
|
+
el("circle", { cx, cy, r: r6, fill: "none", stroke: t.bpmnStroke, "stroke-width": 1 }),
|
|
33594
|
+
el("path", { d: ticks.join(" "), stroke: t.bpmnStroke, "stroke-width": 0.8 }),
|
|
33546
33595
|
// Hands
|
|
33547
33596
|
el("path", {
|
|
33548
33597
|
d: `M ${cx} ${cy} L ${cx} ${cy - r6 * 0.7} M ${cx} ${cy} L ${cx + r6 * 0.5} ${cy}`,
|
|
33549
|
-
stroke:
|
|
33598
|
+
stroke: t.bpmnStroke,
|
|
33550
33599
|
"stroke-width": 1.2,
|
|
33551
33600
|
"stroke-linecap": "round"
|
|
33552
33601
|
})
|
|
33553
33602
|
]);
|
|
33554
33603
|
}
|
|
33555
|
-
function renderFlow2(fl) {
|
|
33604
|
+
function renderFlow2(fl, t) {
|
|
33556
33605
|
const f = fl.flow;
|
|
33557
33606
|
const isMessage = f.kind === "message";
|
|
33558
33607
|
const dasharray = isMessage ? "6 4" : void 0;
|
|
@@ -33562,7 +33611,7 @@ function renderFlow2(fl) {
|
|
|
33562
33611
|
path({
|
|
33563
33612
|
d: fl.path,
|
|
33564
33613
|
fill: "none",
|
|
33565
|
-
stroke: isMessage ?
|
|
33614
|
+
stroke: isMessage ? t.msgFlowStroke : t.flowStroke,
|
|
33566
33615
|
"stroke-width": 1.4,
|
|
33567
33616
|
"stroke-dasharray": dasharray,
|
|
33568
33617
|
"marker-start": markerStart,
|
|
@@ -33575,8 +33624,8 @@ function renderFlow2(fl) {
|
|
|
33575
33624
|
children.push(
|
|
33576
33625
|
el("polygon", {
|
|
33577
33626
|
points: diamondPoints(head.x, head.y, 5),
|
|
33578
|
-
fill:
|
|
33579
|
-
stroke:
|
|
33627
|
+
fill: t.poolFill,
|
|
33628
|
+
stroke: t.flowStroke,
|
|
33580
33629
|
"stroke-width": 1
|
|
33581
33630
|
})
|
|
33582
33631
|
);
|
|
@@ -33588,7 +33637,7 @@ function renderFlow2(fl) {
|
|
|
33588
33637
|
children.push(
|
|
33589
33638
|
el("path", {
|
|
33590
33639
|
d: `M ${head.x - 4} ${head.y + 4} L ${head.x + 4} ${head.y - 4}`,
|
|
33591
|
-
stroke:
|
|
33640
|
+
stroke: t.flowStroke,
|
|
33592
33641
|
"stroke-width": 1.5
|
|
33593
33642
|
})
|
|
33594
33643
|
);
|
|
@@ -33603,7 +33652,7 @@ function renderFlow2(fl) {
|
|
|
33603
33652
|
"text-anchor": "middle",
|
|
33604
33653
|
"font-family": FONT_FAMILY,
|
|
33605
33654
|
"font-size": 10,
|
|
33606
|
-
fill:
|
|
33655
|
+
fill: t.bpmnText
|
|
33607
33656
|
},
|
|
33608
33657
|
f.label
|
|
33609
33658
|
)
|
|
@@ -33635,7 +33684,7 @@ var bpmn = {
|
|
|
33635
33684
|
},
|
|
33636
33685
|
parse: parseBpmn,
|
|
33637
33686
|
render(text2, config) {
|
|
33638
|
-
return renderBpmn(text2);
|
|
33687
|
+
return renderBpmn(text2, config);
|
|
33639
33688
|
}
|
|
33640
33689
|
};
|
|
33641
33690
|
|
|
@@ -35922,6 +35971,2508 @@ var sfc = {
|
|
|
35922
35971
|
}
|
|
35923
35972
|
};
|
|
35924
35973
|
|
|
35974
|
+
// src/diagrams/floorplan/catalog.ts
|
|
35975
|
+
var CHAIR_W = 0.44;
|
|
35976
|
+
var CHAIR_D = 0.38;
|
|
35977
|
+
var CHAIR_GAP = 0.27;
|
|
35978
|
+
var CHAIR_OVERHANG = 0.5;
|
|
35979
|
+
var RING = 0.45;
|
|
35980
|
+
function chairAt(px, cx, cy, deg) {
|
|
35981
|
+
const body = rect({
|
|
35982
|
+
class: "sx-fp-chair",
|
|
35983
|
+
x: px(-CHAIR_W / 2),
|
|
35984
|
+
y: px(-CHAIR_D / 2),
|
|
35985
|
+
width: px(CHAIR_W),
|
|
35986
|
+
height: px(CHAIR_D),
|
|
35987
|
+
rx: px(0.09)
|
|
35988
|
+
});
|
|
35989
|
+
const rot = Math.round(deg * 10) / 10;
|
|
35990
|
+
return el("g", { transform: `translate(${px(cx)},${px(cy)}) rotate(${rot})` }, [body]);
|
|
35991
|
+
}
|
|
35992
|
+
function edgeChairs(c, top, bottom) {
|
|
35993
|
+
const n = Math.max(1, Math.round(c.w / 0.65));
|
|
35994
|
+
const out = [];
|
|
35995
|
+
for (let i = 0; i < n; i++) {
|
|
35996
|
+
const cx = (i + 0.5) / n * c.w;
|
|
35997
|
+
if (top) out.push(chairAt(c.px, cx, -CHAIR_GAP, 0));
|
|
35998
|
+
out.push(chairAt(c.px, cx, c.h + CHAIR_GAP, 180));
|
|
35999
|
+
}
|
|
36000
|
+
return out.join("");
|
|
36001
|
+
}
|
|
36002
|
+
function box(c, cls = "sx-fp-furn", rx = 0) {
|
|
36003
|
+
return rect({ class: cls, x: 0, y: 0, width: c.px(c.w), height: c.px(c.h), rx: rx ? c.px(rx) : void 0 });
|
|
36004
|
+
}
|
|
36005
|
+
function glyphText(c, label) {
|
|
36006
|
+
return text(
|
|
36007
|
+
{
|
|
36008
|
+
class: "sx-fp-furn-text",
|
|
36009
|
+
x: c.px(c.w / 2),
|
|
36010
|
+
y: c.px(c.h / 2),
|
|
36011
|
+
"text-anchor": "middle",
|
|
36012
|
+
"dominant-baseline": "central",
|
|
36013
|
+
"font-size": c.px(Math.min(0.24, c.h * 0.4))
|
|
36014
|
+
},
|
|
36015
|
+
label
|
|
36016
|
+
);
|
|
36017
|
+
}
|
|
36018
|
+
function bedDraw(pillows) {
|
|
36019
|
+
return (c) => {
|
|
36020
|
+
const parts = [box(c, "sx-fp-furn", 0.05)];
|
|
36021
|
+
const pw = pillows === 2 ? c.w / 2 - 0.18 : c.w - 0.24;
|
|
36022
|
+
parts.push(rect({ class: "sx-fp-furn", x: c.px(0.12), y: c.px(0.1), width: c.px(pw), height: c.px(0.42), rx: c.px(0.06) }));
|
|
36023
|
+
if (pillows === 2) {
|
|
36024
|
+
parts.push(rect({ class: "sx-fp-furn", x: c.px(c.w / 2 + 0.06), y: c.px(0.1), width: c.px(pw), height: c.px(0.42), rx: c.px(0.06) }));
|
|
36025
|
+
}
|
|
36026
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(0.72), x2: c.px(c.w), y2: c.px(0.72) }));
|
|
36027
|
+
return parts.join("");
|
|
36028
|
+
};
|
|
36029
|
+
}
|
|
36030
|
+
function sofaDraw(cushions) {
|
|
36031
|
+
return (c) => {
|
|
36032
|
+
const parts = [box(c, "sx-fp-furn", 0.08)];
|
|
36033
|
+
parts.push(rect({ class: "sx-fp-furn", x: c.px(0.15), y: 0, width: c.px(c.w - 0.3), height: c.px(0.18), rx: c.px(0.05) }));
|
|
36034
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(0.15), y1: c.px(0.18), x2: c.px(0.15), y2: c.px(c.h - 0.05) }));
|
|
36035
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(c.w - 0.15), y1: c.px(0.18), x2: c.px(c.w - 0.15), y2: c.px(c.h - 0.05) }));
|
|
36036
|
+
for (let i = 1; i < cushions; i++) {
|
|
36037
|
+
const x = c.px(0.15 + (c.w - 0.3) * i / cushions);
|
|
36038
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: c.px(0.18), x2: x, y2: c.px(c.h - 0.08) }));
|
|
36039
|
+
}
|
|
36040
|
+
return parts.join("");
|
|
36041
|
+
};
|
|
36042
|
+
}
|
|
36043
|
+
function shelfDraw(c) {
|
|
36044
|
+
const parts = [box(c)];
|
|
36045
|
+
const n = Math.max(2, Math.round(c.w / 0.15));
|
|
36046
|
+
for (let i = 1; i < n; i++) {
|
|
36047
|
+
const x = c.px(c.w * i / n);
|
|
36048
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: 0, x2: x, y2: c.px(c.h) }));
|
|
36049
|
+
}
|
|
36050
|
+
return parts.join("");
|
|
36051
|
+
}
|
|
36052
|
+
function boardDraw(c) {
|
|
36053
|
+
return [
|
|
36054
|
+
rect({ class: "sx-fp-furn-solid", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) }),
|
|
36055
|
+
rect({ class: "sx-fp-board-inner", x: c.px(0.08), y: c.px(c.h * 0.2), width: c.px(c.w - 0.16), height: c.px(c.h * 0.6) })
|
|
36056
|
+
].join("");
|
|
36057
|
+
}
|
|
36058
|
+
function applianceDraw(label, drum) {
|
|
36059
|
+
return (c) => {
|
|
36060
|
+
const parts = [box(c)];
|
|
36061
|
+
if (drum) {
|
|
36062
|
+
parts.push(circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(Math.min(c.w, c.h) * 0.32) }));
|
|
36063
|
+
} else {
|
|
36064
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(0.12), x2: c.px(c.w), y2: c.px(0.12) }));
|
|
36065
|
+
}
|
|
36066
|
+
parts.push(glyphText(c, label));
|
|
36067
|
+
return parts.join("");
|
|
36068
|
+
};
|
|
36069
|
+
}
|
|
36070
|
+
function roundTable(seats, diaM) {
|
|
36071
|
+
const nominal = diaM + 2 * RING;
|
|
36072
|
+
return {
|
|
36073
|
+
w: nominal,
|
|
36074
|
+
h: nominal,
|
|
36075
|
+
draw: (c) => {
|
|
36076
|
+
const half = Math.min(c.w, c.h) / 2;
|
|
36077
|
+
const ring = Math.min(RING, half * 0.37);
|
|
36078
|
+
const r6 = half - ring;
|
|
36079
|
+
const cx = c.w / 2;
|
|
36080
|
+
const cy = c.h / 2;
|
|
36081
|
+
const parts = [circle({ class: "sx-fp-furn", cx: c.px(cx), cy: c.px(cy), r: c.px(r6) })];
|
|
36082
|
+
for (let i = 0; i < seats; i++) {
|
|
36083
|
+
const a = i / seats * 2 * Math.PI - Math.PI / 2;
|
|
36084
|
+
const px0 = cx + (r6 + ring * 0.55) * Math.cos(a);
|
|
36085
|
+
const py0 = cy + (r6 + ring * 0.55) * Math.sin(a);
|
|
36086
|
+
parts.push(chairAt(c.px, px0, py0, a * 180 / Math.PI + 90));
|
|
36087
|
+
}
|
|
36088
|
+
return parts.join("");
|
|
36089
|
+
}
|
|
36090
|
+
};
|
|
36091
|
+
}
|
|
36092
|
+
function tableDraw(top, bottom) {
|
|
36093
|
+
return (c) => box(c) + edgeChairs(c, top);
|
|
36094
|
+
}
|
|
36095
|
+
var TREAD = 0.28;
|
|
36096
|
+
function treadLines(c, vert, fixed0, fixed1, from, to, dashedFrom) {
|
|
36097
|
+
const parts = [];
|
|
36098
|
+
const dir = to >= from ? 1 : -1;
|
|
36099
|
+
for (let d = from; dir > 0 ? d <= to : d >= to; d += TREAD * dir) {
|
|
36100
|
+
const dashed = dashedFrom !== void 0 && (dir > 0 ? d > dashedFrom : d < dashedFrom);
|
|
36101
|
+
const cls = dashed ? "sx-fp-furn-dash" : "sx-fp-furn-line";
|
|
36102
|
+
if (vert) parts.push(line({ class: cls, x1: c.px(fixed0), y1: c.px(d), x2: c.px(fixed1), y2: c.px(d) }));
|
|
36103
|
+
else parts.push(line({ class: cls, x1: c.px(d), y1: c.px(fixed0), x2: c.px(d), y2: c.px(fixed1) }));
|
|
36104
|
+
}
|
|
36105
|
+
return parts.join("");
|
|
36106
|
+
}
|
|
36107
|
+
function breakLine(c, vert, lo, hi, at) {
|
|
36108
|
+
const span = hi - lo;
|
|
36109
|
+
const dz = Math.min(0.12, span * 0.18);
|
|
36110
|
+
const tilt = span * 0.18;
|
|
36111
|
+
const pts = vert ? [
|
|
36112
|
+
[lo, at + tilt],
|
|
36113
|
+
[lo + span * 0.4, at + tilt * 0.2],
|
|
36114
|
+
[lo + span * 0.5 - dz, at + tilt * 0.2 + dz],
|
|
36115
|
+
[lo + span * 0.5 + dz, at - tilt * 0.2 - dz],
|
|
36116
|
+
[lo + span * 0.6, at - tilt * 0.2],
|
|
36117
|
+
[hi, at - tilt]
|
|
36118
|
+
] : [
|
|
36119
|
+
[at + tilt, lo],
|
|
36120
|
+
[at + tilt * 0.2, lo + span * 0.4],
|
|
36121
|
+
[at + tilt * 0.2 + dz, lo + span * 0.5 - dz],
|
|
36122
|
+
[at - tilt * 0.2 - dz, lo + span * 0.5 + dz],
|
|
36123
|
+
[at - tilt * 0.2, lo + span * 0.6],
|
|
36124
|
+
[at - tilt, hi]
|
|
36125
|
+
];
|
|
36126
|
+
const d = pts.map(([a, b], i) => `${i === 0 ? "M" : "L"} ${c.px(vert ? a : a)} ${c.px(vert ? b : b)}`).join(" ");
|
|
36127
|
+
return path({ class: "sx-fp-stair-break", d });
|
|
36128
|
+
}
|
|
36129
|
+
function dirArrow(c, points, labelText2) {
|
|
36130
|
+
const parts = [];
|
|
36131
|
+
const [sx, sy] = points[0];
|
|
36132
|
+
parts.push(circle({ class: "sx-fp-furn-line", cx: c.px(sx), cy: c.px(sy), r: c.px(0.05) }));
|
|
36133
|
+
const d = points.map(([x, y], i) => `${i === 0 ? "M" : "L"} ${c.px(x)} ${c.px(y)}`).join(" ");
|
|
36134
|
+
parts.push(path({ class: "sx-fp-furn-line", d }));
|
|
36135
|
+
const [ex, ey] = points[points.length - 1];
|
|
36136
|
+
const [px2, py2] = points[points.length - 2];
|
|
36137
|
+
const ang = Math.atan2(ey - py2, ex - px2);
|
|
36138
|
+
const hs = 0.11;
|
|
36139
|
+
const a1 = ang + Math.PI - 0.5;
|
|
36140
|
+
const a2 = ang + Math.PI + 0.5;
|
|
36141
|
+
parts.push(
|
|
36142
|
+
polygon({
|
|
36143
|
+
class: "sx-fp-furn-dot",
|
|
36144
|
+
points: `${c.px(ex)},${c.px(ey)} ${c.px(ex + hs * Math.cos(a1))},${c.px(ey + hs * Math.sin(a1))} ${c.px(ex + hs * Math.cos(a2))},${c.px(ey + hs * Math.sin(a2))}`
|
|
36145
|
+
})
|
|
36146
|
+
);
|
|
36147
|
+
const [nx, ny] = points[1];
|
|
36148
|
+
const segLen = Math.hypot(nx - sx, ny - sy) || 1;
|
|
36149
|
+
const ux = (nx - sx) / segLen;
|
|
36150
|
+
const uy = (ny - sy) / segLen;
|
|
36151
|
+
const lx = sx + ux * 0.42 - uy * 0.16;
|
|
36152
|
+
const ly = sy + uy * 0.42 + ux * 0.16;
|
|
36153
|
+
parts.push(
|
|
36154
|
+
text(
|
|
36155
|
+
{
|
|
36156
|
+
class: "sx-fp-furn-text",
|
|
36157
|
+
x: c.px(lx),
|
|
36158
|
+
y: c.px(ly),
|
|
36159
|
+
"text-anchor": "middle",
|
|
36160
|
+
"dominant-baseline": "central",
|
|
36161
|
+
"font-size": c.px(0.18)
|
|
36162
|
+
},
|
|
36163
|
+
labelText2
|
|
36164
|
+
)
|
|
36165
|
+
);
|
|
36166
|
+
return parts.join("");
|
|
36167
|
+
}
|
|
36168
|
+
function straightStairs(c) {
|
|
36169
|
+
const vert = c.h >= c.w;
|
|
36170
|
+
const len = vert ? c.h : c.w;
|
|
36171
|
+
const breakAt = Math.min(7 * TREAD, len * 0.62);
|
|
36172
|
+
const parts = [box(c, "sx-fp-furn-nofill")];
|
|
36173
|
+
if (vert) {
|
|
36174
|
+
parts.push(treadLines(c, true, 0, c.w, c.h - TREAD, 0.02, c.h - breakAt));
|
|
36175
|
+
parts.push(breakLine(c, true, 0, c.w, c.h - breakAt));
|
|
36176
|
+
parts.push(dirArrow(c, [[c.w / 2, c.h - 0.18], [c.w / 2, c.h - breakAt + 0.28]], c.label ?? "UP"));
|
|
36177
|
+
} else {
|
|
36178
|
+
parts.push(treadLines(c, false, 0, c.h, TREAD, c.w - 0.02, breakAt));
|
|
36179
|
+
parts.push(breakLine(c, false, 0, c.h, breakAt));
|
|
36180
|
+
parts.push(dirArrow(c, [[0.18, c.h / 2], [breakAt - 0.28, c.h / 2]], c.label ?? "UP"));
|
|
36181
|
+
}
|
|
36182
|
+
return parts.join("");
|
|
36183
|
+
}
|
|
36184
|
+
function lStairs(c) {
|
|
36185
|
+
const rw = Math.min(c.w, c.h) * 0.45;
|
|
36186
|
+
const parts = [
|
|
36187
|
+
path({
|
|
36188
|
+
class: "sx-fp-furn-nofill",
|
|
36189
|
+
d: `M 0 0 L ${c.px(c.w)} 0 L ${c.px(c.w)} ${c.px(rw)} L ${c.px(rw)} ${c.px(rw)} L ${c.px(rw)} ${c.px(c.h)} L 0 ${c.px(c.h)} Z`
|
|
36190
|
+
})
|
|
36191
|
+
];
|
|
36192
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(rw), y1: 0, x2: c.px(rw), y2: c.px(rw) }));
|
|
36193
|
+
parts.push(treadLines(c, true, 0, rw, c.h - TREAD, rw + 0.05));
|
|
36194
|
+
const breakAt = rw + (c.w - rw) * 0.55;
|
|
36195
|
+
parts.push(treadLines(c, false, 0, rw, rw + TREAD, c.w - 0.02, breakAt));
|
|
36196
|
+
parts.push(breakLine(c, false, 0, rw, breakAt));
|
|
36197
|
+
parts.push(
|
|
36198
|
+
dirArrow(
|
|
36199
|
+
c,
|
|
36200
|
+
[
|
|
36201
|
+
[rw / 2, c.h - 0.18],
|
|
36202
|
+
[rw / 2, rw / 2],
|
|
36203
|
+
[breakAt - 0.24, rw / 2]
|
|
36204
|
+
],
|
|
36205
|
+
c.label ?? "UP"
|
|
36206
|
+
)
|
|
36207
|
+
);
|
|
36208
|
+
return parts.join("");
|
|
36209
|
+
}
|
|
36210
|
+
function uStairs(c) {
|
|
36211
|
+
const lh = Math.min(c.h * 0.3, Math.max(0.9, c.w / 2));
|
|
36212
|
+
const mid = c.w / 2;
|
|
36213
|
+
const parts = [box(c, "sx-fp-furn-nofill")];
|
|
36214
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(lh), x2: c.px(c.w), y2: c.px(lh) }));
|
|
36215
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(mid), y1: c.px(lh), x2: c.px(mid), y2: c.px(c.h) }));
|
|
36216
|
+
parts.push(treadLines(c, true, mid, c.w, c.h - TREAD, lh + 0.05));
|
|
36217
|
+
const breakAt = lh + (c.h - lh) * 0.4;
|
|
36218
|
+
parts.push(treadLines(c, true, 0, mid, lh + TREAD, c.h - 0.02, breakAt));
|
|
36219
|
+
parts.push(breakLine(c, true, 0, mid, breakAt));
|
|
36220
|
+
parts.push(
|
|
36221
|
+
dirArrow(
|
|
36222
|
+
c,
|
|
36223
|
+
[
|
|
36224
|
+
[mid + mid / 2, c.h - 0.18],
|
|
36225
|
+
[mid + mid / 2, lh / 2],
|
|
36226
|
+
[mid / 2, lh / 2],
|
|
36227
|
+
[mid / 2, breakAt - 0.24]
|
|
36228
|
+
],
|
|
36229
|
+
c.label ?? "UP"
|
|
36230
|
+
)
|
|
36231
|
+
);
|
|
36232
|
+
return parts.join("");
|
|
36233
|
+
}
|
|
36234
|
+
function spiralStairs(c) {
|
|
36235
|
+
const r6 = Math.min(c.w, c.h) / 2 - 0.02;
|
|
36236
|
+
const cx = c.w / 2;
|
|
36237
|
+
const cy = c.h / 2;
|
|
36238
|
+
const parts = [
|
|
36239
|
+
circle({ class: "sx-fp-furn", cx: c.px(cx), cy: c.px(cy), r: c.px(r6) }),
|
|
36240
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(cx), cy: c.px(cy), r: c.px(0.08) })
|
|
36241
|
+
];
|
|
36242
|
+
for (let i = 0; i < 12; i++) {
|
|
36243
|
+
const a = i / 12 * 2 * Math.PI;
|
|
36244
|
+
parts.push(
|
|
36245
|
+
line({
|
|
36246
|
+
class: "sx-fp-furn-line",
|
|
36247
|
+
x1: c.px(cx + 0.08 * Math.cos(a)),
|
|
36248
|
+
y1: c.px(cy + 0.08 * Math.sin(a)),
|
|
36249
|
+
x2: c.px(cx + r6 * Math.cos(a)),
|
|
36250
|
+
y2: c.px(cy + r6 * Math.sin(a))
|
|
36251
|
+
})
|
|
36252
|
+
);
|
|
36253
|
+
}
|
|
36254
|
+
const wr = r6 * 0.62;
|
|
36255
|
+
const a0 = Math.PI * 0.75;
|
|
36256
|
+
const a1 = Math.PI * 1.9;
|
|
36257
|
+
const steps = 5;
|
|
36258
|
+
const pts = [];
|
|
36259
|
+
for (let i = 0; i <= steps; i++) {
|
|
36260
|
+
const a = a0 + (a1 - a0) * i / steps;
|
|
36261
|
+
pts.push([cx + wr * Math.cos(a), cy + wr * Math.sin(a)]);
|
|
36262
|
+
}
|
|
36263
|
+
parts.push(dirArrow(c, pts, c.label ?? "UP"));
|
|
36264
|
+
return parts.join("");
|
|
36265
|
+
}
|
|
36266
|
+
var FLOORPLAN_SYMBOLS = {
|
|
36267
|
+
// ── residential / living ──
|
|
36268
|
+
"bed-double": { w: 1.6, h: 2, draw: bedDraw(2) },
|
|
36269
|
+
"bed-single": { w: 0.9, h: 2, draw: bedDraw(1) },
|
|
36270
|
+
"bed-queen": { w: 1.55, h: 2.05, draw: bedDraw(2) },
|
|
36271
|
+
"bed-king": { w: 1.95, h: 2.05, draw: bedDraw(2) },
|
|
36272
|
+
sofa: { w: 2.2, h: 0.9, draw: sofaDraw(3) },
|
|
36273
|
+
loveseat: { w: 1.5, h: 0.9, draw: sofaDraw(2) },
|
|
36274
|
+
armchair: { w: 0.9, h: 0.9, draw: sofaDraw(1) },
|
|
36275
|
+
"coffee-table": { w: 1, h: 0.5, draw: (c) => box(c, "sx-fp-furn", 0.06) },
|
|
36276
|
+
tv: { w: 1.4, h: 0.15, draw: (c) => rect({ class: "sx-fp-furn-solid", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) }) },
|
|
36277
|
+
rug: {
|
|
36278
|
+
w: 2,
|
|
36279
|
+
h: 1.4,
|
|
36280
|
+
underlay: true,
|
|
36281
|
+
draw: (c) => rect({ class: "sx-fp-rug", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h), rx: c.px(0.1) })
|
|
36282
|
+
},
|
|
36283
|
+
wardrobe: {
|
|
36284
|
+
w: 1.8,
|
|
36285
|
+
h: 0.6,
|
|
36286
|
+
draw: (c) => {
|
|
36287
|
+
const parts = [box(c)];
|
|
36288
|
+
if (c.w >= c.h) {
|
|
36289
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h / 2), x2: c.px(c.w), y2: c.px(c.h / 2) }));
|
|
36290
|
+
const n = Math.max(2, Math.round(c.w / 0.15));
|
|
36291
|
+
for (let i = 0; i < n; i++) {
|
|
36292
|
+
const x = c.px((i + 0.5) / n * c.w);
|
|
36293
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: c.px(c.h / 2 - 0.1), x2: x, y2: c.px(c.h / 2 + 0.1) }));
|
|
36294
|
+
}
|
|
36295
|
+
} else {
|
|
36296
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(c.w / 2), y1: 0, x2: c.px(c.w / 2), y2: c.px(c.h) }));
|
|
36297
|
+
const n = Math.max(2, Math.round(c.h / 0.15));
|
|
36298
|
+
for (let i = 0; i < n; i++) {
|
|
36299
|
+
const y = c.px((i + 0.5) / n * c.h);
|
|
36300
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(c.w / 2 - 0.1), y1: y, x2: c.px(c.w / 2 + 0.1), y2: y }));
|
|
36301
|
+
}
|
|
36302
|
+
}
|
|
36303
|
+
return parts.join("");
|
|
36304
|
+
}
|
|
36305
|
+
},
|
|
36306
|
+
dresser: {
|
|
36307
|
+
w: 1.2,
|
|
36308
|
+
h: 0.5,
|
|
36309
|
+
draw: (c) => [
|
|
36310
|
+
box(c),
|
|
36311
|
+
line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h / 2), x2: c.px(c.w), y2: c.px(c.h / 2) }),
|
|
36312
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(c.w * 0.3), cy: c.px(c.h * 0.25), r: c.px(0.03) }),
|
|
36313
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(c.w * 0.7), cy: c.px(c.h * 0.25), r: c.px(0.03) })
|
|
36314
|
+
].join("")
|
|
36315
|
+
},
|
|
36316
|
+
nightstand: {
|
|
36317
|
+
w: 0.5,
|
|
36318
|
+
h: 0.4,
|
|
36319
|
+
draw: (c) => box(c) + circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(0.08) })
|
|
36320
|
+
},
|
|
36321
|
+
bookshelf: { w: 0.9, h: 0.3, draw: shelfDraw },
|
|
36322
|
+
plant: {
|
|
36323
|
+
w: 0.5,
|
|
36324
|
+
h: 0.5,
|
|
36325
|
+
draw: (c) => {
|
|
36326
|
+
const r6 = Math.min(c.w, c.h) / 2;
|
|
36327
|
+
const parts = [circle({ class: "sx-fp-furn", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(r6) })];
|
|
36328
|
+
for (const a of [0, 60, 120, 180, 240, 300]) {
|
|
36329
|
+
const rad = a * Math.PI / 180;
|
|
36330
|
+
parts.push(
|
|
36331
|
+
line({
|
|
36332
|
+
class: "sx-fp-furn-line",
|
|
36333
|
+
x1: c.px(c.w / 2),
|
|
36334
|
+
y1: c.px(c.h / 2),
|
|
36335
|
+
x2: c.px(c.w / 2 + (r6 - 0.03) * Math.cos(rad)),
|
|
36336
|
+
y2: c.px(c.h / 2 + (r6 - 0.03) * Math.sin(rad))
|
|
36337
|
+
})
|
|
36338
|
+
);
|
|
36339
|
+
}
|
|
36340
|
+
return parts.join("");
|
|
36341
|
+
}
|
|
36342
|
+
},
|
|
36343
|
+
"dining-table": { w: 1.6, h: 0.9, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true) },
|
|
36344
|
+
sectional: {
|
|
36345
|
+
w: 2.6,
|
|
36346
|
+
h: 2,
|
|
36347
|
+
draw: (c) => {
|
|
36348
|
+
const d = 0.9 * Math.min(c.h / 2, 1);
|
|
36349
|
+
const parts = [
|
|
36350
|
+
path({
|
|
36351
|
+
class: "sx-fp-furn",
|
|
36352
|
+
d: `M 0 0 L ${c.px(c.w)} 0 L ${c.px(c.w)} ${c.px(d)} L ${c.px(d)} ${c.px(d)} L ${c.px(d)} ${c.px(c.h)} L 0 ${c.px(c.h)} Z`
|
|
36353
|
+
}),
|
|
36354
|
+
rect({ class: "sx-fp-furn", x: c.px(0.12), y: 0, width: c.px(c.w - 0.24), height: c.px(0.16), rx: c.px(0.05) }),
|
|
36355
|
+
rect({ class: "sx-fp-furn", x: 0, y: c.px(0.12), width: c.px(0.16), height: c.px(c.h - 0.24), rx: c.px(0.05) })
|
|
36356
|
+
];
|
|
36357
|
+
for (const fx of [0.33, 0.66]) {
|
|
36358
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(d + (c.w - d) * fx), y1: c.px(0.16), x2: c.px(d + (c.w - d) * fx), y2: c.px(d - 0.05) }));
|
|
36359
|
+
}
|
|
36360
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(0.16), y1: c.px(c.h / 2 + d / 4), x2: c.px(d - 0.05), y2: c.px(c.h / 2 + d / 4) }));
|
|
36361
|
+
return parts.join("");
|
|
36362
|
+
}
|
|
36363
|
+
},
|
|
36364
|
+
"side-table": { w: 0.5, h: 0.5, draw: (c) => box(c, "sx-fp-furn", 0.06) },
|
|
36365
|
+
"tv-stand": {
|
|
36366
|
+
w: 1.6,
|
|
36367
|
+
h: 0.45,
|
|
36368
|
+
underlay: true,
|
|
36369
|
+
// a surface — the TV sits on it
|
|
36370
|
+
draw: (c) => box(c) + line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h / 2), x2: c.px(c.w), y2: c.px(c.h / 2) })
|
|
36371
|
+
},
|
|
36372
|
+
fireplace: {
|
|
36373
|
+
w: 1.5,
|
|
36374
|
+
h: 0.5,
|
|
36375
|
+
draw: (c) => {
|
|
36376
|
+
const inset = Math.min(0.18, c.w * 0.15);
|
|
36377
|
+
return [
|
|
36378
|
+
box(c),
|
|
36379
|
+
// firebox opening toward the room (south edge)
|
|
36380
|
+
rect({
|
|
36381
|
+
class: "sx-fp-furn-line",
|
|
36382
|
+
x: c.px(inset),
|
|
36383
|
+
y: c.px(c.h * 0.3),
|
|
36384
|
+
width: c.px(c.w - 2 * inset),
|
|
36385
|
+
height: c.px(c.h * 0.7 - 0.04)
|
|
36386
|
+
}),
|
|
36387
|
+
line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h * 0.3), x2: c.px(inset), y2: 0 }),
|
|
36388
|
+
line({ class: "sx-fp-furn-line", x1: c.px(c.w), y1: c.px(c.h * 0.3), x2: c.px(c.w - inset), y2: 0 })
|
|
36389
|
+
].join("");
|
|
36390
|
+
}
|
|
36391
|
+
},
|
|
36392
|
+
"floor-lamp": {
|
|
36393
|
+
w: 0.35,
|
|
36394
|
+
h: 0.35,
|
|
36395
|
+
draw: (c) => {
|
|
36396
|
+
const r6 = Math.min(c.w, c.h) / 2;
|
|
36397
|
+
return [
|
|
36398
|
+
circle({ class: "sx-fp-furn", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(r6) }),
|
|
36399
|
+
line({ class: "sx-fp-furn-line", x1: c.px(c.w / 2 - r6), y1: c.px(c.h / 2), x2: c.px(c.w / 2 + r6), y2: c.px(c.h / 2) }),
|
|
36400
|
+
line({ class: "sx-fp-furn-line", x1: c.px(c.w / 2), y1: c.px(c.h / 2 - r6), x2: c.px(c.w / 2), y2: c.px(c.h / 2 + r6) })
|
|
36401
|
+
].join("");
|
|
36402
|
+
}
|
|
36403
|
+
},
|
|
36404
|
+
ottoman: { w: 0.6, h: 0.45, draw: (c) => box(c, "sx-fp-furn", 0.12) },
|
|
36405
|
+
piano: {
|
|
36406
|
+
w: 1.5,
|
|
36407
|
+
h: 1.7,
|
|
36408
|
+
draw: (c) => {
|
|
36409
|
+
const X = (v) => c.px(v * (c.w / 1.5));
|
|
36410
|
+
const Y = (v) => c.px(v * (c.h / 1.7));
|
|
36411
|
+
const body = [
|
|
36412
|
+
`M ${X(0)} ${Y(0)}`,
|
|
36413
|
+
`L ${X(1.5)} ${Y(0)}`,
|
|
36414
|
+
`L ${X(1.5)} ${Y(0.75)}`,
|
|
36415
|
+
`C ${X(1.5)} ${Y(1.25)} ${X(1.25)} ${Y(1.7)} ${X(0.8)} ${Y(1.7)}`,
|
|
36416
|
+
`C ${X(0.45)} ${Y(1.7)} ${X(0.28)} ${Y(1.45)} ${X(0.28)} ${Y(1.1)}`,
|
|
36417
|
+
`C ${X(0.28)} ${Y(0.85)} ${X(0.14)} ${Y(0.72)} ${X(0)} ${Y(0.68)}`,
|
|
36418
|
+
"Z"
|
|
36419
|
+
].join(" ");
|
|
36420
|
+
return path({ class: "sx-fp-furn", d: body }) + line({ class: "sx-fp-furn-line", x1: X(0), y1: Y(0.14), x2: X(1.5), y2: Y(0.14) }) + chairAt(c.px, c.w / 1.5 * 0.75, -0.25, 180);
|
|
36421
|
+
},
|
|
36422
|
+
envelope: [CHAIR_OVERHANG, 0, 0, 0]
|
|
36423
|
+
},
|
|
36424
|
+
"piano-upright": {
|
|
36425
|
+
w: 1.5,
|
|
36426
|
+
h: 0.6,
|
|
36427
|
+
envelope: [0, 0, CHAIR_OVERHANG, 0],
|
|
36428
|
+
draw: (c) => box(c) + line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h * 0.55), x2: c.px(c.w), y2: c.px(c.h * 0.55) }) + chairAt(c.px, c.w / 2, c.h + CHAIR_GAP, 180)
|
|
36429
|
+
},
|
|
36430
|
+
"pool-table": {
|
|
36431
|
+
w: 2.54,
|
|
36432
|
+
h: 1.27,
|
|
36433
|
+
draw: (c) => {
|
|
36434
|
+
const rail = Math.min(0.12, c.h * 0.1);
|
|
36435
|
+
const parts = [
|
|
36436
|
+
box(c, "sx-fp-furn", 0.06),
|
|
36437
|
+
rect({ class: "sx-fp-furn-line", x: c.px(rail), y: c.px(rail), width: c.px(c.w - 2 * rail), height: c.px(c.h - 2 * rail) })
|
|
36438
|
+
];
|
|
36439
|
+
for (const [px0, py0] of [
|
|
36440
|
+
[rail, rail],
|
|
36441
|
+
[c.w / 2, rail],
|
|
36442
|
+
[c.w - rail, rail],
|
|
36443
|
+
[rail, c.h - rail],
|
|
36444
|
+
[c.w / 2, c.h - rail],
|
|
36445
|
+
[c.w - rail, c.h - rail]
|
|
36446
|
+
]) {
|
|
36447
|
+
parts.push(circle({ class: "sx-fp-furn-dot", cx: c.px(px0), cy: c.px(py0), r: c.px(0.045) }));
|
|
36448
|
+
}
|
|
36449
|
+
return parts.join("");
|
|
36450
|
+
}
|
|
36451
|
+
},
|
|
36452
|
+
crib: {
|
|
36453
|
+
w: 0.7,
|
|
36454
|
+
h: 1.3,
|
|
36455
|
+
draw: (c) => {
|
|
36456
|
+
const parts = [box(c, "sx-fp-furn", 0.05), rect({ class: "sx-fp-furn-line", x: c.px(0.08), y: c.px(0.08), width: c.px(c.w - 0.16), height: c.px(c.h - 0.16), rx: c.px(0.04) })];
|
|
36457
|
+
const n = Math.max(3, Math.round(c.h / 0.18));
|
|
36458
|
+
for (let i = 1; i < n; i++) {
|
|
36459
|
+
const y = c.px(0.08 + (c.h - 0.16) * i / n);
|
|
36460
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(0.08), y1: y, x2: c.px(c.w - 0.08), y2: y }));
|
|
36461
|
+
}
|
|
36462
|
+
return parts.join("");
|
|
36463
|
+
}
|
|
36464
|
+
},
|
|
36465
|
+
"bunk-bed": {
|
|
36466
|
+
w: 0.95,
|
|
36467
|
+
h: 2,
|
|
36468
|
+
draw: (c) => bedDraw(1)(c) + line({ class: "sx-fp-furn-dash", x1: c.px(0.07), y1: c.px(0.07), x2: c.px(c.w - 0.07), y2: c.px(0.07) }) + [0.25, 0.45, 0.65].map((f) => line({ class: "sx-fp-furn-line", x1: c.px(c.w - 0.16), y1: c.px(c.h * f), x2: c.px(c.w), y2: c.px(c.h * f) })).join("")
|
|
36469
|
+
},
|
|
36470
|
+
"ceiling-fan": {
|
|
36471
|
+
w: 0.9,
|
|
36472
|
+
h: 0.9,
|
|
36473
|
+
underlay: true,
|
|
36474
|
+
// overhead fixture — never collides with floor furniture
|
|
36475
|
+
draw: (c) => {
|
|
36476
|
+
const r6 = Math.min(c.w, c.h) / 2;
|
|
36477
|
+
const cx = c.w / 2;
|
|
36478
|
+
const cy = c.h / 2;
|
|
36479
|
+
const parts = [
|
|
36480
|
+
circle({ class: "sx-fp-furn-dash", cx: c.px(cx), cy: c.px(cy), r: c.px(r6) }),
|
|
36481
|
+
circle({ class: "sx-fp-furn", cx: c.px(cx), cy: c.px(cy), r: c.px(r6 * 0.18) })
|
|
36482
|
+
];
|
|
36483
|
+
for (const a of [20, 110, 200, 290]) {
|
|
36484
|
+
const rad = a * Math.PI / 180;
|
|
36485
|
+
parts.push(
|
|
36486
|
+
el("ellipse", {
|
|
36487
|
+
class: "sx-fp-furn-line",
|
|
36488
|
+
cx: c.px(cx + r6 * 0.55 * Math.cos(rad)),
|
|
36489
|
+
cy: c.px(cy + r6 * 0.55 * Math.sin(rad)),
|
|
36490
|
+
rx: c.px(r6 * 0.42),
|
|
36491
|
+
ry: c.px(r6 * 0.15),
|
|
36492
|
+
transform: `rotate(${a} ${c.px(cx + r6 * 0.55 * Math.cos(rad))} ${c.px(cy + r6 * 0.55 * Math.sin(rad))})`
|
|
36493
|
+
})
|
|
36494
|
+
);
|
|
36495
|
+
}
|
|
36496
|
+
return parts.join("");
|
|
36497
|
+
}
|
|
36498
|
+
},
|
|
36499
|
+
// ── kitchen / bath ──
|
|
36500
|
+
counter: {
|
|
36501
|
+
w: 2,
|
|
36502
|
+
h: 0.6,
|
|
36503
|
+
underlay: true,
|
|
36504
|
+
draw: (c) => box(c) + line({
|
|
36505
|
+
class: "sx-fp-furn-dash",
|
|
36506
|
+
x1: c.px(0.05),
|
|
36507
|
+
y1: c.px(c.h - 0.06),
|
|
36508
|
+
x2: c.px(c.w - 0.05),
|
|
36509
|
+
y2: c.px(c.h - 0.06)
|
|
36510
|
+
})
|
|
36511
|
+
},
|
|
36512
|
+
"kitchen-sink": {
|
|
36513
|
+
w: 0.8,
|
|
36514
|
+
h: 0.6,
|
|
36515
|
+
draw: (c) => [
|
|
36516
|
+
box(c),
|
|
36517
|
+
rect({ class: "sx-fp-furn-line", x: c.px(0.07), y: c.px(0.1), width: c.px(c.w / 2 - 0.11), height: c.px(c.h - 0.2), rx: c.px(0.04) }),
|
|
36518
|
+
rect({ class: "sx-fp-furn-line", x: c.px(c.w / 2 + 0.04), y: c.px(0.1), width: c.px(c.w / 2 - 0.11), height: c.px(c.h - 0.2), rx: c.px(0.04) }),
|
|
36519
|
+
circle({ class: "sx-fp-furn-dot", cx: c.px(c.w / 2), cy: c.px(0.06), r: c.px(0.024) })
|
|
36520
|
+
].join("")
|
|
36521
|
+
},
|
|
36522
|
+
stove: {
|
|
36523
|
+
w: 0.6,
|
|
36524
|
+
h: 0.6,
|
|
36525
|
+
draw: (c) => {
|
|
36526
|
+
const parts = [box(c)];
|
|
36527
|
+
const inset = 0.18;
|
|
36528
|
+
for (const [bx, by] of [
|
|
36529
|
+
[inset, inset],
|
|
36530
|
+
[c.w - inset, inset],
|
|
36531
|
+
[inset, c.h - inset],
|
|
36532
|
+
[c.w - inset, c.h - inset]
|
|
36533
|
+
]) {
|
|
36534
|
+
parts.push(circle({ class: "sx-fp-furn-line", cx: c.px(bx), cy: c.px(by), r: c.px(0.085) }));
|
|
36535
|
+
}
|
|
36536
|
+
return parts.join("");
|
|
36537
|
+
}
|
|
36538
|
+
},
|
|
36539
|
+
fridge: { w: 0.7, h: 0.7, draw: applianceDraw("REF", false) },
|
|
36540
|
+
dishwasher: { w: 0.6, h: 0.6, draw: applianceDraw("DW", false) },
|
|
36541
|
+
island: {
|
|
36542
|
+
w: 1.8,
|
|
36543
|
+
h: 0.9,
|
|
36544
|
+
underlay: true,
|
|
36545
|
+
draw: (c) => box(c) + rect({ class: "sx-fp-furn-line", x: c.px(0.08), y: c.px(0.08), width: c.px(c.w - 0.16), height: c.px(c.h - 0.16) })
|
|
36546
|
+
},
|
|
36547
|
+
toilet: {
|
|
36548
|
+
w: 0.5,
|
|
36549
|
+
h: 0.7,
|
|
36550
|
+
draw: (c) => [
|
|
36551
|
+
rect({ class: "sx-fp-furn", x: 0, y: 0, width: c.px(c.w), height: c.px(0.2), rx: c.px(0.04) }),
|
|
36552
|
+
el("ellipse", {
|
|
36553
|
+
class: "sx-fp-furn",
|
|
36554
|
+
cx: c.px(c.w / 2),
|
|
36555
|
+
cy: c.px(0.2 + (c.h - 0.24) / 2),
|
|
36556
|
+
rx: c.px(c.w / 2 - 0.04),
|
|
36557
|
+
ry: c.px((c.h - 0.28) / 2)
|
|
36558
|
+
})
|
|
36559
|
+
].join("")
|
|
36560
|
+
},
|
|
36561
|
+
sink: {
|
|
36562
|
+
w: 0.55,
|
|
36563
|
+
h: 0.45,
|
|
36564
|
+
draw: (c) => [
|
|
36565
|
+
box(c, "sx-fp-furn", 0.05),
|
|
36566
|
+
el("ellipse", {
|
|
36567
|
+
class: "sx-fp-furn-line",
|
|
36568
|
+
cx: c.px(c.w / 2),
|
|
36569
|
+
cy: c.px(c.h / 2 + 0.02),
|
|
36570
|
+
rx: c.px(c.w / 2 - 0.08),
|
|
36571
|
+
ry: c.px(c.h / 2 - 0.1)
|
|
36572
|
+
}),
|
|
36573
|
+
circle({ class: "sx-fp-furn-dot", cx: c.px(c.w / 2), cy: c.px(0.07), r: c.px(0.024) })
|
|
36574
|
+
].join("")
|
|
36575
|
+
},
|
|
36576
|
+
bathtub: {
|
|
36577
|
+
w: 0.8,
|
|
36578
|
+
h: 1.7,
|
|
36579
|
+
draw: (c) => [
|
|
36580
|
+
box(c, "sx-fp-furn", 0.08),
|
|
36581
|
+
rect({ class: "sx-fp-furn-line", x: c.px(0.09), y: c.px(0.09), width: c.px(c.w - 0.18), height: c.px(c.h - 0.18), rx: c.px(0.22) }),
|
|
36582
|
+
circle({ class: "sx-fp-furn-dot", cx: c.px(c.w / 2), cy: c.px(0.26), r: c.px(0.035) })
|
|
36583
|
+
].join("")
|
|
36584
|
+
},
|
|
36585
|
+
shower: {
|
|
36586
|
+
w: 0.9,
|
|
36587
|
+
h: 0.9,
|
|
36588
|
+
draw: (c) => [
|
|
36589
|
+
box(c),
|
|
36590
|
+
line({ class: "sx-fp-furn-line", x1: 0, y1: 0, x2: c.px(c.w), y2: c.px(c.h) }),
|
|
36591
|
+
line({ class: "sx-fp-furn-line", x1: c.px(c.w), y1: 0, x2: 0, y2: c.px(c.h) }),
|
|
36592
|
+
circle({ class: "sx-fp-furn", cx: c.px(0.15), cy: c.px(0.15), r: c.px(0.06) })
|
|
36593
|
+
].join("")
|
|
36594
|
+
},
|
|
36595
|
+
washer: { w: 0.6, h: 0.6, draw: applianceDraw("W", true) },
|
|
36596
|
+
dryer: { w: 0.6, h: 0.6, draw: applianceDraw("D", true) },
|
|
36597
|
+
"wall-cabinet": {
|
|
36598
|
+
w: 0.9,
|
|
36599
|
+
h: 0.35,
|
|
36600
|
+
underlay: true,
|
|
36601
|
+
// above the cut plane — drawn dashed over the base run
|
|
36602
|
+
draw: (c) => rect({ class: "sx-fp-furn-dash", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) })
|
|
36603
|
+
},
|
|
36604
|
+
"range-hood": {
|
|
36605
|
+
w: 0.8,
|
|
36606
|
+
h: 0.55,
|
|
36607
|
+
underlay: true,
|
|
36608
|
+
// above the cut plane
|
|
36609
|
+
draw: (c) => el("g", {}, [
|
|
36610
|
+
polygon({
|
|
36611
|
+
class: "sx-fp-furn-dash",
|
|
36612
|
+
points: `${c.px(0.08)},0 ${c.px(c.w - 0.08)},0 ${c.px(c.w)},${c.px(c.h)} 0,${c.px(c.h)}`
|
|
36613
|
+
})
|
|
36614
|
+
])
|
|
36615
|
+
},
|
|
36616
|
+
"bar-stool": {
|
|
36617
|
+
w: 0.35,
|
|
36618
|
+
h: 0.35,
|
|
36619
|
+
draw: (c) => circle({ class: "sx-fp-chair", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(Math.min(c.w, c.h) / 2) })
|
|
36620
|
+
},
|
|
36621
|
+
vanity: {
|
|
36622
|
+
w: 1.5,
|
|
36623
|
+
h: 0.55,
|
|
36624
|
+
draw: (c) => {
|
|
36625
|
+
const parts = [box(c)];
|
|
36626
|
+
for (const fx of [0.27, 0.73]) {
|
|
36627
|
+
parts.push(
|
|
36628
|
+
el("ellipse", {
|
|
36629
|
+
class: "sx-fp-furn-line",
|
|
36630
|
+
cx: c.px(c.w * fx),
|
|
36631
|
+
cy: c.px(c.h / 2 + 0.02),
|
|
36632
|
+
rx: c.px(Math.min(0.22, c.w * 0.16)),
|
|
36633
|
+
ry: c.px(c.h / 2 - 0.12)
|
|
36634
|
+
})
|
|
36635
|
+
);
|
|
36636
|
+
parts.push(circle({ class: "sx-fp-furn-dot", cx: c.px(c.w * fx), cy: c.px(0.07), r: c.px(0.022) }));
|
|
36637
|
+
}
|
|
36638
|
+
return parts.join("");
|
|
36639
|
+
}
|
|
36640
|
+
},
|
|
36641
|
+
bidet: {
|
|
36642
|
+
w: 0.4,
|
|
36643
|
+
h: 0.6,
|
|
36644
|
+
draw: (c) => rect({ class: "sx-fp-furn", x: c.px(c.w * 0.15), y: 0, width: c.px(c.w * 0.7), height: c.px(0.12), rx: c.px(0.03) }) + el("ellipse", {
|
|
36645
|
+
class: "sx-fp-furn",
|
|
36646
|
+
cx: c.px(c.w / 2),
|
|
36647
|
+
cy: c.px(0.12 + (c.h - 0.16) / 2),
|
|
36648
|
+
rx: c.px(c.w / 2 - 0.03),
|
|
36649
|
+
ry: c.px((c.h - 0.18) / 2)
|
|
36650
|
+
}) + circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h * 0.45), r: c.px(0.035) })
|
|
36651
|
+
},
|
|
36652
|
+
urinal: {
|
|
36653
|
+
w: 0.4,
|
|
36654
|
+
h: 0.35,
|
|
36655
|
+
draw: (c) => rect({ class: "sx-fp-furn", x: 0, y: 0, width: c.px(c.w), height: c.px(0.08) }) + path({
|
|
36656
|
+
class: "sx-fp-furn",
|
|
36657
|
+
d: `M ${c.px(0.05)} ${c.px(0.08)} L ${c.px(c.w - 0.05)} ${c.px(0.08)} C ${c.px(c.w - 0.05)} ${c.px(c.h * 0.75)} ${c.px(c.w * 0.7)} ${c.px(c.h)} ${c.px(c.w / 2)} ${c.px(c.h)} C ${c.px(c.w * 0.3)} ${c.px(c.h)} ${c.px(0.05)} ${c.px(c.h * 0.75)} ${c.px(0.05)} ${c.px(0.08)} Z`
|
|
36658
|
+
})
|
|
36659
|
+
},
|
|
36660
|
+
// ── stairs & vertical circulation ──
|
|
36661
|
+
stairs: { w: 1, h: 3, consumesLabel: true, draw: straightStairs },
|
|
36662
|
+
"stairs-l": { w: 2.2, h: 2.2, consumesLabel: true, draw: lStairs },
|
|
36663
|
+
"stairs-u": { w: 2, h: 3, consumesLabel: true, draw: uStairs },
|
|
36664
|
+
"spiral-stairs": { w: 1.5, h: 1.5, consumesLabel: true, draw: spiralStairs },
|
|
36665
|
+
elevator: {
|
|
36666
|
+
w: 1.6,
|
|
36667
|
+
h: 1.5,
|
|
36668
|
+
draw: (c) => box(c) + line({ class: "sx-fp-furn-line", x1: 0, y1: 0, x2: c.px(c.w), y2: c.px(c.h) }) + line({ class: "sx-fp-furn-line", x1: c.px(c.w), y1: 0, x2: 0, y2: c.px(c.h) })
|
|
36669
|
+
},
|
|
36670
|
+
// ── structural ──
|
|
36671
|
+
column: {
|
|
36672
|
+
w: 0.4,
|
|
36673
|
+
h: 0.4,
|
|
36674
|
+
draw: (c) => rect({ class: "sx-fp-furn-solid", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) })
|
|
36675
|
+
},
|
|
36676
|
+
// ── classroom / office ──
|
|
36677
|
+
"desk-chair": {
|
|
36678
|
+
w: 0.6,
|
|
36679
|
+
h: 0.75,
|
|
36680
|
+
draw: (c) => [
|
|
36681
|
+
rect({ class: "sx-fp-furn", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h * 0.58) }),
|
|
36682
|
+
rect({
|
|
36683
|
+
class: "sx-fp-chair",
|
|
36684
|
+
x: c.px(c.w / 2 - c.w * 0.3),
|
|
36685
|
+
y: c.px(c.h * 0.66),
|
|
36686
|
+
width: c.px(c.w * 0.6),
|
|
36687
|
+
height: c.px(c.h * 0.32),
|
|
36688
|
+
rx: c.px(0.07)
|
|
36689
|
+
})
|
|
36690
|
+
].join("")
|
|
36691
|
+
},
|
|
36692
|
+
desk: {
|
|
36693
|
+
w: 1.4,
|
|
36694
|
+
h: 0.7,
|
|
36695
|
+
envelope: [0, 0, CHAIR_OVERHANG, 0],
|
|
36696
|
+
draw: (c) => box(c) + chairAt(c.px, c.w / 2, c.h + CHAIR_GAP, 180)
|
|
36697
|
+
},
|
|
36698
|
+
chair: {
|
|
36699
|
+
w: 0.45,
|
|
36700
|
+
h: 0.45,
|
|
36701
|
+
draw: (c) => rect({ class: "sx-fp-chair", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h), rx: c.px(0.1) }) + line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(0.06), x2: 0, y2: c.px(c.h - 0.06) })
|
|
36702
|
+
},
|
|
36703
|
+
whiteboard: { w: 3, h: 0.12, draw: boardDraw },
|
|
36704
|
+
smartboard: { w: 2, h: 0.12, draw: boardDraw },
|
|
36705
|
+
bookcase: { w: 0.9, h: 0.3, draw: shelfDraw },
|
|
36706
|
+
"desk-l": {
|
|
36707
|
+
w: 1.6,
|
|
36708
|
+
h: 1.6,
|
|
36709
|
+
draw: (c) => {
|
|
36710
|
+
const d = 0.7 * Math.min(c.w / 1.6, c.h / 1.6);
|
|
36711
|
+
return path({
|
|
36712
|
+
class: "sx-fp-furn",
|
|
36713
|
+
d: `M 0 0 L ${c.px(c.w)} 0 L ${c.px(c.w)} ${c.px(d)} L ${c.px(d)} ${c.px(d)} L ${c.px(d)} ${c.px(c.h)} L 0 ${c.px(c.h)} Z`
|
|
36714
|
+
}) + chairAt(c.px, d + 0.32, d + 0.32, 315);
|
|
36715
|
+
}
|
|
36716
|
+
},
|
|
36717
|
+
"filing-cabinet": {
|
|
36718
|
+
w: 0.5,
|
|
36719
|
+
h: 0.6,
|
|
36720
|
+
draw: (c) => box(c) + line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h / 3), x2: c.px(c.w), y2: c.px(c.h / 3) }) + line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(2 * c.h / 3), x2: c.px(c.w), y2: c.px(2 * c.h / 3) })
|
|
36721
|
+
},
|
|
36722
|
+
lockers: {
|
|
36723
|
+
w: 1.8,
|
|
36724
|
+
h: 0.45,
|
|
36725
|
+
draw: (c) => {
|
|
36726
|
+
const parts = [box(c)];
|
|
36727
|
+
const n = Math.max(2, Math.round(c.w / 0.3));
|
|
36728
|
+
for (let i = 1; i < n; i++) {
|
|
36729
|
+
const x = c.px(c.w * i / n);
|
|
36730
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: 0, x2: x, y2: c.px(c.h) }));
|
|
36731
|
+
}
|
|
36732
|
+
for (let i = 0; i < n; i++) {
|
|
36733
|
+
parts.push(circle({ class: "sx-fp-furn-dot", cx: c.px((i + 0.78) / n * c.w), cy: c.px(c.h / 2), r: c.px(0.02) }));
|
|
36734
|
+
}
|
|
36735
|
+
return parts.join("");
|
|
36736
|
+
}
|
|
36737
|
+
},
|
|
36738
|
+
cubbies: {
|
|
36739
|
+
w: 2,
|
|
36740
|
+
h: 0.4,
|
|
36741
|
+
draw: (c) => {
|
|
36742
|
+
const parts = [box(c)];
|
|
36743
|
+
const n = Math.max(2, Math.round(c.w / 0.3));
|
|
36744
|
+
for (let i = 1; i < n; i++) {
|
|
36745
|
+
const x = c.px(c.w * i / n);
|
|
36746
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: 0, x2: x, y2: c.px(c.h) }));
|
|
36747
|
+
}
|
|
36748
|
+
return parts.join("");
|
|
36749
|
+
}
|
|
36750
|
+
},
|
|
36751
|
+
"kidney-table": {
|
|
36752
|
+
w: 1.8,
|
|
36753
|
+
h: 1.2,
|
|
36754
|
+
draw: (c) => {
|
|
36755
|
+
const sx = c.w / 1.8;
|
|
36756
|
+
const sy = c.h / 1.2;
|
|
36757
|
+
const X = (v) => c.px(v * sx);
|
|
36758
|
+
const Y = (v) => c.px(v * sy);
|
|
36759
|
+
const d = [
|
|
36760
|
+
`M ${X(0)} ${Y(0.55)}`,
|
|
36761
|
+
`C ${X(0)} ${Y(0.15)} ${X(0.35)} ${Y(0)} ${X(0.9)} ${Y(0)}`,
|
|
36762
|
+
`C ${X(1.45)} ${Y(0)} ${X(1.8)} ${Y(0.15)} ${X(1.8)} ${Y(0.55)}`,
|
|
36763
|
+
`C ${X(1.8)} ${Y(0.95)} ${X(1.5)} ${Y(1.2)} ${X(1.18)} ${Y(1.2)}`,
|
|
36764
|
+
`Q ${X(0.9)} ${Y(0.82)} ${X(0.62)} ${Y(1.2)}`,
|
|
36765
|
+
`C ${X(0.3)} ${Y(1.2)} ${X(0)} ${Y(0.95)} ${X(0)} ${Y(0.55)}`,
|
|
36766
|
+
"Z"
|
|
36767
|
+
].join(" ");
|
|
36768
|
+
return path({ class: "sx-fp-furn", d });
|
|
36769
|
+
}
|
|
36770
|
+
},
|
|
36771
|
+
"round-table-4": roundTable(4, 1.52),
|
|
36772
|
+
"round-table-6": roundTable(6, 1.52),
|
|
36773
|
+
"round-table-8": roundTable(8, 1.52),
|
|
36774
|
+
"round-table-10": roundTable(10, 1.83),
|
|
36775
|
+
"conference-table": { w: 2.4, h: 1.2, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true) },
|
|
36776
|
+
// ── event / banquet ──
|
|
36777
|
+
"banquet-table": { w: 2.44, h: 0.76, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true) },
|
|
36778
|
+
"head-table": { w: 3.7, h: 0.76, envelope: [0, 0, CHAIR_OVERHANG, 0], draw: tableDraw(false) },
|
|
36779
|
+
stage: {
|
|
36780
|
+
w: 4,
|
|
36781
|
+
h: 2,
|
|
36782
|
+
draw: (c) => box(c) + rect({
|
|
36783
|
+
class: "sx-fp-furn-dash",
|
|
36784
|
+
x: c.px(0.1),
|
|
36785
|
+
y: c.px(0.1),
|
|
36786
|
+
width: c.px(c.w - 0.2),
|
|
36787
|
+
height: c.px(c.h - 0.2)
|
|
36788
|
+
})
|
|
36789
|
+
},
|
|
36790
|
+
"dance-floor": {
|
|
36791
|
+
w: 4,
|
|
36792
|
+
h: 4,
|
|
36793
|
+
underlay: true,
|
|
36794
|
+
draw: (c) => {
|
|
36795
|
+
const parts = [];
|
|
36796
|
+
for (let d = 0.5; d < c.w + c.h; d += 0.5) {
|
|
36797
|
+
const x1 = Math.max(0, d - c.h);
|
|
36798
|
+
const y1 = Math.min(d, c.h);
|
|
36799
|
+
const x2 = Math.min(d, c.w);
|
|
36800
|
+
const y2 = Math.max(0, d - c.w);
|
|
36801
|
+
parts.push(line({ class: "sx-fp-hatch", x1: c.px(x1), y1: c.px(y1), x2: c.px(x2), y2: c.px(y2) }));
|
|
36802
|
+
}
|
|
36803
|
+
parts.push(rect({ class: "sx-fp-furn-nofill", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) }));
|
|
36804
|
+
return parts.join("");
|
|
36805
|
+
}
|
|
36806
|
+
},
|
|
36807
|
+
bar: {
|
|
36808
|
+
w: 3,
|
|
36809
|
+
h: 0.7,
|
|
36810
|
+
draw: (c) => box(c) + line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h - 0.15), x2: c.px(c.w), y2: c.px(c.h - 0.15) })
|
|
36811
|
+
},
|
|
36812
|
+
"dj-booth": { w: 1.2, h: 0.8, draw: (c) => box(c) + glyphText(c, "DJ") },
|
|
36813
|
+
"cocktail-table": {
|
|
36814
|
+
w: 0.76,
|
|
36815
|
+
h: 0.76,
|
|
36816
|
+
draw: (c) => circle({ class: "sx-fp-furn", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(Math.min(c.w, c.h) / 2) }) + circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(Math.min(c.w, c.h) / 6) })
|
|
36817
|
+
},
|
|
36818
|
+
podium: {
|
|
36819
|
+
w: 0.6,
|
|
36820
|
+
h: 0.5,
|
|
36821
|
+
draw: (c) => polygon({
|
|
36822
|
+
class: "sx-fp-furn",
|
|
36823
|
+
points: `${c.px(c.w * 0.15)},0 ${c.px(c.w * 0.85)},0 ${c.px(c.w)},${c.px(c.h)} 0,${c.px(c.h)}`
|
|
36824
|
+
})
|
|
36825
|
+
},
|
|
36826
|
+
"row-chairs": {
|
|
36827
|
+
w: 2.2,
|
|
36828
|
+
h: 0.5,
|
|
36829
|
+
draw: (c) => {
|
|
36830
|
+
const n = Math.max(1, Math.floor(c.w / 0.55 + 1e-6));
|
|
36831
|
+
const parts = [];
|
|
36832
|
+
for (let i = 0; i < n; i++) {
|
|
36833
|
+
parts.push(chairAt(c.px, (i + 0.5) / n * c.w, c.h / 2, 0));
|
|
36834
|
+
}
|
|
36835
|
+
return parts.join("");
|
|
36836
|
+
}
|
|
36837
|
+
},
|
|
36838
|
+
// ── retail ──
|
|
36839
|
+
// Gondola run: a long fixture with a back-to-back spine and product bays.
|
|
36840
|
+
shelving: {
|
|
36841
|
+
w: 1.8,
|
|
36842
|
+
h: 0.6,
|
|
36843
|
+
draw: (c) => {
|
|
36844
|
+
const parts = [box(c)];
|
|
36845
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h / 2), x2: c.px(c.w), y2: c.px(c.h / 2) }));
|
|
36846
|
+
const n = Math.max(2, Math.round(c.w / 0.45));
|
|
36847
|
+
for (let i = 1; i < n; i++) {
|
|
36848
|
+
const x = c.px(c.w * i / n);
|
|
36849
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: 0, x2: x, y2: c.px(c.h) }));
|
|
36850
|
+
}
|
|
36851
|
+
return parts.join("");
|
|
36852
|
+
}
|
|
36853
|
+
},
|
|
36854
|
+
// POS counter with a register block and a belt line.
|
|
36855
|
+
checkout: {
|
|
36856
|
+
w: 1.6,
|
|
36857
|
+
h: 0.7,
|
|
36858
|
+
draw: (c) => [
|
|
36859
|
+
box(c),
|
|
36860
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(c.w - 0.5), y: c.px(0.12), width: c.px(0.34), height: c.px(0.3), rx: c.px(0.04) }),
|
|
36861
|
+
line({ class: "sx-fp-furn-line", x1: c.px(0.12), y1: c.px(c.h * 0.6), x2: c.px(c.w - 0.62), y2: c.px(c.h * 0.6) })
|
|
36862
|
+
].join("")
|
|
36863
|
+
},
|
|
36864
|
+
// Round garment rack: rail circle with radial hanger ticks.
|
|
36865
|
+
"clothing-rack": {
|
|
36866
|
+
w: 1,
|
|
36867
|
+
h: 1,
|
|
36868
|
+
draw: (c) => {
|
|
36869
|
+
const r6 = Math.min(c.w, c.h) / 2;
|
|
36870
|
+
const cx = c.w / 2;
|
|
36871
|
+
const cy = c.h / 2;
|
|
36872
|
+
const parts = [
|
|
36873
|
+
circle({ class: "sx-fp-furn-nofill", cx: c.px(cx), cy: c.px(cy), r: c.px(r6) }),
|
|
36874
|
+
circle({ class: "sx-fp-furn-dot", cx: c.px(cx), cy: c.px(cy), r: c.px(0.04) })
|
|
36875
|
+
];
|
|
36876
|
+
for (const a of [0, 45, 90, 135, 180, 225, 270, 315]) {
|
|
36877
|
+
const rad = a * Math.PI / 180;
|
|
36878
|
+
parts.push(
|
|
36879
|
+
line({
|
|
36880
|
+
class: "sx-fp-furn-line",
|
|
36881
|
+
x1: c.px(cx + (r6 - 0.09) * Math.cos(rad)),
|
|
36882
|
+
y1: c.px(cy + (r6 - 0.09) * Math.sin(rad)),
|
|
36883
|
+
x2: c.px(cx + r6 * Math.cos(rad)),
|
|
36884
|
+
y2: c.px(cy + r6 * Math.sin(rad))
|
|
36885
|
+
})
|
|
36886
|
+
);
|
|
36887
|
+
}
|
|
36888
|
+
return parts.join("");
|
|
36889
|
+
}
|
|
36890
|
+
},
|
|
36891
|
+
// Changing booth: bench at the back, a mirror strip, a dashed curtain at the opening.
|
|
36892
|
+
"fitting-room": {
|
|
36893
|
+
w: 1.1,
|
|
36894
|
+
h: 1.1,
|
|
36895
|
+
draw: (c) => [
|
|
36896
|
+
rect({ class: "sx-fp-furn-nofill", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) }),
|
|
36897
|
+
rect({ class: "sx-fp-furn", x: c.px(0.12), y: c.px(0.12), width: c.px(c.w - 0.24), height: c.px(0.28), rx: c.px(0.04) }),
|
|
36898
|
+
line({ class: "sx-fp-furn-dash", x1: 0, y1: c.px(c.h), x2: c.px(c.w), y2: c.px(c.h) }),
|
|
36899
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(c.w - 0.06), y: c.px(c.h * 0.5), width: c.px(0.04), height: c.px(c.h * 0.35) })
|
|
36900
|
+
].join("")
|
|
36901
|
+
},
|
|
36902
|
+
// ── warehouse / industrial ──
|
|
36903
|
+
// Pallet racking: an open frame with bay dividers and cross-bracing.
|
|
36904
|
+
"pallet-rack": {
|
|
36905
|
+
w: 2.7,
|
|
36906
|
+
h: 1.1,
|
|
36907
|
+
draw: (c) => {
|
|
36908
|
+
const parts = [rect({ class: "sx-fp-furn-nofill", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) })];
|
|
36909
|
+
const bays = Math.max(2, Math.round(c.w / 1.35));
|
|
36910
|
+
for (let i = 0; i < bays; i++) {
|
|
36911
|
+
const x0 = c.w * i / bays;
|
|
36912
|
+
const x1 = c.w * (i + 1) / bays;
|
|
36913
|
+
if (i > 0) parts.push(line({ class: "sx-fp-furn-line", x1: c.px(x0), y1: 0, x2: c.px(x0), y2: c.px(c.h) }));
|
|
36914
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(x0), y1: 0, x2: c.px(x1), y2: c.px(c.h) }));
|
|
36915
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(x1), y1: 0, x2: c.px(x0), y2: c.px(c.h) }));
|
|
36916
|
+
}
|
|
36917
|
+
return parts.join("");
|
|
36918
|
+
}
|
|
36919
|
+
},
|
|
36920
|
+
// Dock door: roll-up door segments with two bumpers at the outer face.
|
|
36921
|
+
"loading-dock": {
|
|
36922
|
+
w: 3,
|
|
36923
|
+
h: 0.6,
|
|
36924
|
+
draw: (c) => {
|
|
36925
|
+
const parts = [box(c)];
|
|
36926
|
+
const n = Math.max(3, Math.round(c.w / 0.5));
|
|
36927
|
+
for (let i = 1; i < n; i++) {
|
|
36928
|
+
const x = c.px(c.w * i / n);
|
|
36929
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: x, y1: 0, x2: x, y2: c.px(c.h) }));
|
|
36930
|
+
}
|
|
36931
|
+
parts.push(rect({ class: "sx-fp-furn-solid", x: c.px(0.12), y: c.px(c.h - 0.12), width: c.px(0.3), height: c.px(0.1) }));
|
|
36932
|
+
parts.push(rect({ class: "sx-fp-furn-solid", x: c.px(c.w - 0.42), y: c.px(c.h - 0.12), width: c.px(0.3), height: c.px(0.1) }));
|
|
36933
|
+
return parts.join("");
|
|
36934
|
+
}
|
|
36935
|
+
},
|
|
36936
|
+
// Counterbalance forklift silhouette: body, mast forks at the front, operator seat.
|
|
36937
|
+
forklift: {
|
|
36938
|
+
w: 1.2,
|
|
36939
|
+
h: 2.2,
|
|
36940
|
+
draw: (c) => [
|
|
36941
|
+
rect({ class: "sx-fp-furn", x: c.px(0.15), y: c.px(0.42), width: c.px(c.w - 0.3), height: c.px(c.h - 0.72), rx: c.px(0.06) }),
|
|
36942
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(0.25), y: 0, width: c.px(0.12), height: c.px(0.42) }),
|
|
36943
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(c.w - 0.37), y: 0, width: c.px(0.12), height: c.px(0.42) }),
|
|
36944
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h - 0.62), r: c.px(0.18) })
|
|
36945
|
+
].join("")
|
|
36946
|
+
},
|
|
36947
|
+
// ── salon / spa ──
|
|
36948
|
+
// Styling station: back counter with a mirror strip and a chair facing it.
|
|
36949
|
+
"salon-chair": {
|
|
36950
|
+
w: 0.8,
|
|
36951
|
+
h: 1.4,
|
|
36952
|
+
draw: (c) => [
|
|
36953
|
+
rect({ class: "sx-fp-furn", x: 0, y: 0, width: c.px(c.w), height: c.px(0.32), rx: c.px(0.03) }),
|
|
36954
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(0.08), y: c.px(0.05), width: c.px(c.w - 0.16), height: c.px(0.05) }),
|
|
36955
|
+
circle({ class: "sx-fp-furn", cx: c.px(c.w / 2), cy: c.px(c.h * 0.64), r: c.px(Math.min(c.w, 0.62) / 2) }),
|
|
36956
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h * 0.64), r: c.px(0.08) })
|
|
36957
|
+
].join("")
|
|
36958
|
+
},
|
|
36959
|
+
// Backwash unit: a reclining chair with a wash basin at the head end.
|
|
36960
|
+
"shampoo-bowl": {
|
|
36961
|
+
w: 0.9,
|
|
36962
|
+
h: 1.5,
|
|
36963
|
+
draw: (c) => [
|
|
36964
|
+
rect({ class: "sx-fp-furn", x: c.px(0.1), y: c.px(0.5), width: c.px(c.w - 0.2), height: c.px(c.h - 0.6), rx: c.px(0.08) }),
|
|
36965
|
+
circle({ class: "sx-fp-furn", cx: c.px(c.w / 2), cy: c.px(0.4), r: c.px(0.32) }),
|
|
36966
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(0.4), r: c.px(0.12) }),
|
|
36967
|
+
circle({ class: "sx-fp-furn-dot", cx: c.px(c.w / 2), cy: c.px(0.12), r: c.px(0.04) })
|
|
36968
|
+
].join("")
|
|
36969
|
+
},
|
|
36970
|
+
// Manicure table: a small table with a client and a technician chair.
|
|
36971
|
+
"manicure-table": {
|
|
36972
|
+
w: 1,
|
|
36973
|
+
h: 0.5,
|
|
36974
|
+
envelope: [CHAIR_GAP + CHAIR_D, 0, CHAIR_GAP + CHAIR_D, 0],
|
|
36975
|
+
draw: (c) => [box(c), chairAt(c.px, c.w / 2, -CHAIR_GAP, 0), chairAt(c.px, c.w / 2, c.h + CHAIR_GAP, 180)].join("")
|
|
36976
|
+
},
|
|
36977
|
+
// ── gym / fitness ──
|
|
36978
|
+
// Treadmill: a deck with a running belt and a console at the front.
|
|
36979
|
+
treadmill: {
|
|
36980
|
+
w: 0.9,
|
|
36981
|
+
h: 2,
|
|
36982
|
+
draw: (c) => [
|
|
36983
|
+
box(c, "sx-fp-furn", 0.05),
|
|
36984
|
+
rect({ class: "sx-fp-furn-line", x: c.px(0.12), y: c.px(0.5), width: c.px(c.w - 0.24), height: c.px(c.h - 0.65), rx: c.px(0.04) }),
|
|
36985
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(0.1), y: c.px(0.08), width: c.px(c.w - 0.2), height: c.px(0.16), rx: c.px(0.03) })
|
|
36986
|
+
].join("")
|
|
36987
|
+
},
|
|
36988
|
+
// Flat bench with upright posts and a loaded barbell crossing it.
|
|
36989
|
+
"weight-bench": {
|
|
36990
|
+
w: 0.6,
|
|
36991
|
+
h: 1.8,
|
|
36992
|
+
envelope: [0, 0.35, 0, 0.35],
|
|
36993
|
+
draw: (c) => [
|
|
36994
|
+
rect({ class: "sx-fp-furn", x: c.px(c.w / 2 - 0.12), y: c.px(0.3), width: c.px(0.24), height: c.px(c.h - 0.4), rx: c.px(0.05) }),
|
|
36995
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(0.06), y: c.px(0.18), width: c.px(0.1), height: c.px(0.1) }),
|
|
36996
|
+
rect({ class: "sx-fp-furn-solid", x: c.px(c.w - 0.16), y: c.px(0.18), width: c.px(0.1), height: c.px(0.1) }),
|
|
36997
|
+
line({ class: "sx-fp-furn-line", x1: c.px(-0.3), y1: c.px(0.23), x2: c.px(c.w + 0.3), y2: c.px(0.23) })
|
|
36998
|
+
].join("")
|
|
36999
|
+
},
|
|
37000
|
+
// Power rack: a square frame with four corner posts and a barbell.
|
|
37001
|
+
"power-rack": {
|
|
37002
|
+
w: 1.4,
|
|
37003
|
+
h: 1.4,
|
|
37004
|
+
envelope: [0, 0.3, 0, 0.3],
|
|
37005
|
+
draw: (c) => {
|
|
37006
|
+
const post = 0.14;
|
|
37007
|
+
const corners = [
|
|
37008
|
+
[0, 0],
|
|
37009
|
+
[c.w - post, 0],
|
|
37010
|
+
[0, c.h - post],
|
|
37011
|
+
[c.w - post, c.h - post]
|
|
37012
|
+
];
|
|
37013
|
+
const parts = [rect({ class: "sx-fp-furn-nofill", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h) })];
|
|
37014
|
+
for (const [px0, py0] of corners) {
|
|
37015
|
+
parts.push(rect({ class: "sx-fp-furn-solid", x: c.px(px0), y: c.px(py0), width: c.px(post), height: c.px(post) }));
|
|
37016
|
+
}
|
|
37017
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(-0.28), y1: c.px(c.h * 0.4), x2: c.px(c.w + 0.28), y2: c.px(c.h * 0.4) }));
|
|
37018
|
+
return parts.join("");
|
|
37019
|
+
}
|
|
37020
|
+
},
|
|
37021
|
+
// Exercise mat — an underlay surface, like a rug.
|
|
37022
|
+
"yoga-mat": {
|
|
37023
|
+
w: 0.6,
|
|
37024
|
+
h: 1.8,
|
|
37025
|
+
underlay: true,
|
|
37026
|
+
draw: (c) => rect({ class: "sx-fp-furn-dash", x: 0, y: 0, width: c.px(c.w), height: c.px(c.h), rx: c.px(0.08) })
|
|
37027
|
+
},
|
|
37028
|
+
// ── site / outdoor ──
|
|
37029
|
+
// Tree in plan: a canopy disc with a foliage ring and a trunk dot.
|
|
37030
|
+
tree: {
|
|
37031
|
+
w: 2,
|
|
37032
|
+
h: 2,
|
|
37033
|
+
draw: (c) => {
|
|
37034
|
+
const r6 = Math.min(c.w, c.h) / 2;
|
|
37035
|
+
return [
|
|
37036
|
+
circle({ class: "sx-fp-furn", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(r6) }),
|
|
37037
|
+
circle({ class: "sx-fp-furn-line", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(r6 * 0.6) }),
|
|
37038
|
+
circle({ class: "sx-fp-furn-dot", cx: c.px(c.w / 2), cy: c.px(c.h / 2), r: c.px(0.07) })
|
|
37039
|
+
].join("");
|
|
37040
|
+
}
|
|
37041
|
+
},
|
|
37042
|
+
// Car in plan (parking-stall footprint): body, glazing lines, four wheels.
|
|
37043
|
+
car: {
|
|
37044
|
+
w: 1.8,
|
|
37045
|
+
h: 4.4,
|
|
37046
|
+
draw: (c) => {
|
|
37047
|
+
const parts = [rect({ class: "sx-fp-furn", x: c.px(0.12), y: c.px(0.1), width: c.px(c.w - 0.24), height: c.px(c.h - 0.2), rx: c.px(0.35) })];
|
|
37048
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(0.3), y1: c.px(c.h * 0.26), x2: c.px(c.w - 0.3), y2: c.px(c.h * 0.26) }));
|
|
37049
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: c.px(0.3), y1: c.px(c.h * 0.72), x2: c.px(c.w - 0.3), y2: c.px(c.h * 0.72) }));
|
|
37050
|
+
for (const wy of [c.h * 0.3, c.h * 0.7]) {
|
|
37051
|
+
parts.push(rect({ class: "sx-fp-furn-solid", x: 0, y: c.px(wy - 0.18), width: c.px(0.14), height: c.px(0.36), rx: c.px(0.03) }));
|
|
37052
|
+
parts.push(rect({ class: "sx-fp-furn-solid", x: c.px(c.w - 0.14), y: c.px(wy - 0.18), width: c.px(0.14), height: c.px(0.36), rx: c.px(0.03) }));
|
|
37053
|
+
}
|
|
37054
|
+
return parts.join("");
|
|
37055
|
+
}
|
|
37056
|
+
}
|
|
37057
|
+
};
|
|
37058
|
+
var FURNITURE_TYPES = Object.keys(FLOORPLAN_SYMBOLS);
|
|
37059
|
+
|
|
37060
|
+
// src/diagrams/floorplan/parser.ts
|
|
37061
|
+
var FloorplanParseError = class extends Error {
|
|
37062
|
+
line;
|
|
37063
|
+
constructor(message, line2) {
|
|
37064
|
+
super(`line ${line2}: ${message}`);
|
|
37065
|
+
this.name = "FloorplanParseError";
|
|
37066
|
+
this.line = line2;
|
|
37067
|
+
}
|
|
37068
|
+
};
|
|
37069
|
+
var isStr = (t) => t !== void 0 && "str" in t;
|
|
37070
|
+
var isWord = (t, w) => t !== void 0 && "word" in t && (w === void 0 || t.word === w);
|
|
37071
|
+
function normalizeQuotes3(line2) {
|
|
37072
|
+
return line2.replace(/[“”「」『』]/g, '"').replace(/[‘’]/g, "'");
|
|
37073
|
+
}
|
|
37074
|
+
function tokenize7(line2) {
|
|
37075
|
+
const out = [];
|
|
37076
|
+
const re = /"([^"]*)"|(\S+)/g;
|
|
37077
|
+
let m;
|
|
37078
|
+
while (m = re.exec(line2)) {
|
|
37079
|
+
if (m[1] !== void 0) out.push({ str: m[1] });
|
|
37080
|
+
else out.push({ word: m[2] });
|
|
37081
|
+
}
|
|
37082
|
+
return out;
|
|
37083
|
+
}
|
|
37084
|
+
function parseNum(t, what, ln) {
|
|
37085
|
+
if (!isWord(t)) throw new FloorplanParseError(`expected a number for ${what}`, ln);
|
|
37086
|
+
const v = Number(t.word);
|
|
37087
|
+
if (!Number.isFinite(v)) throw new FloorplanParseError(`expected a number for ${what}, got "${t.word}"`, ln);
|
|
37088
|
+
return v;
|
|
37089
|
+
}
|
|
37090
|
+
function parseCoord2(t, what, ln) {
|
|
37091
|
+
if (!isWord(t)) throw new FloorplanParseError(`expected "x,y" for ${what}`, ln);
|
|
37092
|
+
const m = /^(-?\d*\.?\d+),(-?\d*\.?\d+)$/.exec(t.word);
|
|
37093
|
+
if (!m) throw new FloorplanParseError(`expected "x,y" for ${what}, got "${t.word}"`, ln);
|
|
37094
|
+
return { x: Number(m[1]), y: Number(m[2]) };
|
|
37095
|
+
}
|
|
37096
|
+
function parseDims(t, what, ln) {
|
|
37097
|
+
if (!isWord(t)) throw new FloorplanParseError(`expected "WxH" for ${what}`, ln);
|
|
37098
|
+
const m = /^(\d*\.?\d+)x(\d*\.?\d+)$/i.exec(t.word);
|
|
37099
|
+
if (!m) throw new FloorplanParseError(`expected "WxH" for ${what}, got "${t.word}"`, ln);
|
|
37100
|
+
return { w: Number(m[1]), h: Number(m[2]) };
|
|
37101
|
+
}
|
|
37102
|
+
function parsePct(t, ln) {
|
|
37103
|
+
if (!isWord(t)) throw new FloorplanParseError(`expected a percentage for "at"`, ln);
|
|
37104
|
+
const v = Number(t.word.replace(/%$/, ""));
|
|
37105
|
+
if (!Number.isFinite(v)) throw new FloorplanParseError(`expected a percentage for "at", got "${t.word}"`, ln);
|
|
37106
|
+
return v;
|
|
37107
|
+
}
|
|
37108
|
+
function parseId(t, what, ln) {
|
|
37109
|
+
if (!isWord(t)) throw new FloorplanParseError(`expected ${what}`, ln);
|
|
37110
|
+
return t.word;
|
|
37111
|
+
}
|
|
37112
|
+
var SIDES = ["north", "south", "east", "west"];
|
|
37113
|
+
var REL_HOW = ["right-of", "left-of", "above", "below"];
|
|
37114
|
+
function parseFurnitureType(t, ln) {
|
|
37115
|
+
const word = isWord(t) ? t.word : "";
|
|
37116
|
+
if (!FURNITURE_TYPES.includes(word)) {
|
|
37117
|
+
throw new FloorplanParseError(
|
|
37118
|
+
`unknown furniture type "${word}". Valid types: ${FURNITURE_TYPES.join(", ")}`,
|
|
37119
|
+
ln
|
|
37120
|
+
);
|
|
37121
|
+
}
|
|
37122
|
+
return word;
|
|
37123
|
+
}
|
|
37124
|
+
function parseHeader3(tok, ast, ln) {
|
|
37125
|
+
while (tok.length) {
|
|
37126
|
+
const t = tok.shift();
|
|
37127
|
+
if (isStr(t)) ast.title = t.str;
|
|
37128
|
+
else if (t.word === "unit") {
|
|
37129
|
+
const u = parseId(tok.shift(), "unit (m|ft)", ln);
|
|
37130
|
+
if (u !== "m" && u !== "ft") throw new FloorplanParseError(`unit must be "m" or "ft", got "${u}"`, ln);
|
|
37131
|
+
ast.unit = u;
|
|
37132
|
+
} else throw new FloorplanParseError(`floorplan: unexpected token "${t.word}"`, ln);
|
|
37133
|
+
}
|
|
37134
|
+
}
|
|
37135
|
+
function parseRoom(tok, ast, ln) {
|
|
37136
|
+
const id = parseId(tok.shift(), "a room id", ln);
|
|
37137
|
+
if (ast.rooms.some((r6) => r6.id === id)) {
|
|
37138
|
+
throw new FloorplanParseError(`duplicate room id "${id}"`, ln);
|
|
37139
|
+
}
|
|
37140
|
+
const room = { id, label: id, w: 4, h: 3, line: ln };
|
|
37141
|
+
while (tok.length) {
|
|
37142
|
+
const t = tok.shift();
|
|
37143
|
+
if (isStr(t)) room.label = t.str;
|
|
37144
|
+
else if (t.word === "at") room.at = parseCoord2(tok.shift(), "at", ln);
|
|
37145
|
+
else if (REL_HOW.includes(t.word)) {
|
|
37146
|
+
room.rel = {
|
|
37147
|
+
how: t.word,
|
|
37148
|
+
ref: parseId(tok.shift(), `a room id after "${t.word}"`, ln),
|
|
37149
|
+
offset: void 0,
|
|
37150
|
+
align: void 0
|
|
37151
|
+
};
|
|
37152
|
+
} else if (t.word === "offset") {
|
|
37153
|
+
if (!room.rel) throw new FloorplanParseError(`"offset" requires a relative placement (right-of/left-of/above/below)`, ln);
|
|
37154
|
+
room.rel.offset = parseNum(tok.shift(), "offset", ln);
|
|
37155
|
+
} else if (t.word === "align") {
|
|
37156
|
+
if (!room.rel) throw new FloorplanParseError(`"align" requires a relative placement (right-of/left-of/above/below)`, ln);
|
|
37157
|
+
const a = parseId(tok.shift(), "align (start|center|end)", ln);
|
|
37158
|
+
if (a !== "start" && a !== "center" && a !== "end") {
|
|
37159
|
+
throw new FloorplanParseError(`align must be start|center|end, got "${a}"`, ln);
|
|
37160
|
+
}
|
|
37161
|
+
room.rel.align = a;
|
|
37162
|
+
} else if (t.word === "size") {
|
|
37163
|
+
const d = parseDims(tok.shift(), "size", ln);
|
|
37164
|
+
room.w = d.w;
|
|
37165
|
+
room.h = d.h;
|
|
37166
|
+
} else if (t.word === "fill") room.fill = parseId(tok.shift(), "a fill color", ln);
|
|
37167
|
+
else if (t.word === "nolabel") room.nolabel = true;
|
|
37168
|
+
else throw new FloorplanParseError(`room: unexpected token "${t.word}"`, ln);
|
|
37169
|
+
}
|
|
37170
|
+
ast.rooms.push(room);
|
|
37171
|
+
}
|
|
37172
|
+
function parseExtend(tok, ast, ln) {
|
|
37173
|
+
const room = parseId(tok.shift(), "a room id", ln);
|
|
37174
|
+
const ext = { room, w: 2, h: 2, line: ln };
|
|
37175
|
+
while (tok.length) {
|
|
37176
|
+
const t = tok.shift();
|
|
37177
|
+
if (!isWord(t)) throw new FloorplanParseError(`extend: unexpected string "${t.str}"`, ln);
|
|
37178
|
+
else if (t.word === "at") ext.at = parseCoord2(tok.shift(), "at", ln);
|
|
37179
|
+
else if (REL_HOW.includes(t.word)) {
|
|
37180
|
+
ext.rel = {
|
|
37181
|
+
how: t.word,
|
|
37182
|
+
ref: parseId(tok.shift(), `a room id after "${t.word}"`, ln),
|
|
37183
|
+
offset: void 0,
|
|
37184
|
+
align: void 0
|
|
37185
|
+
};
|
|
37186
|
+
} else if (t.word === "offset") {
|
|
37187
|
+
if (!ext.rel) throw new FloorplanParseError(`"offset" requires a relative placement (right-of/left-of/above/below)`, ln);
|
|
37188
|
+
ext.rel.offset = parseNum(tok.shift(), "offset", ln);
|
|
37189
|
+
} else if (t.word === "align") {
|
|
37190
|
+
if (!ext.rel) throw new FloorplanParseError(`"align" requires a relative placement (right-of/left-of/above/below)`, ln);
|
|
37191
|
+
const a = parseId(tok.shift(), "align (start|center|end)", ln);
|
|
37192
|
+
if (a !== "start" && a !== "center" && a !== "end") {
|
|
37193
|
+
throw new FloorplanParseError(`align must be start|center|end, got "${a}"`, ln);
|
|
37194
|
+
}
|
|
37195
|
+
ext.rel.align = a;
|
|
37196
|
+
} else if (t.word === "size") {
|
|
37197
|
+
const d = parseDims(tok.shift(), "size", ln);
|
|
37198
|
+
ext.w = d.w;
|
|
37199
|
+
ext.h = d.h;
|
|
37200
|
+
} else throw new FloorplanParseError(`extend: unexpected token "${t.word}"`, ln);
|
|
37201
|
+
}
|
|
37202
|
+
ast.extensions.push(ext);
|
|
37203
|
+
}
|
|
37204
|
+
var DOOR_TYPES = ["single", "double", "sliding", "pocket", "bifold"];
|
|
37205
|
+
var WINDOW_TYPES = ["fixed", "sliding", "casement", "bay"];
|
|
37206
|
+
function parseOpening(kind, tok, ast, ln) {
|
|
37207
|
+
const op = {
|
|
37208
|
+
kind,
|
|
37209
|
+
pct: 50,
|
|
37210
|
+
width: 0,
|
|
37211
|
+
// resolved after the form is known
|
|
37212
|
+
hinge: "left",
|
|
37213
|
+
swing: "in",
|
|
37214
|
+
doorType: "single",
|
|
37215
|
+
windowType: "fixed",
|
|
37216
|
+
line: ln
|
|
37217
|
+
};
|
|
37218
|
+
const t0 = tok.shift();
|
|
37219
|
+
if (isWord(t0, "between")) {
|
|
37220
|
+
op.between = [parseId(tok.shift(), "a room id", ln), parseId(tok.shift(), "a second room id", ln)];
|
|
37221
|
+
} else {
|
|
37222
|
+
op.room = parseId(t0, `a room id or "between"`, ln);
|
|
37223
|
+
const side = parseId(tok.shift(), "a wall side", ln);
|
|
37224
|
+
if (!SIDES.includes(side)) {
|
|
37225
|
+
throw new FloorplanParseError(`expected a wall side north|south|east|west, got "${side}"`, ln);
|
|
37226
|
+
}
|
|
37227
|
+
op.side = side;
|
|
37228
|
+
}
|
|
37229
|
+
while (tok.length) {
|
|
37230
|
+
const t = tok.shift();
|
|
37231
|
+
if (!isWord(t)) throw new FloorplanParseError(`${kind}: unexpected string "${t.str}"`, ln);
|
|
37232
|
+
else if (t.word === "at") op.pct = parsePct(tok.shift(), ln);
|
|
37233
|
+
else if (t.word === "width") op.width = parseNum(tok.shift(), "width", ln);
|
|
37234
|
+
else if (t.word === "hinge") {
|
|
37235
|
+
const h = parseId(tok.shift(), "hinge (left|right)", ln);
|
|
37236
|
+
if (h !== "left" && h !== "right") throw new FloorplanParseError(`hinge must be left|right, got "${h}"`, ln);
|
|
37237
|
+
op.hinge = h;
|
|
37238
|
+
} else if (t.word === "swing") {
|
|
37239
|
+
const s = parseId(tok.shift(), "swing (in|out)", ln);
|
|
37240
|
+
if (s !== "in" && s !== "out") throw new FloorplanParseError(`swing must be in|out, got "${s}"`, ln);
|
|
37241
|
+
op.swing = s;
|
|
37242
|
+
} else if (t.word === "type") {
|
|
37243
|
+
const d = parseId(tok.shift(), `${kind} type`, ln);
|
|
37244
|
+
if (kind === "window") {
|
|
37245
|
+
if (!WINDOW_TYPES.includes(d)) {
|
|
37246
|
+
throw new FloorplanParseError(`window type must be ${WINDOW_TYPES.join("|")}, got "${d}"`, ln);
|
|
37247
|
+
}
|
|
37248
|
+
op.windowType = d;
|
|
37249
|
+
} else {
|
|
37250
|
+
if (!DOOR_TYPES.includes(d)) {
|
|
37251
|
+
throw new FloorplanParseError(`door type must be ${DOOR_TYPES.join("|")}, got "${d}"`, ln);
|
|
37252
|
+
}
|
|
37253
|
+
op.doorType = d;
|
|
37254
|
+
}
|
|
37255
|
+
} else throw new FloorplanParseError(`${kind}: unexpected token "${t.word}"`, ln);
|
|
37256
|
+
}
|
|
37257
|
+
if (op.width === 0) {
|
|
37258
|
+
op.width = kind === "window" ? 1.2 : kind === "opening" ? 1 : op.between ? 0.8 : 0.9;
|
|
37259
|
+
}
|
|
37260
|
+
ast.openings.push(op);
|
|
37261
|
+
}
|
|
37262
|
+
function parseFurniture(tok, ast, ln) {
|
|
37263
|
+
const type = parseFurnitureType(tok.shift(), ln);
|
|
37264
|
+
const f = { type, x: 0, y: 0, rotate: 0, line: ln };
|
|
37265
|
+
while (tok.length) {
|
|
37266
|
+
const t = tok.shift();
|
|
37267
|
+
if (isStr(t)) f.label = t.str;
|
|
37268
|
+
else if (t.word === "in") f.room = parseId(tok.shift(), `a room id after "in"`, ln);
|
|
37269
|
+
else if (t.word === "at") {
|
|
37270
|
+
const c = parseCoord2(tok.shift(), "at", ln);
|
|
37271
|
+
f.x = c.x;
|
|
37272
|
+
f.y = c.y;
|
|
37273
|
+
} else if (t.word === "size") f.size = parseDims(tok.shift(), "size", ln);
|
|
37274
|
+
else if (t.word === "rotate") f.rotate = parseNum(tok.shift(), "rotate", ln);
|
|
37275
|
+
else throw new FloorplanParseError(`furniture: unexpected token "${t.word}"`, ln);
|
|
37276
|
+
}
|
|
37277
|
+
ast.furniture.push(f);
|
|
37278
|
+
}
|
|
37279
|
+
function parseArray(mode, tok, ast, ln) {
|
|
37280
|
+
const type = parseFurnitureType(tok.shift(), ln);
|
|
37281
|
+
const a = {
|
|
37282
|
+
mode,
|
|
37283
|
+
type,
|
|
37284
|
+
rows: 1,
|
|
37285
|
+
cols: 1,
|
|
37286
|
+
count: Infinity,
|
|
37287
|
+
rotate: 0,
|
|
37288
|
+
line: ln
|
|
37289
|
+
};
|
|
37290
|
+
while (tok.length) {
|
|
37291
|
+
const t = tok.shift();
|
|
37292
|
+
if (!isWord(t)) throw new FloorplanParseError(`${mode}: unexpected string "${t.str}"`, ln);
|
|
37293
|
+
else if (t.word === "in") a.room = parseId(tok.shift(), `a room id after "in"`, ln);
|
|
37294
|
+
else if (t.word === "rows") a.rows = parseNum(tok.shift(), "rows", ln);
|
|
37295
|
+
else if (t.word === "cols") a.cols = parseNum(tok.shift(), "cols", ln);
|
|
37296
|
+
else if (t.word === "count") a.count = parseNum(tok.shift(), "count", ln);
|
|
37297
|
+
else if (t.word === "area") {
|
|
37298
|
+
a.p1 = parseCoord2(tok.shift(), "area p1", ln);
|
|
37299
|
+
a.p2 = parseCoord2(tok.shift(), "area p2", ln);
|
|
37300
|
+
} else if (t.word === "itemsize") a.itemsize = parseDims(tok.shift(), "itemsize", ln);
|
|
37301
|
+
else if (t.word === "rotate") a.rotate = parseNum(tok.shift(), "rotate", ln);
|
|
37302
|
+
else if (t.word === "center") a.center = parseCoord2(tok.shift(), "center", ln);
|
|
37303
|
+
else if (t.word === "radius") a.radius = parseNum(tok.shift(), "radius", ln);
|
|
37304
|
+
else if (t.word === "from") a.fromDeg = parseNum(tok.shift(), "from", ln);
|
|
37305
|
+
else if (t.word === "to") a.toDeg = parseNum(tok.shift(), "to", ln);
|
|
37306
|
+
else throw new FloorplanParseError(`${mode}: unexpected token "${t.word}"`, ln);
|
|
37307
|
+
}
|
|
37308
|
+
ast.arrays.push(a);
|
|
37309
|
+
}
|
|
37310
|
+
function parseFloorplan(text2) {
|
|
37311
|
+
const ast = {
|
|
37312
|
+
type: "floorplan",
|
|
37313
|
+
title: "Floor Plan",
|
|
37314
|
+
unit: "m",
|
|
37315
|
+
rooms: [],
|
|
37316
|
+
extensions: [],
|
|
37317
|
+
openings: [],
|
|
37318
|
+
furniture: [],
|
|
37319
|
+
arrays: []
|
|
37320
|
+
};
|
|
37321
|
+
let sawHeader = false;
|
|
37322
|
+
const lines = text2.split(/\r?\n/);
|
|
37323
|
+
for (let i = 0; i < lines.length; i++) {
|
|
37324
|
+
const ln = i + 1;
|
|
37325
|
+
const raw = normalizeQuotes3(lines[i]).trim();
|
|
37326
|
+
if (!raw) continue;
|
|
37327
|
+
const all = tokenize7(raw);
|
|
37328
|
+
const tok = [];
|
|
37329
|
+
for (let k = 0; k < all.length; k++) {
|
|
37330
|
+
const t = all[k];
|
|
37331
|
+
if (isWord(t) && (t.word.startsWith("#") || t.word.startsWith("//"))) {
|
|
37332
|
+
const prev = all[k - 1];
|
|
37333
|
+
if (t.word.startsWith("#") && isWord(prev, "fill")) {
|
|
37334
|
+
tok.push(t);
|
|
37335
|
+
continue;
|
|
37336
|
+
}
|
|
37337
|
+
break;
|
|
37338
|
+
}
|
|
37339
|
+
tok.push(t);
|
|
37340
|
+
}
|
|
37341
|
+
if (tok.length === 0) continue;
|
|
37342
|
+
const head = tok.shift();
|
|
37343
|
+
if (!isWord(head)) throw new FloorplanParseError(`unexpected string at line start`, ln);
|
|
37344
|
+
const kw = head.word.toLowerCase();
|
|
37345
|
+
if (kw === "floorplan") {
|
|
37346
|
+
parseHeader3(tok, ast, ln);
|
|
37347
|
+
sawHeader = true;
|
|
37348
|
+
} else if (!sawHeader) {
|
|
37349
|
+
throw new FloorplanParseError(`the first statement must be the "floorplan" header`, ln);
|
|
37350
|
+
} else if (kw === "room") parseRoom(tok, ast, ln);
|
|
37351
|
+
else if (kw === "north") {
|
|
37352
|
+
ast.north = tok.length ? parseNum(tok.shift(), "north rotation (degrees)", ln) : 0;
|
|
37353
|
+
if (tok.length) throw new FloorplanParseError(`north: unexpected trailing tokens`, ln);
|
|
37354
|
+
} else if (kw === "extend") parseExtend(tok, ast, ln);
|
|
37355
|
+
else if (kw === "door" || kw === "window" || kw === "opening") parseOpening(kw, tok, ast, ln);
|
|
37356
|
+
else if (kw === "furniture") parseFurniture(tok, ast, ln);
|
|
37357
|
+
else if (kw === "grid" || kw === "row" || kw === "arc") parseArray(kw, tok, ast, ln);
|
|
37358
|
+
else {
|
|
37359
|
+
throw new FloorplanParseError(
|
|
37360
|
+
`unknown keyword "${kw}". Expected: floorplan, room, extend, door, window, opening, furniture, grid, row, arc`,
|
|
37361
|
+
ln
|
|
37362
|
+
);
|
|
37363
|
+
}
|
|
37364
|
+
}
|
|
37365
|
+
return ast;
|
|
37366
|
+
}
|
|
37367
|
+
|
|
37368
|
+
// src/diagrams/floorplan/layout.ts
|
|
37369
|
+
var FT = 0.3048;
|
|
37370
|
+
var FLOORPLAN_CONST = {
|
|
37371
|
+
/** Wall band thickness, meters (§5). */
|
|
37372
|
+
wallT: 0.2,
|
|
37373
|
+
/** Default render scale, px per meter (§4.1). */
|
|
37374
|
+
scale: 55,
|
|
37375
|
+
/** Jamb margin an opening keeps from the segment ends, meters (§4.3). */
|
|
37376
|
+
jamb: 0.05,
|
|
37377
|
+
/** Dimension band depth outside the plan, meters. */
|
|
37378
|
+
dimBand: 1,
|
|
37379
|
+
/** Outer padding, meters. */
|
|
37380
|
+
pad: 0.45,
|
|
37381
|
+
/** Offsets of the major / minor dimension rows from the plan edge, meters. */
|
|
37382
|
+
dimMajorOff: 0.62,
|
|
37383
|
+
dimMinorOff: 0.3
|
|
37384
|
+
};
|
|
37385
|
+
function formatLength(m, unit) {
|
|
37386
|
+
if (unit === "m") {
|
|
37387
|
+
const v = Math.round(m * 100) / 100;
|
|
37388
|
+
return `${v} m`;
|
|
37389
|
+
}
|
|
37390
|
+
const ftv = m / FT;
|
|
37391
|
+
let f = Math.floor(ftv + 1e-6);
|
|
37392
|
+
let inches = Math.round((ftv - f) * 12);
|
|
37393
|
+
if (inches >= 12) {
|
|
37394
|
+
f += 1;
|
|
37395
|
+
inches = 0;
|
|
37396
|
+
}
|
|
37397
|
+
return inches ? `${f}'${inches}"` : `${f}'`;
|
|
37398
|
+
}
|
|
37399
|
+
function formatArea(areaM2, unit) {
|
|
37400
|
+
if (unit === "m") return `${areaM2.toFixed(1)} m\xB2`;
|
|
37401
|
+
return `${Math.round(areaM2 / (FT * FT))} sq ft`;
|
|
37402
|
+
}
|
|
37403
|
+
var fmtNum = (v) => String(Math.round(v * 100) / 100);
|
|
37404
|
+
var snap = (v) => Math.round(v * 1e6) / 1e6;
|
|
37405
|
+
var ADJ_EPS = 0.051;
|
|
37406
|
+
var MIN_OVERLAP = 0.3;
|
|
37407
|
+
function rectSharedEdge(a, b) {
|
|
37408
|
+
if (Math.abs(a.x + a.w - b.x) < ADJ_EPS || Math.abs(b.x + b.w - a.x) < ADJ_EPS) {
|
|
37409
|
+
const along = Math.abs(a.x + a.w - b.x) < ADJ_EPS ? a.x + a.w : b.x + b.w;
|
|
37410
|
+
const lo = Math.max(a.y, b.y);
|
|
37411
|
+
const hi = Math.min(a.y + a.h, b.y + b.h);
|
|
37412
|
+
if (hi - lo >= MIN_OVERLAP) return { vertical: true, along, lo, hi };
|
|
37413
|
+
}
|
|
37414
|
+
if (Math.abs(a.y + a.h - b.y) < ADJ_EPS || Math.abs(b.y + b.h - a.y) < ADJ_EPS) {
|
|
37415
|
+
const along = Math.abs(a.y + a.h - b.y) < ADJ_EPS ? a.y + a.h : b.y + b.h;
|
|
37416
|
+
const lo = Math.max(a.x, b.x);
|
|
37417
|
+
const hi = Math.min(a.x + a.w, b.x + b.w);
|
|
37418
|
+
if (hi - lo >= MIN_OVERLAP) return { vertical: false, along, lo, hi };
|
|
37419
|
+
}
|
|
37420
|
+
return null;
|
|
37421
|
+
}
|
|
37422
|
+
function roomSharedEdge(a, b) {
|
|
37423
|
+
let best = null;
|
|
37424
|
+
for (const pa of a.parts) {
|
|
37425
|
+
for (const pb of b.parts) {
|
|
37426
|
+
const e = rectSharedEdge(pa, pb);
|
|
37427
|
+
if (e && (!best || e.hi - e.lo > best.edge.hi - best.edge.lo)) {
|
|
37428
|
+
best = { edge: e, aPart: pa, bPart: pb };
|
|
37429
|
+
}
|
|
37430
|
+
}
|
|
37431
|
+
}
|
|
37432
|
+
return best;
|
|
37433
|
+
}
|
|
37434
|
+
function rectOverlap(a, b) {
|
|
37435
|
+
return {
|
|
37436
|
+
ox: Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x),
|
|
37437
|
+
oy: Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y)
|
|
37438
|
+
};
|
|
37439
|
+
}
|
|
37440
|
+
function obbCorners(x, y, w, h, rotDeg, margins) {
|
|
37441
|
+
const [mt, mr, mb, ml] = margins;
|
|
37442
|
+
const cx = x + w / 2;
|
|
37443
|
+
const cy = y + h / 2;
|
|
37444
|
+
const corners = [
|
|
37445
|
+
[x - ml, y - mt],
|
|
37446
|
+
[x + w + mr, y - mt],
|
|
37447
|
+
[x + w + mr, y + h + mb],
|
|
37448
|
+
[x - ml, y + h + mb]
|
|
37449
|
+
];
|
|
37450
|
+
if (!rotDeg) return corners;
|
|
37451
|
+
const rad = rotDeg * Math.PI / 180;
|
|
37452
|
+
const cos = Math.cos(rad);
|
|
37453
|
+
const sin = Math.sin(rad);
|
|
37454
|
+
return corners.map(([px, py]) => {
|
|
37455
|
+
const dx = px - cx;
|
|
37456
|
+
const dy = py - cy;
|
|
37457
|
+
return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
|
|
37458
|
+
});
|
|
37459
|
+
}
|
|
37460
|
+
function rotatedAabb(x, y, w, h, rotDeg, margins) {
|
|
37461
|
+
const corners = obbCorners(x, y, w, h, rotDeg, margins);
|
|
37462
|
+
let minX = Infinity;
|
|
37463
|
+
let minY = Infinity;
|
|
37464
|
+
let maxX = -Infinity;
|
|
37465
|
+
let maxY = -Infinity;
|
|
37466
|
+
for (const [px, py] of corners) {
|
|
37467
|
+
minX = Math.min(minX, px);
|
|
37468
|
+
minY = Math.min(minY, py);
|
|
37469
|
+
maxX = Math.max(maxX, px);
|
|
37470
|
+
maxY = Math.max(maxY, py);
|
|
37471
|
+
}
|
|
37472
|
+
return { minX, minY, maxX, maxY };
|
|
37473
|
+
}
|
|
37474
|
+
function obbPenetration(a, b) {
|
|
37475
|
+
let minPen = Infinity;
|
|
37476
|
+
for (const poly of [a, b]) {
|
|
37477
|
+
for (let i = 0; i < 4; i++) {
|
|
37478
|
+
const j = (i + 1) % 4;
|
|
37479
|
+
let ax = poly[j][1] - poly[i][1];
|
|
37480
|
+
let ay = poly[i][0] - poly[j][0];
|
|
37481
|
+
const len = Math.hypot(ax, ay);
|
|
37482
|
+
if (len < 1e-12) continue;
|
|
37483
|
+
ax /= len;
|
|
37484
|
+
ay /= len;
|
|
37485
|
+
let aLo = Infinity;
|
|
37486
|
+
let aHi = -Infinity;
|
|
37487
|
+
for (const [px, py] of a) {
|
|
37488
|
+
const v = px * ax + py * ay;
|
|
37489
|
+
aLo = Math.min(aLo, v);
|
|
37490
|
+
aHi = Math.max(aHi, v);
|
|
37491
|
+
}
|
|
37492
|
+
let bLo = Infinity;
|
|
37493
|
+
let bHi = -Infinity;
|
|
37494
|
+
for (const [px, py] of b) {
|
|
37495
|
+
const v = px * ax + py * ay;
|
|
37496
|
+
bLo = Math.min(bLo, v);
|
|
37497
|
+
bHi = Math.max(bHi, v);
|
|
37498
|
+
}
|
|
37499
|
+
const pen = Math.min(aHi, bHi) - Math.max(aLo, bLo);
|
|
37500
|
+
if (pen <= 0) return 0;
|
|
37501
|
+
minPen = Math.min(minPen, pen);
|
|
37502
|
+
}
|
|
37503
|
+
}
|
|
37504
|
+
return minPen === Infinity ? 0 : minPen;
|
|
37505
|
+
}
|
|
37506
|
+
function resolvePlacement(p, w, h, byId, rooms, u, who, errors) {
|
|
37507
|
+
if (p.at) return { x: snap(p.at.x * u), y: snap(p.at.y * u) };
|
|
37508
|
+
if (p.rel) {
|
|
37509
|
+
const refIdx = byId.get(p.rel.ref);
|
|
37510
|
+
if (refIdx === void 0) {
|
|
37511
|
+
errors.push(`${who}: unknown reference room "${p.rel.ref}" \u2014 declare it first`);
|
|
37512
|
+
return null;
|
|
37513
|
+
}
|
|
37514
|
+
const ref = rooms[refIdx];
|
|
37515
|
+
const off = (p.rel.offset ?? 0) * u;
|
|
37516
|
+
const alignPos = (refStart, refLen, len) => {
|
|
37517
|
+
if (p.rel.align === "center") return refStart + (refLen - len) / 2 + off;
|
|
37518
|
+
if (p.rel.align === "end") return refStart + refLen - len + off;
|
|
37519
|
+
return refStart + off;
|
|
37520
|
+
};
|
|
37521
|
+
let x = 0;
|
|
37522
|
+
let y = 0;
|
|
37523
|
+
switch (p.rel.how) {
|
|
37524
|
+
case "right-of":
|
|
37525
|
+
x = ref.x + ref.w;
|
|
37526
|
+
y = alignPos(ref.y, ref.h, h);
|
|
37527
|
+
break;
|
|
37528
|
+
case "left-of":
|
|
37529
|
+
x = ref.x - w;
|
|
37530
|
+
y = alignPos(ref.y, ref.h, h);
|
|
37531
|
+
break;
|
|
37532
|
+
case "below":
|
|
37533
|
+
y = ref.y + ref.h;
|
|
37534
|
+
x = alignPos(ref.x, ref.w, w);
|
|
37535
|
+
break;
|
|
37536
|
+
case "above":
|
|
37537
|
+
y = ref.y - h;
|
|
37538
|
+
x = alignPos(ref.x, ref.w, w);
|
|
37539
|
+
break;
|
|
37540
|
+
}
|
|
37541
|
+
return { x: snap(x), y: snap(y) };
|
|
37542
|
+
}
|
|
37543
|
+
return { x: 0, y: 0 };
|
|
37544
|
+
}
|
|
37545
|
+
function refreshRoomBounds(room, unit) {
|
|
37546
|
+
let minX = Infinity;
|
|
37547
|
+
let minY = Infinity;
|
|
37548
|
+
let maxX = -Infinity;
|
|
37549
|
+
let maxY = -Infinity;
|
|
37550
|
+
let area = 0;
|
|
37551
|
+
for (const p of room.parts) {
|
|
37552
|
+
minX = Math.min(minX, p.x);
|
|
37553
|
+
minY = Math.min(minY, p.y);
|
|
37554
|
+
maxX = Math.max(maxX, p.x + p.w);
|
|
37555
|
+
maxY = Math.max(maxY, p.y + p.h);
|
|
37556
|
+
area += p.w * p.h;
|
|
37557
|
+
}
|
|
37558
|
+
room.x = snap(minX);
|
|
37559
|
+
room.y = snap(minY);
|
|
37560
|
+
room.w = snap(maxX - minX);
|
|
37561
|
+
room.h = snap(maxY - minY);
|
|
37562
|
+
room.areaM2 = area;
|
|
37563
|
+
room.areaText = formatArea(area, unit);
|
|
37564
|
+
}
|
|
37565
|
+
function subtractIntervals(lo, hi, cuts) {
|
|
37566
|
+
let pieces = [[lo, hi]];
|
|
37567
|
+
for (const [cLo, cHi] of cuts) {
|
|
37568
|
+
const next = [];
|
|
37569
|
+
for (const [pLo, pHi] of pieces) {
|
|
37570
|
+
if (cHi <= pLo + 1e-9 || cLo >= pHi - 1e-9) {
|
|
37571
|
+
next.push([pLo, pHi]);
|
|
37572
|
+
continue;
|
|
37573
|
+
}
|
|
37574
|
+
if (cLo > pLo + 1e-9) next.push([pLo, Math.min(cLo, pHi)]);
|
|
37575
|
+
if (cHi < pHi - 1e-9) next.push([Math.max(cHi, pLo), pHi]);
|
|
37576
|
+
}
|
|
37577
|
+
pieces = next;
|
|
37578
|
+
}
|
|
37579
|
+
return pieces.filter(([a, b]) => b - a > 1e-9);
|
|
37580
|
+
}
|
|
37581
|
+
function sideSegments(room, side) {
|
|
37582
|
+
const segs = [];
|
|
37583
|
+
for (const p of room.parts) {
|
|
37584
|
+
const vertical = side === "west" || side === "east";
|
|
37585
|
+
const along = side === "north" ? p.y : side === "south" ? p.y + p.h : side === "west" ? p.x : p.x + p.w;
|
|
37586
|
+
const lo = vertical ? p.y : p.x;
|
|
37587
|
+
const hi = vertical ? p.y + p.h : p.x + p.w;
|
|
37588
|
+
const cuts = [];
|
|
37589
|
+
for (const q of room.parts) {
|
|
37590
|
+
if (q === p) continue;
|
|
37591
|
+
const qNear = side === "north" ? q.y + q.h : side === "south" ? q.y : side === "west" ? q.x + q.w : q.x;
|
|
37592
|
+
if (Math.abs(qNear - along) >= ADJ_EPS) continue;
|
|
37593
|
+
const cLo = vertical ? Math.max(lo, q.y) : Math.max(lo, q.x);
|
|
37594
|
+
const cHi = vertical ? Math.min(hi, q.y + q.h) : Math.min(hi, q.x + q.w);
|
|
37595
|
+
if (cHi > cLo) cuts.push([cLo, cHi]);
|
|
37596
|
+
}
|
|
37597
|
+
for (const [sLo, sHi] of subtractIntervals(lo, hi, cuts)) {
|
|
37598
|
+
if (sHi - sLo >= MIN_OVERLAP) segs.push({ along, lo: sLo, hi: sHi });
|
|
37599
|
+
}
|
|
37600
|
+
}
|
|
37601
|
+
return segs.sort((a, b) => a.lo - b.lo || a.along - b.along);
|
|
37602
|
+
}
|
|
37603
|
+
function layoutFloorplan(ast) {
|
|
37604
|
+
const u = ast.unit === "ft" ? FT : 1;
|
|
37605
|
+
const errors = [];
|
|
37606
|
+
const warnings = [];
|
|
37607
|
+
const rooms = [];
|
|
37608
|
+
const byId = /* @__PURE__ */ new Map();
|
|
37609
|
+
const stmts = [
|
|
37610
|
+
...ast.rooms.map((room) => ({ line: room.line ?? 0, room })),
|
|
37611
|
+
...ast.extensions.map((ext) => ({ line: ext.line ?? 0, ext }))
|
|
37612
|
+
].sort((a, b) => a.line - b.line);
|
|
37613
|
+
for (const stmt of stmts) {
|
|
37614
|
+
if (stmt.room) {
|
|
37615
|
+
const r6 = stmt.room;
|
|
37616
|
+
const w = r6.w * u;
|
|
37617
|
+
const h = r6.h * u;
|
|
37618
|
+
const pos = resolvePlacement(r6, w, h, byId, rooms, u, `room "${r6.id}"`, errors) ?? { x: 0, y: 0 };
|
|
37619
|
+
const part = { x: pos.x, y: pos.y, w, h };
|
|
37620
|
+
const room = {
|
|
37621
|
+
id: r6.id,
|
|
37622
|
+
label: r6.label,
|
|
37623
|
+
x: part.x,
|
|
37624
|
+
y: part.y,
|
|
37625
|
+
w,
|
|
37626
|
+
h,
|
|
37627
|
+
parts: [part],
|
|
37628
|
+
areaM2: 0,
|
|
37629
|
+
areaText: "",
|
|
37630
|
+
fill: r6.fill,
|
|
37631
|
+
nolabel: r6.nolabel ?? false
|
|
37632
|
+
};
|
|
37633
|
+
refreshRoomBounds(room, ast.unit);
|
|
37634
|
+
byId.set(r6.id, rooms.length);
|
|
37635
|
+
rooms.push(room);
|
|
37636
|
+
} else if (stmt.ext) {
|
|
37637
|
+
const e = stmt.ext;
|
|
37638
|
+
const idx = byId.get(e.room);
|
|
37639
|
+
if (idx === void 0) {
|
|
37640
|
+
errors.push(`extend: unknown room "${e.room}" \u2014 declare it first`);
|
|
37641
|
+
continue;
|
|
37642
|
+
}
|
|
37643
|
+
const room = rooms[idx];
|
|
37644
|
+
const w = e.w * u;
|
|
37645
|
+
const h = e.h * u;
|
|
37646
|
+
const pos = resolvePlacement(e, w, h, byId, rooms, u, `extend "${e.room}"`, errors);
|
|
37647
|
+
if (!pos) continue;
|
|
37648
|
+
const part = { x: pos.x, y: pos.y, w, h };
|
|
37649
|
+
let touches = false;
|
|
37650
|
+
let overlaps = false;
|
|
37651
|
+
for (const p of room.parts) {
|
|
37652
|
+
const { ox, oy } = rectOverlap(p, part);
|
|
37653
|
+
if (ox > ADJ_EPS && oy > ADJ_EPS) overlaps = true;
|
|
37654
|
+
if (rectSharedEdge(p, part)) touches = true;
|
|
37655
|
+
}
|
|
37656
|
+
if (overlaps) {
|
|
37657
|
+
errors.push(`extend "${e.room}": extension overlaps the room's existing area \u2014 place it edge-to-edge`);
|
|
37658
|
+
continue;
|
|
37659
|
+
}
|
|
37660
|
+
if (!touches) {
|
|
37661
|
+
errors.push(`extend "${e.room}": extension does not touch the room \u2014 extensions must share an edge`);
|
|
37662
|
+
continue;
|
|
37663
|
+
}
|
|
37664
|
+
room.parts.push(part);
|
|
37665
|
+
refreshRoomBounds(room, ast.unit);
|
|
37666
|
+
}
|
|
37667
|
+
}
|
|
37668
|
+
for (let i = 0; i < rooms.length; i++) {
|
|
37669
|
+
for (let j = i + 1; j < rooms.length; j++) {
|
|
37670
|
+
const a = rooms[i];
|
|
37671
|
+
const b = rooms[j];
|
|
37672
|
+
let worst = null;
|
|
37673
|
+
for (const pa of a.parts) {
|
|
37674
|
+
for (const pb of b.parts) {
|
|
37675
|
+
const { ox, oy } = rectOverlap(pa, pb);
|
|
37676
|
+
if (ox > ADJ_EPS && oy > ADJ_EPS && (!worst || ox * oy > worst.ox * worst.oy)) {
|
|
37677
|
+
worst = { ox, oy };
|
|
37678
|
+
}
|
|
37679
|
+
}
|
|
37680
|
+
}
|
|
37681
|
+
if (worst) {
|
|
37682
|
+
errors.push(
|
|
37683
|
+
`rooms "${a.id}" and "${b.id}" overlap by ${worst.ox.toFixed(2)}\xD7${worst.oy.toFixed(2)} m \u2014 move "${b.id}" right-of "${a.id}" or shrink size`
|
|
37684
|
+
);
|
|
37685
|
+
}
|
|
37686
|
+
}
|
|
37687
|
+
}
|
|
37688
|
+
const seams = [];
|
|
37689
|
+
for (let ri = 0; ri < rooms.length; ri++) {
|
|
37690
|
+
const parts = rooms[ri].parts;
|
|
37691
|
+
for (let i = 0; i < parts.length; i++) {
|
|
37692
|
+
for (let j = i + 1; j < parts.length; j++) {
|
|
37693
|
+
const e = rectSharedEdge(parts[i], parts[j]);
|
|
37694
|
+
if (e) seams.push({ vertical: e.vertical, along: e.along, lo: e.lo, hi: e.hi, room: ri });
|
|
37695
|
+
}
|
|
37696
|
+
}
|
|
37697
|
+
}
|
|
37698
|
+
const openings = [];
|
|
37699
|
+
for (const op of ast.openings) {
|
|
37700
|
+
const geom = resolveOpening(op, rooms, byId, u, ast.unit, errors, warnings);
|
|
37701
|
+
if (geom) openings.push(geom);
|
|
37702
|
+
}
|
|
37703
|
+
const items = [];
|
|
37704
|
+
const seqByType = /* @__PURE__ */ new Map();
|
|
37705
|
+
const place = (type, roomIdx, localX, localY, w, h, rotate, label) => {
|
|
37706
|
+
const room = rooms[roomIdx];
|
|
37707
|
+
const seq = (seqByType.get(type) ?? 0) + 1;
|
|
37708
|
+
seqByType.set(type, seq);
|
|
37709
|
+
items.push({ type, x: room.x + localX, y: room.y + localY, w, h, rotate, label, roomId: room.id, seq });
|
|
37710
|
+
};
|
|
37711
|
+
const roomIdxOf = (stmt, roomId, line2) => {
|
|
37712
|
+
if (!roomId) {
|
|
37713
|
+
errors.push(`${stmt}${line2 ? ` (line ${line2})` : ""}: missing "in <room>"`);
|
|
37714
|
+
return void 0;
|
|
37715
|
+
}
|
|
37716
|
+
const idx = byId.get(roomId);
|
|
37717
|
+
if (idx === void 0) {
|
|
37718
|
+
errors.push(`${stmt}: unknown room "${roomId}"`);
|
|
37719
|
+
return void 0;
|
|
37720
|
+
}
|
|
37721
|
+
return idx;
|
|
37722
|
+
};
|
|
37723
|
+
for (const f of ast.furniture) {
|
|
37724
|
+
const def = FLOORPLAN_SYMBOLS[f.type];
|
|
37725
|
+
const idx = roomIdxOf(`furniture ${f.type}`, f.room, f.line);
|
|
37726
|
+
if (idx === void 0) continue;
|
|
37727
|
+
const w = f.size ? f.size.w * u : def.w;
|
|
37728
|
+
const h = f.size ? f.size.h * u : def.h;
|
|
37729
|
+
place(f.type, idx, f.x * u, f.y * u, w, h, f.rotate, f.label);
|
|
37730
|
+
}
|
|
37731
|
+
for (const a of ast.arrays) {
|
|
37732
|
+
const def = FLOORPLAN_SYMBOLS[a.type];
|
|
37733
|
+
const idx = roomIdxOf(`${a.mode} ${a.type}`, a.room, a.line);
|
|
37734
|
+
if (idx === void 0) continue;
|
|
37735
|
+
const room = rooms[idx];
|
|
37736
|
+
const iw = a.itemsize ? a.itemsize.w * u : def.w;
|
|
37737
|
+
const ih = a.itemsize ? a.itemsize.h * u : def.h;
|
|
37738
|
+
const p1 = a.p1 ? { x: a.p1.x * u, y: a.p1.y * u } : { x: 0.5 * u, y: 0.5 * u };
|
|
37739
|
+
const p2 = a.p2 ? { x: a.p2.x * u, y: a.p2.y * u } : { x: room.w - 0.5 * u, y: room.h - 0.5 * u };
|
|
37740
|
+
if (a.mode === "grid" || a.mode === "row") {
|
|
37741
|
+
const nRows = a.mode === "row" ? 1 : Math.max(1, Math.round(a.rows));
|
|
37742
|
+
const nCols = Math.max(1, Math.round(a.cols));
|
|
37743
|
+
const cap2 = Number.isFinite(a.count) ? a.count : nRows * nCols;
|
|
37744
|
+
const spanW = p2.x - p1.x;
|
|
37745
|
+
const spanH = p2.y - p1.y;
|
|
37746
|
+
let placed = 0;
|
|
37747
|
+
for (let r6 = 0; r6 < nRows && placed < cap2; r6++) {
|
|
37748
|
+
for (let col = 0; col < nCols && placed < cap2; col++) {
|
|
37749
|
+
const cx = p1.x + (nCols === 1 ? spanW / 2 : col * spanW / (nCols - 1));
|
|
37750
|
+
const cy = p1.y + (nRows === 1 ? spanH / 2 : r6 * spanH / (nRows - 1));
|
|
37751
|
+
place(a.type, idx, cx - iw / 2, cy - ih / 2, iw, ih, a.rotate);
|
|
37752
|
+
placed++;
|
|
37753
|
+
}
|
|
37754
|
+
}
|
|
37755
|
+
} else {
|
|
37756
|
+
const n = Math.max(1, Number.isFinite(a.count) ? a.count : Math.max(1, Math.round(a.cols)));
|
|
37757
|
+
const cen = a.center ? { x: a.center.x * u, y: a.center.y * u } : { x: room.w / 2, y: room.h * 0.6 };
|
|
37758
|
+
const radius = a.radius !== void 0 ? a.radius * u : Math.min(room.w, room.h) / 3;
|
|
37759
|
+
const a0 = (a.fromDeg ?? 200) * Math.PI / 180;
|
|
37760
|
+
const a1 = (a.toDeg ?? 340) * Math.PI / 180;
|
|
37761
|
+
for (let i = 0; i < n; i++) {
|
|
37762
|
+
const th = a0 + (a1 - a0) * i / Math.max(n - 1, 1);
|
|
37763
|
+
const cx = cen.x + radius * Math.cos(th);
|
|
37764
|
+
const cy = cen.y + radius * Math.sin(th);
|
|
37765
|
+
const facing = th * 180 / Math.PI + 270;
|
|
37766
|
+
place(a.type, idx, cx - iw / 2, cy - ih / 2, iw, ih, facing + a.rotate);
|
|
37767
|
+
}
|
|
37768
|
+
}
|
|
37769
|
+
}
|
|
37770
|
+
const roomOf = /* @__PURE__ */ new Map();
|
|
37771
|
+
for (const r6 of rooms) roomOf.set(r6.id, r6);
|
|
37772
|
+
const warnItems = /* @__PURE__ */ new Set();
|
|
37773
|
+
for (const it of items) {
|
|
37774
|
+
const room = roomOf.get(it.roomId);
|
|
37775
|
+
if (!room) continue;
|
|
37776
|
+
const bb = rotatedAabb(it.x, it.y, it.w, it.h, it.rotate, [0, 0, 0, 0]);
|
|
37777
|
+
const over = Math.max(
|
|
37778
|
+
room.x - bb.minX,
|
|
37779
|
+
bb.maxX - (room.x + room.w),
|
|
37780
|
+
room.y - bb.minY,
|
|
37781
|
+
bb.maxY - (room.y + room.h)
|
|
37782
|
+
);
|
|
37783
|
+
if (over > 0.011) {
|
|
37784
|
+
errors.push(
|
|
37785
|
+
`furniture ${it.type} #${it.seq} extends ${fmtNum(over)} m outside room "${it.roomId}" \u2014 move it or shrink size`
|
|
37786
|
+
);
|
|
37787
|
+
continue;
|
|
37788
|
+
}
|
|
37789
|
+
if (room.parts.length > 1) {
|
|
37790
|
+
const bw = bb.maxX - bb.minX;
|
|
37791
|
+
const bh = bb.maxY - bb.minY;
|
|
37792
|
+
let covered = 0;
|
|
37793
|
+
for (const p of room.parts) {
|
|
37794
|
+
const ox = Math.min(p.x + p.w, bb.maxX) - Math.max(p.x, bb.minX);
|
|
37795
|
+
const oy = Math.min(p.y + p.h, bb.maxY) - Math.max(p.y, bb.minY);
|
|
37796
|
+
if (ox > 0 && oy > 0) covered += ox * oy;
|
|
37797
|
+
}
|
|
37798
|
+
const uncovered = bw * bh - covered;
|
|
37799
|
+
if (uncovered > 0.01) {
|
|
37800
|
+
errors.push(
|
|
37801
|
+
`furniture ${it.type} #${it.seq} sits outside room "${it.roomId}"'s L-shape (${fmtNum(uncovered)} m\xB2 past the notch) \u2014 move it onto a room part`
|
|
37802
|
+
);
|
|
37803
|
+
}
|
|
37804
|
+
}
|
|
37805
|
+
}
|
|
37806
|
+
const envelopes = items.map((it) => {
|
|
37807
|
+
const def = FLOORPLAN_SYMBOLS[it.type];
|
|
37808
|
+
return obbCorners(it.x, it.y, it.w, it.h, it.rotate, def.envelope ?? [0, 0, 0, 0]);
|
|
37809
|
+
});
|
|
37810
|
+
for (let i = 0; i < items.length; i++) {
|
|
37811
|
+
const a = items[i];
|
|
37812
|
+
if (FLOORPLAN_SYMBOLS[a.type].underlay) continue;
|
|
37813
|
+
for (let j = i + 1; j < items.length; j++) {
|
|
37814
|
+
const b = items[j];
|
|
37815
|
+
if (FLOORPLAN_SYMBOLS[b.type].underlay) continue;
|
|
37816
|
+
const pen = obbPenetration(envelopes[i], envelopes[j]);
|
|
37817
|
+
if (pen > 0.011) {
|
|
37818
|
+
warnings.push(
|
|
37819
|
+
`${a.type} #${a.seq} overlaps ${b.type} #${b.seq} by ${fmtNum(pen)} m \u2014 increase spacing or reduce cols`
|
|
37820
|
+
);
|
|
37821
|
+
warnItems.add(i);
|
|
37822
|
+
warnItems.add(j);
|
|
37823
|
+
}
|
|
37824
|
+
}
|
|
37825
|
+
}
|
|
37826
|
+
let minX = Infinity;
|
|
37827
|
+
let minY = Infinity;
|
|
37828
|
+
let maxX = -Infinity;
|
|
37829
|
+
let maxY = -Infinity;
|
|
37830
|
+
for (const r6 of rooms) {
|
|
37831
|
+
minX = Math.min(minX, r6.x);
|
|
37832
|
+
minY = Math.min(minY, r6.y);
|
|
37833
|
+
maxX = Math.max(maxX, r6.x + r6.w);
|
|
37834
|
+
maxY = Math.max(maxY, r6.y + r6.h);
|
|
37835
|
+
}
|
|
37836
|
+
if (rooms.length === 0) {
|
|
37837
|
+
minX = minY = 0;
|
|
37838
|
+
maxX = maxY = 1;
|
|
37839
|
+
errors.push('no rooms defined \u2014 declare at least one: room id "Label" at 0,0 size 4x3');
|
|
37840
|
+
}
|
|
37841
|
+
const dims = [];
|
|
37842
|
+
if (rooms.length > 0) {
|
|
37843
|
+
dims.push({
|
|
37844
|
+
vertical: false,
|
|
37845
|
+
at: minY - FLOORPLAN_CONST.dimMajorOff,
|
|
37846
|
+
lo: minX,
|
|
37847
|
+
hi: maxX,
|
|
37848
|
+
label: formatLength(maxX - minX, ast.unit),
|
|
37849
|
+
minor: false
|
|
37850
|
+
});
|
|
37851
|
+
dims.push({
|
|
37852
|
+
vertical: true,
|
|
37853
|
+
at: minX - FLOORPLAN_CONST.dimMajorOff,
|
|
37854
|
+
lo: minY,
|
|
37855
|
+
hi: maxY,
|
|
37856
|
+
label: formatLength(maxY - minY, ast.unit),
|
|
37857
|
+
minor: false
|
|
37858
|
+
});
|
|
37859
|
+
const topSegs = [];
|
|
37860
|
+
for (const r6 of rooms) {
|
|
37861
|
+
for (const sg of sideSegments(r6, "north")) {
|
|
37862
|
+
if (Math.abs(sg.along - minY) < 0.01) topSegs.push({ lo: sg.lo, hi: sg.hi });
|
|
37863
|
+
}
|
|
37864
|
+
}
|
|
37865
|
+
if (topSegs.length > 1) {
|
|
37866
|
+
for (const sg of topSegs.sort((a, b) => a.lo - b.lo)) {
|
|
37867
|
+
dims.push({
|
|
37868
|
+
vertical: false,
|
|
37869
|
+
at: minY - FLOORPLAN_CONST.dimMinorOff,
|
|
37870
|
+
lo: sg.lo,
|
|
37871
|
+
hi: sg.hi,
|
|
37872
|
+
label: formatLength(sg.hi - sg.lo, ast.unit),
|
|
37873
|
+
minor: true
|
|
37874
|
+
});
|
|
37875
|
+
}
|
|
37876
|
+
}
|
|
37877
|
+
const leftSegs = [];
|
|
37878
|
+
for (const r6 of rooms) {
|
|
37879
|
+
for (const sg of sideSegments(r6, "west")) {
|
|
37880
|
+
if (Math.abs(sg.along - minX) < 0.01) leftSegs.push({ lo: sg.lo, hi: sg.hi });
|
|
37881
|
+
}
|
|
37882
|
+
}
|
|
37883
|
+
if (leftSegs.length > 1) {
|
|
37884
|
+
for (const sg of leftSegs.sort((a, b) => a.lo - b.lo)) {
|
|
37885
|
+
dims.push({
|
|
37886
|
+
vertical: true,
|
|
37887
|
+
at: minX - FLOORPLAN_CONST.dimMinorOff,
|
|
37888
|
+
lo: sg.lo,
|
|
37889
|
+
hi: sg.hi,
|
|
37890
|
+
label: formatLength(sg.hi - sg.lo, ast.unit),
|
|
37891
|
+
minor: true
|
|
37892
|
+
});
|
|
37893
|
+
}
|
|
37894
|
+
}
|
|
37895
|
+
}
|
|
37896
|
+
return {
|
|
37897
|
+
title: ast.title,
|
|
37898
|
+
unit: ast.unit,
|
|
37899
|
+
north: ast.north,
|
|
37900
|
+
rooms,
|
|
37901
|
+
seams,
|
|
37902
|
+
openings,
|
|
37903
|
+
items,
|
|
37904
|
+
dims,
|
|
37905
|
+
bounds: { minX, minY, maxX, maxY },
|
|
37906
|
+
wallT: FLOORPLAN_CONST.wallT,
|
|
37907
|
+
totalAreaM2: rooms.reduce((s, r6) => s + r6.areaM2, 0),
|
|
37908
|
+
errors,
|
|
37909
|
+
warnings,
|
|
37910
|
+
warnItems: [...warnItems]
|
|
37911
|
+
};
|
|
37912
|
+
}
|
|
37913
|
+
function resolveOpening(op, rooms, byId, u, unit, errors, warnings) {
|
|
37914
|
+
let seg = null;
|
|
37915
|
+
let owner = -1;
|
|
37916
|
+
let negRoom;
|
|
37917
|
+
let posRoom;
|
|
37918
|
+
let inward = 1;
|
|
37919
|
+
if (op.between) {
|
|
37920
|
+
const ia = byId.get(op.between[0]);
|
|
37921
|
+
const ib = byId.get(op.between[1]);
|
|
37922
|
+
if (ia === void 0 || ib === void 0) {
|
|
37923
|
+
errors.push(`${op.kind}: unknown room "${ia === void 0 ? op.between[0] : op.between[1]}"`);
|
|
37924
|
+
return null;
|
|
37925
|
+
}
|
|
37926
|
+
const a = rooms[ia];
|
|
37927
|
+
const b = rooms[ib];
|
|
37928
|
+
const found = roomSharedEdge(a, b);
|
|
37929
|
+
if (!found) {
|
|
37930
|
+
const gapX = Math.max(a.x, b.x) - Math.min(a.x + a.w, b.x + b.w);
|
|
37931
|
+
const gapY = Math.max(a.y, b.y) - Math.min(a.y + a.h, b.y + b.h);
|
|
37932
|
+
const axis = gapX >= gapY ? "x" : "y";
|
|
37933
|
+
const gap = Math.max(gapX, gapY);
|
|
37934
|
+
errors.push(
|
|
37935
|
+
gap > 0 ? `${op.kind} between "${a.id}" and "${b.id}": rooms share no wall (gap ${fmtNum(gap)} m on ${axis}-axis)` : `${op.kind} between "${a.id}" and "${b.id}": rooms share no wall`
|
|
37936
|
+
);
|
|
37937
|
+
return null;
|
|
37938
|
+
}
|
|
37939
|
+
seg = found.edge;
|
|
37940
|
+
owner = ia;
|
|
37941
|
+
if (seg.vertical) {
|
|
37942
|
+
const aIsNeg = found.aPart.x + found.aPart.w / 2 < seg.along;
|
|
37943
|
+
negRoom = aIsNeg ? ia : ib;
|
|
37944
|
+
posRoom = aIsNeg ? ib : ia;
|
|
37945
|
+
inward = aIsNeg ? -1 : 1;
|
|
37946
|
+
} else {
|
|
37947
|
+
const aIsNeg = found.aPart.y + found.aPart.h / 2 < seg.along;
|
|
37948
|
+
negRoom = aIsNeg ? ia : ib;
|
|
37949
|
+
posRoom = aIsNeg ? ib : ia;
|
|
37950
|
+
inward = aIsNeg ? -1 : 1;
|
|
37951
|
+
}
|
|
37952
|
+
} else {
|
|
37953
|
+
const idx = byId.get(op.room);
|
|
37954
|
+
if (idx === void 0) {
|
|
37955
|
+
errors.push(`${op.kind}: unknown room "${op.room}"`);
|
|
37956
|
+
return null;
|
|
37957
|
+
}
|
|
37958
|
+
owner = idx;
|
|
37959
|
+
const r6 = rooms[idx];
|
|
37960
|
+
const side = op.side;
|
|
37961
|
+
const segs = sideSegments(r6, side);
|
|
37962
|
+
if (segs.length === 0) {
|
|
37963
|
+
errors.push(`${op.kind} on "${r6.id}" ${side}: that side has no exterior wall segment`);
|
|
37964
|
+
return null;
|
|
37965
|
+
}
|
|
37966
|
+
const total = segs.reduce((s, sg) => s + (sg.hi - sg.lo), 0);
|
|
37967
|
+
const pct2 = Math.min(100, Math.max(0, op.pct));
|
|
37968
|
+
let target = total * pct2 / 100;
|
|
37969
|
+
let chosen = segs[segs.length - 1];
|
|
37970
|
+
for (const sg of segs) {
|
|
37971
|
+
const len = sg.hi - sg.lo;
|
|
37972
|
+
if (target <= len) {
|
|
37973
|
+
chosen = sg;
|
|
37974
|
+
break;
|
|
37975
|
+
}
|
|
37976
|
+
target -= len;
|
|
37977
|
+
}
|
|
37978
|
+
const within = Math.min(1, Math.max(0, target / (chosen.hi - chosen.lo)));
|
|
37979
|
+
op = { ...op, pct: within * 100 };
|
|
37980
|
+
seg = { vertical: side === "west" || side === "east", along: chosen.along, lo: chosen.lo, hi: chosen.hi };
|
|
37981
|
+
switch (side) {
|
|
37982
|
+
case "north":
|
|
37983
|
+
inward = 1;
|
|
37984
|
+
posRoom = idx;
|
|
37985
|
+
break;
|
|
37986
|
+
case "south":
|
|
37987
|
+
inward = -1;
|
|
37988
|
+
negRoom = idx;
|
|
37989
|
+
break;
|
|
37990
|
+
case "west":
|
|
37991
|
+
inward = 1;
|
|
37992
|
+
posRoom = idx;
|
|
37993
|
+
break;
|
|
37994
|
+
case "east":
|
|
37995
|
+
inward = -1;
|
|
37996
|
+
negRoom = idx;
|
|
37997
|
+
break;
|
|
37998
|
+
}
|
|
37999
|
+
}
|
|
38000
|
+
const jamb = FLOORPLAN_CONST.jamb;
|
|
38001
|
+
const segLen = seg.hi - seg.lo;
|
|
38002
|
+
const avail = segLen - 2 * jamb;
|
|
38003
|
+
let wd = op.width * u;
|
|
38004
|
+
if (wd > avail) {
|
|
38005
|
+
warnings.push(
|
|
38006
|
+
`${op.kind}${op.room ? ` on "${op.room}" ${op.side}` : op.between ? ` between "${op.between[0]}" and "${op.between[1]}"` : ""}: width ${formatLength(wd, unit)} clamped to ${formatLength(avail, unit)} to fit the wall segment`
|
|
38007
|
+
);
|
|
38008
|
+
wd = avail;
|
|
38009
|
+
}
|
|
38010
|
+
const pct = Math.min(100, Math.max(0, op.pct));
|
|
38011
|
+
const c = seg.lo + segLen * pct / 100;
|
|
38012
|
+
const lo = Math.max(seg.lo + jamb, Math.min(c - wd / 2, seg.hi - jamb - wd));
|
|
38013
|
+
const hi = lo + wd;
|
|
38014
|
+
const arcDir = op.kind === "door" && op.swing === "out" ? inward === 1 ? -1 : 1 : inward;
|
|
38015
|
+
return {
|
|
38016
|
+
kind: op.kind,
|
|
38017
|
+
doorType: op.doorType,
|
|
38018
|
+
windowType: op.windowType,
|
|
38019
|
+
vertical: seg.vertical,
|
|
38020
|
+
along: seg.along,
|
|
38021
|
+
lo,
|
|
38022
|
+
hi,
|
|
38023
|
+
inward: arcDir,
|
|
38024
|
+
hinge: op.hinge,
|
|
38025
|
+
negRoom,
|
|
38026
|
+
posRoom,
|
|
38027
|
+
owner
|
|
38028
|
+
};
|
|
38029
|
+
}
|
|
38030
|
+
|
|
38031
|
+
// src/diagrams/floorplan/renderer.ts
|
|
38032
|
+
var r24 = (n) => Math.round(n * 100) / 100;
|
|
38033
|
+
function buildCss13(t) {
|
|
38034
|
+
return `
|
|
38035
|
+
.sx-fp { font-family: ${DEFAULT_FONT_FAMILY}; }
|
|
38036
|
+
.sx-fp-title { font: ${TITLE.weight} ${TITLE.size}px sans-serif; fill: ${t.text}; }
|
|
38037
|
+
.sx-fp-wall { fill: ${t.wallFill}; stroke: none; }
|
|
38038
|
+
.sx-fp-furn { fill: ${t.furnFill}; stroke: ${t.furnStroke}; stroke-width: 1.2; }
|
|
38039
|
+
.sx-fp-furn-nofill { fill: none; stroke: ${t.furnStroke}; stroke-width: 1.2; }
|
|
38040
|
+
.sx-fp-furn-line { fill: none; stroke: ${t.furnStroke}; stroke-width: 1; }
|
|
38041
|
+
.sx-fp-furn-dash { fill: none; stroke: ${t.furnStroke}; stroke-width: 1; stroke-dasharray: 4 3; }
|
|
38042
|
+
.sx-fp-furn-dot { fill: ${t.furnStroke}; stroke: none; }
|
|
38043
|
+
.sx-fp-furn-solid { fill: ${t.furnSolid}; stroke: none; }
|
|
38044
|
+
.sx-fp-board-inner { fill: ${t.boardInner}; stroke: none; }
|
|
38045
|
+
.sx-fp-chair { fill: ${t.chairFill}; stroke: ${t.furnStroke}; stroke-width: 1; }
|
|
38046
|
+
.sx-fp-rug { fill: none; stroke: ${t.rugStroke}; stroke-width: 1.2; stroke-dasharray: 5 4; }
|
|
38047
|
+
.sx-fp-hatch { fill: none; stroke: ${t.hatchStroke}; stroke-width: 1; }
|
|
38048
|
+
.sx-fp-furn-text { font-weight: 600; font-family: sans-serif; fill: ${t.furnLabel}; paint-order: stroke; stroke: ${t.floorFill}; stroke-width: 2.5px; stroke-linejoin: round; }
|
|
38049
|
+
.sx-fp-furn-label { font: 11px sans-serif; fill: ${t.furnLabel}; paint-order: stroke; stroke: ${t.floorFill}; stroke-width: 3px; stroke-linejoin: round; }
|
|
38050
|
+
.sx-fp-door-leaf { fill: none; stroke: ${t.doorLeaf}; stroke-width: 1.6; }
|
|
38051
|
+
.sx-fp-door-arc { fill: none; stroke: ${t.doorArc}; stroke-width: 1; }
|
|
38052
|
+
.sx-fp-window { fill: none; stroke: ${t.windowStroke}; stroke-width: 1.3; }
|
|
38053
|
+
.sx-fp-jamb { fill: none; stroke: ${t.furnStroke}; stroke-width: 1.2; }
|
|
38054
|
+
.sx-fp-room-name { font: 600 13.5px sans-serif; fill: ${t.roomName}; paint-order: stroke; stroke: ${t.floorFill}; stroke-width: 3px; stroke-linejoin: round; }
|
|
38055
|
+
.sx-fp-room-area { font: 11px sans-serif; fill: ${t.roomArea}; paint-order: stroke; stroke: ${t.floorFill}; stroke-width: 3px; stroke-linejoin: round; }
|
|
38056
|
+
.sx-fp-stair-break { fill: none; stroke: ${t.furnStroke}; stroke-width: 1.6; }
|
|
38057
|
+
.sx-fp-compass { fill: none; stroke: ${t.dimStroke}; stroke-width: 1.2; }
|
|
38058
|
+
.sx-fp-compass-n { font: 700 11px sans-serif; fill: ${t.dimText}; }
|
|
38059
|
+
.sx-fp-dim { fill: none; stroke: ${t.dimStroke}; stroke-width: 1; }
|
|
38060
|
+
.sx-fp-dim-text { font: 10.5px sans-serif; fill: ${t.dimText}; }
|
|
38061
|
+
.sx-fp-dim-text-minor { font: 9px sans-serif; fill: ${t.dimText}; }
|
|
38062
|
+
.sx-fp-warn-item { fill: none; stroke: ${t.negative}; stroke-width: 1.5; stroke-dasharray: 4 3; }
|
|
38063
|
+
.sx-fp-warn { font: 11px ui-monospace, Menlo, monospace; fill: ${t.warn}; }
|
|
38064
|
+
.sx-fp-error-box { fill: ${t.bg}; stroke: ${t.negative}; stroke-width: 1.5; }
|
|
38065
|
+
.sx-fp-error-title { font: 700 13px ui-monospace, Menlo, monospace; fill: ${t.negative}; }
|
|
38066
|
+
.sx-fp-error-line { font: 12px ui-monospace, Menlo, monospace; fill: ${t.negative}; }
|
|
38067
|
+
`.trim();
|
|
38068
|
+
}
|
|
38069
|
+
function renderErrorPanel(lay, t) {
|
|
38070
|
+
const lines = lay.errors;
|
|
38071
|
+
const w = Math.max(560, ...lines.map((l) => l.length * 6.6 + 48));
|
|
38072
|
+
const h = 56 + lines.length * 19;
|
|
38073
|
+
return svgRoot(
|
|
38074
|
+
{ viewBox: `0 0 ${r24(w)} ${h}`, class: "sx-fp", role: "img" },
|
|
38075
|
+
[
|
|
38076
|
+
title(lay.title),
|
|
38077
|
+
desc(`Floor plan validation failed with ${lines.length} error${lines.length === 1 ? "" : "s"}.`),
|
|
38078
|
+
el("style", {}, buildCss13(t)),
|
|
38079
|
+
rect({ class: "sx-fp-error-box", x: 1, y: 1, width: r24(w - 2), height: h - 2, rx: 6 }),
|
|
38080
|
+
text({ class: "sx-fp-error-title", x: 16, y: 26 }, `floorplan: ${lines.length} validation error${lines.length === 1 ? "" : "s"}`),
|
|
38081
|
+
...lines.map((e, i) => text({ class: "sx-fp-error-line", x: 16, y: 50 + i * 19 }, `\u26A0 ${e}`))
|
|
38082
|
+
]
|
|
38083
|
+
);
|
|
38084
|
+
}
|
|
38085
|
+
function gapFill(lay, t, roomIdx) {
|
|
38086
|
+
if (roomIdx === void 0) return t.bg;
|
|
38087
|
+
return lay.rooms[roomIdx]?.fill ?? t.floorFill;
|
|
38088
|
+
}
|
|
38089
|
+
function punchGap(o, lay, c) {
|
|
38090
|
+
const tpx = c.px(c.wallT);
|
|
38091
|
+
const negFill = gapFill(lay, c.t, o.negRoom);
|
|
38092
|
+
const posFill = gapFill(lay, c.t, o.posRoom);
|
|
38093
|
+
if (o.vertical) {
|
|
38094
|
+
const x2 = c.X(o.along);
|
|
38095
|
+
const y2 = c.Y(o.lo);
|
|
38096
|
+
const h = c.px(o.hi - o.lo);
|
|
38097
|
+
return rect({ class: "sx-fp-gap", fill: negFill, x: r24(x2 - tpx / 2 - 0.5), y: y2, width: r24(tpx / 2 + 0.5), height: h }) + rect({ class: "sx-fp-gap", fill: posFill, x: x2, y: y2, width: r24(tpx / 2 + 0.5), height: h });
|
|
38098
|
+
}
|
|
38099
|
+
const y = c.Y(o.along);
|
|
38100
|
+
const x = c.X(o.lo);
|
|
38101
|
+
const w = c.px(o.hi - o.lo);
|
|
38102
|
+
return rect({ class: "sx-fp-gap", fill: negFill, x, y: r24(y - tpx / 2 - 0.5), width: w, height: r24(tpx / 2 + 0.5) }) + rect({ class: "sx-fp-gap", fill: posFill, x, y, width: w, height: r24(tpx / 2 + 0.5) });
|
|
38103
|
+
}
|
|
38104
|
+
function windowSymbol(o, c) {
|
|
38105
|
+
const tpx = c.px(c.wallT);
|
|
38106
|
+
const parts = [];
|
|
38107
|
+
const outward = o.inward === 1 ? -1 : 1;
|
|
38108
|
+
if (o.vertical) {
|
|
38109
|
+
const x = c.X(o.along);
|
|
38110
|
+
const mid = c.Y((o.lo + o.hi) / 2);
|
|
38111
|
+
if (o.windowType === "sliding") {
|
|
38112
|
+
parts.push(line({ class: "sx-fp-window", x1: r24(x - tpx * 0.22), y1: c.Y(o.lo), x2: r24(x - tpx * 0.22), y2: mid }));
|
|
38113
|
+
parts.push(line({ class: "sx-fp-window", x1: r24(x + tpx * 0.22), y1: mid, x2: r24(x + tpx * 0.22), y2: c.Y(o.hi) }));
|
|
38114
|
+
} else {
|
|
38115
|
+
for (const k of [-1, 0, 1]) {
|
|
38116
|
+
parts.push(line({ class: "sx-fp-window", x1: r24(x + k * tpx * 0.38), y1: c.Y(o.lo), x2: r24(x + k * tpx * 0.38), y2: c.Y(o.hi) }));
|
|
38117
|
+
}
|
|
38118
|
+
}
|
|
38119
|
+
for (const yy of [o.lo, o.hi]) {
|
|
38120
|
+
parts.push(line({ class: "sx-fp-window", x1: r24(x - tpx / 2), y1: c.Y(yy), x2: r24(x + tpx / 2), y2: c.Y(yy) }));
|
|
38121
|
+
}
|
|
38122
|
+
if (o.windowType === "casement") {
|
|
38123
|
+
const wd = c.px(o.hi - o.lo);
|
|
38124
|
+
const leafX = r24(x + outward * wd);
|
|
38125
|
+
const sweep = outward === 1 ? 1 : 0;
|
|
38126
|
+
parts.push(line({ class: "sx-fp-door-leaf", x1: x, y1: c.Y(o.lo), x2: leafX, y2: c.Y(o.lo) }));
|
|
38127
|
+
parts.push(path({ class: "sx-fp-door-arc", d: `M ${leafX} ${c.Y(o.lo)} A ${wd} ${wd} 0 0 ${sweep} ${x} ${r24(c.Y(o.lo) + wd)}` }));
|
|
38128
|
+
}
|
|
38129
|
+
if (o.windowType === "bay") {
|
|
38130
|
+
const proj = c.px(0.45) * outward;
|
|
38131
|
+
const splay = c.px(0.3);
|
|
38132
|
+
const x0 = r24(x + outward * tpx / 2);
|
|
38133
|
+
parts.push(
|
|
38134
|
+
path({
|
|
38135
|
+
class: "sx-fp-window",
|
|
38136
|
+
d: `M ${x0} ${c.Y(o.lo)} L ${r24(x0 + proj)} ${r24(c.Y(o.lo) + splay)} L ${r24(x0 + proj)} ${r24(c.Y(o.hi) - splay)} L ${x0} ${c.Y(o.hi)}`
|
|
38137
|
+
})
|
|
38138
|
+
);
|
|
38139
|
+
}
|
|
38140
|
+
} else {
|
|
38141
|
+
const y = c.Y(o.along);
|
|
38142
|
+
const mid = c.X((o.lo + o.hi) / 2);
|
|
38143
|
+
if (o.windowType === "sliding") {
|
|
38144
|
+
parts.push(line({ class: "sx-fp-window", x1: c.X(o.lo), y1: r24(y - tpx * 0.22), x2: mid, y2: r24(y - tpx * 0.22) }));
|
|
38145
|
+
parts.push(line({ class: "sx-fp-window", x1: mid, y1: r24(y + tpx * 0.22), x2: c.X(o.hi), y2: r24(y + tpx * 0.22) }));
|
|
38146
|
+
} else {
|
|
38147
|
+
for (const k of [-1, 0, 1]) {
|
|
38148
|
+
parts.push(line({ class: "sx-fp-window", x1: c.X(o.lo), y1: r24(y + k * tpx * 0.38), x2: c.X(o.hi), y2: r24(y + k * tpx * 0.38) }));
|
|
38149
|
+
}
|
|
38150
|
+
}
|
|
38151
|
+
for (const xx of [o.lo, o.hi]) {
|
|
38152
|
+
parts.push(line({ class: "sx-fp-window", x1: c.X(xx), y1: r24(y - tpx / 2), x2: c.X(xx), y2: r24(y + tpx / 2) }));
|
|
38153
|
+
}
|
|
38154
|
+
if (o.windowType === "casement") {
|
|
38155
|
+
const wd = c.px(o.hi - o.lo);
|
|
38156
|
+
const leafY = r24(y + outward * wd);
|
|
38157
|
+
const sweep = outward === 1 ? 0 : 1;
|
|
38158
|
+
parts.push(line({ class: "sx-fp-door-leaf", x1: c.X(o.lo), y1: y, x2: c.X(o.lo), y2: leafY }));
|
|
38159
|
+
parts.push(path({ class: "sx-fp-door-arc", d: `M ${c.X(o.lo)} ${leafY} A ${wd} ${wd} 0 0 ${sweep} ${r24(c.X(o.lo) + wd)} ${y}` }));
|
|
38160
|
+
}
|
|
38161
|
+
if (o.windowType === "bay") {
|
|
38162
|
+
const proj = c.px(0.45) * outward;
|
|
38163
|
+
const splay = c.px(0.3);
|
|
38164
|
+
const y0 = r24(y + outward * tpx / 2);
|
|
38165
|
+
parts.push(
|
|
38166
|
+
path({
|
|
38167
|
+
class: "sx-fp-window",
|
|
38168
|
+
d: `M ${c.X(o.lo)} ${y0} L ${r24(c.X(o.lo) + splay)} ${r24(y0 + proj)} L ${r24(c.X(o.hi) - splay)} ${r24(y0 + proj)} L ${c.X(o.hi)} ${y0}`
|
|
38169
|
+
})
|
|
38170
|
+
);
|
|
38171
|
+
}
|
|
38172
|
+
}
|
|
38173
|
+
return parts.join("");
|
|
38174
|
+
}
|
|
38175
|
+
function jambLines(o, c) {
|
|
38176
|
+
const tpx = c.px(c.wallT);
|
|
38177
|
+
if (o.vertical) {
|
|
38178
|
+
const x = c.X(o.along);
|
|
38179
|
+
return [o.lo, o.hi].map((yy) => line({ class: "sx-fp-jamb", x1: r24(x - tpx / 2), y1: c.Y(yy), x2: r24(x + tpx / 2), y2: c.Y(yy) })).join("");
|
|
38180
|
+
}
|
|
38181
|
+
const y = c.Y(o.along);
|
|
38182
|
+
return [o.lo, o.hi].map((xx) => line({ class: "sx-fp-jamb", x1: c.X(xx), y1: r24(y - tpx / 2), x2: c.X(xx), y2: r24(y + tpx / 2) })).join("");
|
|
38183
|
+
}
|
|
38184
|
+
function swingLeaf(o, c, hingeAtLo, widthM) {
|
|
38185
|
+
const dir = o.inward;
|
|
38186
|
+
const rpx = c.px(widthM);
|
|
38187
|
+
if (o.vertical) {
|
|
38188
|
+
const x = c.X(o.along);
|
|
38189
|
+
const hy = hingeAtLo ? c.Y(o.lo) : c.Y(o.hi);
|
|
38190
|
+
const sy = hingeAtLo ? 1 : -1;
|
|
38191
|
+
const leafX = r24(x + dir * rpx);
|
|
38192
|
+
const sweep2 = dir === 1 === hingeAtLo ? 1 : 0;
|
|
38193
|
+
return line({ class: "sx-fp-door-leaf", x1: x, y1: hy, x2: leafX, y2: hy }) + path({ class: "sx-fp-door-arc", d: `M ${leafX} ${hy} A ${rpx} ${rpx} 0 0 ${sweep2} ${x} ${r24(hy + sy * rpx)}` });
|
|
38194
|
+
}
|
|
38195
|
+
const y = c.Y(o.along);
|
|
38196
|
+
const hx = hingeAtLo ? c.X(o.lo) : c.X(o.hi);
|
|
38197
|
+
const sx = hingeAtLo ? 1 : -1;
|
|
38198
|
+
const leafY = r24(y + dir * rpx);
|
|
38199
|
+
const sweep = dir === 1 !== hingeAtLo ? 1 : 0;
|
|
38200
|
+
return line({ class: "sx-fp-door-leaf", x1: hx, y1: y, x2: hx, y2: leafY }) + path({ class: "sx-fp-door-arc", d: `M ${hx} ${leafY} A ${rpx} ${rpx} 0 0 ${sweep} ${r24(hx + sx * rpx)} ${y}` });
|
|
38201
|
+
}
|
|
38202
|
+
function doorSymbol(o, c) {
|
|
38203
|
+
const wd = o.hi - o.lo;
|
|
38204
|
+
if (o.doorType === "double") {
|
|
38205
|
+
const half = { ...o };
|
|
38206
|
+
const mid = (o.lo + o.hi) / 2;
|
|
38207
|
+
const left = { ...half, hi: mid };
|
|
38208
|
+
const right = { ...half, lo: mid };
|
|
38209
|
+
return jambLines(o, c) + swingLeaf(left, c, true, wd / 2) + swingLeaf(right, c, false, wd / 2);
|
|
38210
|
+
}
|
|
38211
|
+
if (o.doorType === "bifold") {
|
|
38212
|
+
const peak = c.px((o.hi - o.lo) * 0.22) * o.inward;
|
|
38213
|
+
const q1 = o.lo + (o.hi - o.lo) * 0.25;
|
|
38214
|
+
const mid = (o.lo + o.hi) / 2;
|
|
38215
|
+
const q3 = o.lo + (o.hi - o.lo) * 0.75;
|
|
38216
|
+
const parts = [jambLines(o, c)];
|
|
38217
|
+
if (o.vertical) {
|
|
38218
|
+
const x = c.X(o.along);
|
|
38219
|
+
parts.push(
|
|
38220
|
+
path({
|
|
38221
|
+
class: "sx-fp-door-leaf",
|
|
38222
|
+
d: `M ${x} ${c.Y(o.lo)} L ${r24(x + peak)} ${c.Y(q1)} L ${x} ${c.Y(mid)} L ${r24(x + peak)} ${c.Y(q3)} L ${x} ${c.Y(o.hi)}`
|
|
38223
|
+
})
|
|
38224
|
+
);
|
|
38225
|
+
} else {
|
|
38226
|
+
const y = c.Y(o.along);
|
|
38227
|
+
parts.push(
|
|
38228
|
+
path({
|
|
38229
|
+
class: "sx-fp-door-leaf",
|
|
38230
|
+
d: `M ${c.X(o.lo)} ${y} L ${c.X(q1)} ${r24(y + peak)} L ${c.X(mid)} ${y} L ${c.X(q3)} ${r24(y + peak)} L ${c.X(o.hi)} ${y}`
|
|
38231
|
+
})
|
|
38232
|
+
);
|
|
38233
|
+
}
|
|
38234
|
+
return parts.join("");
|
|
38235
|
+
}
|
|
38236
|
+
if (o.doorType === "sliding" || o.doorType === "pocket") {
|
|
38237
|
+
const off = c.px(c.wallT) * 0.28;
|
|
38238
|
+
const mid = (o.lo + o.hi) / 2;
|
|
38239
|
+
const parts = [jambLines(o, c)];
|
|
38240
|
+
if (o.vertical) {
|
|
38241
|
+
const x = c.X(o.along);
|
|
38242
|
+
parts.push(line({ class: "sx-fp-door-leaf", x1: r24(x - off), y1: c.Y(o.lo), x2: r24(x - off), y2: c.Y(mid) }));
|
|
38243
|
+
if (o.doorType === "sliding") {
|
|
38244
|
+
parts.push(line({ class: "sx-fp-door-leaf", x1: r24(x + off), y1: c.Y(mid), x2: r24(x + off), y2: c.Y(o.hi) }));
|
|
38245
|
+
}
|
|
38246
|
+
} else {
|
|
38247
|
+
const y = c.Y(o.along);
|
|
38248
|
+
parts.push(line({ class: "sx-fp-door-leaf", x1: c.X(o.lo), y1: r24(y - off), x2: c.X(mid), y2: r24(y - off) }));
|
|
38249
|
+
if (o.doorType === "sliding") {
|
|
38250
|
+
parts.push(line({ class: "sx-fp-door-leaf", x1: c.X(mid), y1: r24(y + off), x2: c.X(o.hi), y2: r24(y + off) }));
|
|
38251
|
+
}
|
|
38252
|
+
}
|
|
38253
|
+
return parts.join("");
|
|
38254
|
+
}
|
|
38255
|
+
return jambLines(o, c) + swingLeaf(o, c, o.hinge !== "right", wd);
|
|
38256
|
+
}
|
|
38257
|
+
function renderDim(d, c) {
|
|
38258
|
+
const cls = d.minor ? "sx-fp-dim-text-minor" : "sx-fp-dim-text";
|
|
38259
|
+
const tick = 3.6;
|
|
38260
|
+
if (!d.vertical) {
|
|
38261
|
+
const y = c.Y(d.at);
|
|
38262
|
+
const x1 = c.X(d.lo);
|
|
38263
|
+
const x2 = c.X(d.hi);
|
|
38264
|
+
return line({ class: "sx-fp-dim", x1, y1: y, x2, y2: y }) + line({ class: "sx-fp-dim", x1: r24(x1 - tick), y1: r24(y + tick), x2: r24(x1 + tick), y2: r24(y - tick) }) + line({ class: "sx-fp-dim", x1: r24(x2 - tick), y1: r24(y + tick), x2: r24(x2 + tick), y2: r24(y - tick) }) + text({ class: cls, x: r24((x1 + x2) / 2), y: r24(y - 4), "text-anchor": "middle" }, d.label);
|
|
38265
|
+
}
|
|
38266
|
+
const x = c.X(d.at);
|
|
38267
|
+
const y1 = c.Y(d.lo);
|
|
38268
|
+
const y2 = c.Y(d.hi);
|
|
38269
|
+
const mid = r24((y1 + y2) / 2);
|
|
38270
|
+
return line({ class: "sx-fp-dim", x1: x, y1, x2: x, y2 }) + line({ class: "sx-fp-dim", x1: r24(x - tick), y1: r24(y1 + tick), x2: r24(x + tick), y2: r24(y1 - tick) }) + line({ class: "sx-fp-dim", x1: r24(x - tick), y1: r24(y2 + tick), x2: r24(x + tick), y2: r24(y2 - tick) }) + text(
|
|
38271
|
+
{ class: cls, x: r24(x - 5), y: mid, "text-anchor": "middle", transform: `rotate(-90 ${r24(x - 5)} ${mid})` },
|
|
38272
|
+
d.label
|
|
38273
|
+
);
|
|
38274
|
+
}
|
|
38275
|
+
function renderFloorplanLayout(lay, config) {
|
|
38276
|
+
const t = resolveFloorplanTheme(config?.theme ?? "default");
|
|
38277
|
+
if (lay.errors.length > 0) return renderErrorPanel(lay, t);
|
|
38278
|
+
const scale = FLOORPLAN_CONST.scale;
|
|
38279
|
+
const px = (m) => r24(m * scale);
|
|
38280
|
+
const band = FLOORPLAN_CONST.dimBand + FLOORPLAN_CONST.pad;
|
|
38281
|
+
let outward = 0;
|
|
38282
|
+
for (const o of lay.openings) {
|
|
38283
|
+
if (o.kind !== "window") continue;
|
|
38284
|
+
if (o.windowType === "casement") outward = Math.max(outward, o.hi - o.lo + 0.1);
|
|
38285
|
+
if (o.windowType === "bay") outward = Math.max(outward, 0.6);
|
|
38286
|
+
}
|
|
38287
|
+
const tail = FLOORPLAN_CONST.pad + lay.wallT + outward;
|
|
38288
|
+
const ox = -lay.bounds.minX + band;
|
|
38289
|
+
const oy = -lay.bounds.minY + band;
|
|
38290
|
+
const X = (m) => px(m + ox);
|
|
38291
|
+
const Y = (m) => px(m + oy);
|
|
38292
|
+
const ctx = { X, Y, px, t, wallT: lay.wallT };
|
|
38293
|
+
const titleH = TITLE.bandH;
|
|
38294
|
+
const warnH = lay.warnings.length ? lay.warnings.length * 17 + 10 : 0;
|
|
38295
|
+
const W2 = px(lay.bounds.maxX - lay.bounds.minX + band + tail);
|
|
38296
|
+
const H2 = px(lay.bounds.maxY - lay.bounds.minY + band + tail) + titleH + warnH;
|
|
38297
|
+
const titleX = r24(X((lay.bounds.minX + lay.bounds.maxX) / 2));
|
|
38298
|
+
const floors = [];
|
|
38299
|
+
const furniture = [];
|
|
38300
|
+
const walls = [];
|
|
38301
|
+
const openings = [];
|
|
38302
|
+
const labels = [];
|
|
38303
|
+
const dims = [];
|
|
38304
|
+
for (const r6 of lay.rooms) {
|
|
38305
|
+
floors.push(
|
|
38306
|
+
group(
|
|
38307
|
+
{ class: "sx-fp-floor", "data-room": r6.id },
|
|
38308
|
+
r6.parts.map(
|
|
38309
|
+
(p) => rect({
|
|
38310
|
+
fill: r6.fill ?? t.floorFill,
|
|
38311
|
+
x: X(p.x),
|
|
38312
|
+
y: Y(p.y),
|
|
38313
|
+
width: px(p.w),
|
|
38314
|
+
height: px(p.h)
|
|
38315
|
+
})
|
|
38316
|
+
)
|
|
38317
|
+
)
|
|
38318
|
+
);
|
|
38319
|
+
if (!r6.nolabel) {
|
|
38320
|
+
const main = r6.parts.reduce((a, b) => b.w * b.h > a.w * a.h ? b : a);
|
|
38321
|
+
const cx = X(main.x + main.w / 2);
|
|
38322
|
+
const cy = Y(main.y + main.h / 2);
|
|
38323
|
+
labels.push(text({ class: "sx-fp-room-name", x: cx, y: r24(cy - 3), "text-anchor": "middle" }, r6.label));
|
|
38324
|
+
labels.push(text({ class: "sx-fp-room-area", x: cx, y: r24(cy + 13), "text-anchor": "middle" }, r6.areaText));
|
|
38325
|
+
}
|
|
38326
|
+
}
|
|
38327
|
+
const warnSet = new Set(lay.warnItems);
|
|
38328
|
+
lay.items.forEach((it, idx) => {
|
|
38329
|
+
const def = FLOORPLAN_SYMBOLS[it.type];
|
|
38330
|
+
const wpx = px(it.w);
|
|
38331
|
+
const hpx = px(it.h);
|
|
38332
|
+
const cx = r24(X(it.x) + wpx / 2);
|
|
38333
|
+
const cy = r24(Y(it.y) + hpx / 2);
|
|
38334
|
+
const rot = Math.round(it.rotate * 10) / 10;
|
|
38335
|
+
const children = [def.draw({ w: it.w, h: it.h, px, label: it.label })];
|
|
38336
|
+
if (warnSet.has(idx)) {
|
|
38337
|
+
children.push(rect({ class: "sx-fp-warn-item", x: -1, y: -1, width: r24(wpx + 2), height: r24(hpx + 2) }));
|
|
38338
|
+
}
|
|
38339
|
+
furniture.push(
|
|
38340
|
+
group(
|
|
38341
|
+
{
|
|
38342
|
+
class: "sx-fp-item",
|
|
38343
|
+
"data-furniture": it.type,
|
|
38344
|
+
transform: `translate(${cx},${cy})${rot ? ` rotate(${rot})` : ""} translate(${r24(-wpx / 2)},${r24(-hpx / 2)})`
|
|
38345
|
+
},
|
|
38346
|
+
children
|
|
38347
|
+
)
|
|
38348
|
+
);
|
|
38349
|
+
if (it.label && !def.consumesLabel) {
|
|
38350
|
+
labels.push(text({ class: "sx-fp-furn-label", x: cx, y: r24(cy + 4), "text-anchor": "middle" }, it.label));
|
|
38351
|
+
}
|
|
38352
|
+
});
|
|
38353
|
+
const tw = lay.wallT;
|
|
38354
|
+
for (const r6 of lay.rooms) {
|
|
38355
|
+
for (const p of r6.parts) {
|
|
38356
|
+
walls.push(rect({ class: "sx-fp-wall", x: r24(X(p.x) - px(tw / 2)), y: r24(Y(p.y) - px(tw / 2)), width: r24(px(p.w + tw)), height: px(tw) }));
|
|
38357
|
+
walls.push(rect({ class: "sx-fp-wall", x: r24(X(p.x) - px(tw / 2)), y: r24(Y(p.y + p.h) - px(tw / 2)), width: r24(px(p.w + tw)), height: px(tw) }));
|
|
38358
|
+
walls.push(rect({ class: "sx-fp-wall", x: r24(X(p.x) - px(tw / 2)), y: r24(Y(p.y) - px(tw / 2)), width: px(tw), height: r24(px(p.h + tw)) }));
|
|
38359
|
+
walls.push(rect({ class: "sx-fp-wall", x: r24(X(p.x + p.w) - px(tw / 2)), y: r24(Y(p.y) - px(tw / 2)), width: px(tw), height: r24(px(p.h + tw)) }));
|
|
38360
|
+
}
|
|
38361
|
+
}
|
|
38362
|
+
for (const s of lay.seams) {
|
|
38363
|
+
const fill = lay.rooms[s.room]?.fill ?? t.floorFill;
|
|
38364
|
+
const tpx = px(tw);
|
|
38365
|
+
if (s.vertical) {
|
|
38366
|
+
openings.push(
|
|
38367
|
+
rect({ class: "sx-fp-gap", fill, x: r24(X(s.along) - tpx / 2 - 0.5), y: Y(s.lo), width: r24(tpx + 1), height: px(s.hi - s.lo) })
|
|
38368
|
+
);
|
|
38369
|
+
} else {
|
|
38370
|
+
openings.push(
|
|
38371
|
+
rect({ class: "sx-fp-gap", fill, x: X(s.lo), y: r24(Y(s.along) - tpx / 2 - 0.5), width: px(s.hi - s.lo), height: r24(tpx + 1) })
|
|
38372
|
+
);
|
|
38373
|
+
}
|
|
38374
|
+
}
|
|
38375
|
+
for (const o of lay.openings) {
|
|
38376
|
+
openings.push(punchGap(o, lay, ctx));
|
|
38377
|
+
if (o.kind === "window") openings.push(windowSymbol(o, ctx));
|
|
38378
|
+
else if (o.kind === "door") openings.push(doorSymbol(o, ctx));
|
|
38379
|
+
else openings.push(jambLines(o, ctx));
|
|
38380
|
+
}
|
|
38381
|
+
for (const d of lay.dims) dims.push(renderDim(d, ctx));
|
|
38382
|
+
if (lay.north !== void 0) {
|
|
38383
|
+
const ncx = r24(X(lay.bounds.maxX) + px(0.55));
|
|
38384
|
+
const ncy = r24(Y(lay.bounds.minY) - px(0.62));
|
|
38385
|
+
const rr = px(0.32);
|
|
38386
|
+
const rot = Math.round(lay.north * 10) / 10;
|
|
38387
|
+
dims.push(
|
|
38388
|
+
group({ class: "sx-fp-compass-g", transform: rot ? `rotate(${rot} ${ncx} ${ncy})` : "" }, [
|
|
38389
|
+
el("circle", { class: "sx-fp-compass", cx: ncx, cy: ncy, r: rr }),
|
|
38390
|
+
line({ class: "sx-fp-compass", x1: ncx, y1: r24(ncy + rr * 0.7), x2: ncx, y2: r24(ncy - rr * 0.55) }),
|
|
38391
|
+
el("polygon", {
|
|
38392
|
+
class: "sx-fp-compass",
|
|
38393
|
+
fill: t.dimText,
|
|
38394
|
+
points: `${ncx},${r24(ncy - rr * 0.85)} ${r24(ncx - rr * 0.22)},${r24(ncy - rr * 0.3)} ${r24(ncx + rr * 0.22)},${r24(ncy - rr * 0.3)}`
|
|
38395
|
+
}),
|
|
38396
|
+
text({ class: "sx-fp-compass-n", x: r24(ncx + rr + 4), y: r24(ncy - rr * 0.45), "text-anchor": "start" }, "N")
|
|
38397
|
+
])
|
|
38398
|
+
);
|
|
38399
|
+
}
|
|
38400
|
+
const warnBlock = [];
|
|
38401
|
+
if (lay.warnings.length) {
|
|
38402
|
+
const y0 = H2 - warnH + 4;
|
|
38403
|
+
lay.warnings.forEach((w, i) => {
|
|
38404
|
+
warnBlock.push(text({ class: "sx-fp-warn", x: 10, y: r24(y0 + i * 17 + 10) }, `\u26A0 ${w}`));
|
|
38405
|
+
});
|
|
38406
|
+
}
|
|
38407
|
+
const nRooms = lay.rooms.length;
|
|
38408
|
+
const descText = `${nRooms} room${nRooms === 1 ? "" : "s"}, ${formatArea(lay.totalAreaM2, lay.unit)} total. ${lay.items.length} furniture item${lay.items.length === 1 ? "" : "s"}.` + (lay.warnings.length ? ` Warnings: ${lay.warnings.join("; ")}.` : "");
|
|
38409
|
+
return svgRoot(
|
|
38410
|
+
{ viewBox: `0 0 ${W2} ${H2}`, class: "sx-fp", role: "img" },
|
|
38411
|
+
[
|
|
38412
|
+
title(lay.title),
|
|
38413
|
+
desc(descText),
|
|
38414
|
+
el("style", {}, buildCss13(t)),
|
|
38415
|
+
rect({ fill: t.bg, x: 0, y: 0, width: W2, height: H2 }),
|
|
38416
|
+
text({ class: "sx-fp-title", x: titleX, y: TITLE.y, "text-anchor": "middle" }, lay.title),
|
|
38417
|
+
group({ transform: `translate(0,${titleH})` }, [
|
|
38418
|
+
group({ class: "sx-fp-floors" }, floors),
|
|
38419
|
+
group({ class: "sx-fp-furniture" }, furniture),
|
|
38420
|
+
group({ class: "sx-fp-walls" }, walls),
|
|
38421
|
+
group({ class: "sx-fp-openings" }, openings),
|
|
38422
|
+
group({ class: "sx-fp-labels" }, labels),
|
|
38423
|
+
group({ class: "sx-fp-dims" }, dims)
|
|
38424
|
+
]),
|
|
38425
|
+
...warnBlock
|
|
38426
|
+
]
|
|
38427
|
+
);
|
|
38428
|
+
}
|
|
38429
|
+
function renderFloorplan(text2, config) {
|
|
38430
|
+
return renderFloorplanLayout(layoutFloorplan(parseFloorplan(text2)), config);
|
|
38431
|
+
}
|
|
38432
|
+
|
|
38433
|
+
// src/diagrams/floorplan/index.ts
|
|
38434
|
+
var floorplan = {
|
|
38435
|
+
type: "floorplan",
|
|
38436
|
+
detect(text2) {
|
|
38437
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
38438
|
+
const t = raw.trim();
|
|
38439
|
+
if (!t) continue;
|
|
38440
|
+
if (t.startsWith("#") || t.startsWith("//")) continue;
|
|
38441
|
+
return /^floorplan\b/i.test(t);
|
|
38442
|
+
}
|
|
38443
|
+
return false;
|
|
38444
|
+
},
|
|
38445
|
+
parse: parseFloorplan,
|
|
38446
|
+
render(text2, config) {
|
|
38447
|
+
return renderFloorplan(text2, config);
|
|
38448
|
+
},
|
|
38449
|
+
lint(text2) {
|
|
38450
|
+
try {
|
|
38451
|
+
const lay = layoutFloorplan(parseFloorplan(text2));
|
|
38452
|
+
return [
|
|
38453
|
+
...lay.errors.map(
|
|
38454
|
+
(message) => ({
|
|
38455
|
+
severity: "error",
|
|
38456
|
+
code: "floorplan/validation",
|
|
38457
|
+
message,
|
|
38458
|
+
fatal: false
|
|
38459
|
+
})
|
|
38460
|
+
),
|
|
38461
|
+
...lay.warnings.map(
|
|
38462
|
+
(message) => ({
|
|
38463
|
+
severity: "warning",
|
|
38464
|
+
code: "floorplan/warning",
|
|
38465
|
+
message,
|
|
38466
|
+
fatal: false
|
|
38467
|
+
})
|
|
38468
|
+
)
|
|
38469
|
+
];
|
|
38470
|
+
} catch {
|
|
38471
|
+
return [];
|
|
38472
|
+
}
|
|
38473
|
+
}
|
|
38474
|
+
};
|
|
38475
|
+
|
|
35925
38476
|
// src/core/api.ts
|
|
35926
38477
|
var plugins = [
|
|
35927
38478
|
genogram,
|
|
@@ -35968,7 +38519,8 @@ var plugins = [
|
|
|
35968
38519
|
epc,
|
|
35969
38520
|
idef0,
|
|
35970
38521
|
threatmodel,
|
|
35971
|
-
welding
|
|
38522
|
+
welding,
|
|
38523
|
+
floorplan
|
|
35972
38524
|
];
|
|
35973
38525
|
function detectPlugin(text2, config) {
|
|
35974
38526
|
if (config?.type) {
|
|
@@ -35979,7 +38531,7 @@ function detectPlugin(text2, config) {
|
|
|
35979
38531
|
if (plugin.detect(text2)) return plugin;
|
|
35980
38532
|
}
|
|
35981
38533
|
throw new Error(
|
|
35982
|
-
"Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', 'network', 'umlclass', 'faulttree', or '
|
|
38534
|
+
"Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', 'network', 'umlclass', 'faulttree', 'bowtie', or 'floorplan'."
|
|
35983
38535
|
);
|
|
35984
38536
|
}
|
|
35985
38537
|
function stripCodeFences(text2) {
|
|
@@ -36116,6 +38668,6 @@ function renderWithPlugin(prepared, plugin, config) {
|
|
|
36116
38668
|
return plugin.render(prepared, renderConfig);
|
|
36117
38669
|
}
|
|
36118
38670
|
|
|
36119
|
-
export { GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
|
|
36120
|
-
//# sourceMappingURL=chunk-
|
|
36121
|
-
//# sourceMappingURL=chunk-
|
|
38671
|
+
export { FLOORPLAN_SYMBOLS, GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
|
|
38672
|
+
//# sourceMappingURL=chunk-BP6MLXJU.js.map
|
|
38673
|
+
//# sourceMappingURL=chunk-BP6MLXJU.js.map
|