schematex 0.9.7 → 0.9.9
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 +43 -3
- package/dist/ai/ai-sdk.cjs +23 -23
- package/dist/ai/ai-sdk.d.cts +3 -3
- package/dist/ai/ai-sdk.d.ts +3 -3
- package/dist/ai/ai-sdk.js +18 -18
- package/dist/ai/index.cjs +32 -32
- package/dist/ai/index.d.cts +4 -4
- package/dist/ai/index.d.ts +4 -4
- package/dist/ai/index.js +19 -19
- package/dist/{api-BOJJlNb1.d.ts → api-D_d-JklT.d.ts} +1 -1
- package/dist/{api-v9t1T1v6.d.cts → api-DnTyW-6F.d.cts} +1 -1
- package/dist/browser.cjs +24 -24
- package/dist/browser.d.cts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +18 -18
- package/dist/{chunk-6QZQTASC.cjs → chunk-246OJILS.cjs} +12 -12
- package/dist/{chunk-6QZQTASC.cjs.map → chunk-246OJILS.cjs.map} +1 -1
- package/dist/{chunk-JEMAOC2D.js → chunk-2IRYXK4M.js} +3 -3
- package/dist/{chunk-JEMAOC2D.js.map → chunk-2IRYXK4M.js.map} +1 -1
- package/dist/{chunk-ITI3STJ6.cjs → chunk-3JN4762U.cjs} +4 -4
- package/dist/{chunk-ITI3STJ6.cjs.map → chunk-3JN4762U.cjs.map} +1 -1
- package/dist/{chunk-GAQ36VFD.cjs → chunk-4MANMKQ4.cjs} +4 -4
- package/dist/{chunk-GAQ36VFD.cjs.map → chunk-4MANMKQ4.cjs.map} +1 -1
- package/dist/{chunk-64LABNTF.js → chunk-7NO5LYLL.js} +3 -3
- package/dist/{chunk-64LABNTF.js.map → chunk-7NO5LYLL.js.map} +1 -1
- package/dist/{chunk-ENUM7GMZ.cjs → chunk-7OOUU6BL.cjs} +147 -2
- package/dist/chunk-7OOUU6BL.cjs.map +1 -0
- package/dist/{chunk-M26ORU4P.js → chunk-AETIGJXP.js} +3 -3
- package/dist/{chunk-M26ORU4P.js.map → chunk-AETIGJXP.js.map} +1 -1
- package/dist/{chunk-OJ3P4IC4.cjs → chunk-DLXDR2E7.cjs} +4 -4
- package/dist/{chunk-OJ3P4IC4.cjs.map → chunk-DLXDR2E7.cjs.map} +1 -1
- package/dist/{chunk-3J4DZPZC.js → chunk-EB7T5SEO.js} +3 -3
- package/dist/{chunk-3J4DZPZC.js.map → chunk-EB7T5SEO.js.map} +1 -1
- package/dist/{chunk-ECHPMEZX.cjs → chunk-EUN6FEGE.cjs} +181 -7
- package/dist/chunk-EUN6FEGE.cjs.map +1 -0
- package/dist/{chunk-VY6UZYYL.cjs → chunk-F5RYWDWO.cjs} +15 -15
- package/dist/{chunk-VY6UZYYL.cjs.map → chunk-F5RYWDWO.cjs.map} +1 -1
- package/dist/{chunk-INVLJYAE.cjs → chunk-GTCGATOL.cjs} +4 -4
- package/dist/{chunk-INVLJYAE.cjs.map → chunk-GTCGATOL.cjs.map} +1 -1
- package/dist/{chunk-ZCHGIWJK.js → chunk-HIWJMJJB.js} +3 -3
- package/dist/{chunk-ZCHGIWJK.js.map → chunk-HIWJMJJB.js.map} +1 -1
- package/dist/{chunk-RDYACU2G.js → chunk-IHX6J4HF.js} +3 -3
- package/dist/{chunk-RDYACU2G.js.map → chunk-IHX6J4HF.js.map} +1 -1
- package/dist/{chunk-4MRVJI7G.js → chunk-JVNV2T2T.js} +147 -3
- package/dist/chunk-JVNV2T2T.js.map +1 -0
- package/dist/{chunk-SXOAAQNY.js → chunk-KUSW5I2P.js} +3 -3
- package/dist/{chunk-SXOAAQNY.js.map → chunk-KUSW5I2P.js.map} +1 -1
- package/dist/{chunk-35NGXDT2.cjs → chunk-KYE7CRED.cjs} +12 -12
- package/dist/{chunk-35NGXDT2.cjs.map → chunk-KYE7CRED.cjs.map} +1 -1
- package/dist/{chunk-II4GLKGF.js → chunk-MVTYPWHO.js} +3 -3
- package/dist/chunk-MVTYPWHO.js.map +1 -0
- package/dist/{chunk-N3HU635X.cjs → chunk-MXV3XJFH.cjs} +4 -4
- package/dist/chunk-MXV3XJFH.cjs.map +1 -0
- package/dist/{chunk-XCCXG6RR.js → chunk-NB7MFQCZ.js} +3 -3
- package/dist/{chunk-XCCXG6RR.js.map → chunk-NB7MFQCZ.js.map} +1 -1
- package/dist/{chunk-GYYYULBL.js → chunk-NP7N3DZG.js} +3 -3
- package/dist/{chunk-GYYYULBL.js.map → chunk-NP7N3DZG.js.map} +1 -1
- package/dist/{chunk-6X7MVZZT.cjs → chunk-OY2CXLVY.cjs} +2156 -768
- package/dist/chunk-OY2CXLVY.cjs.map +1 -0
- package/dist/{chunk-3PH2MQGN.js → chunk-P4U565XH.js} +3 -3
- package/dist/{chunk-3PH2MQGN.js.map → chunk-P4U565XH.js.map} +1 -1
- package/dist/{chunk-VYQXB2RC.cjs → chunk-QC2RICQ4.cjs} +12 -12
- package/dist/{chunk-VYQXB2RC.cjs.map → chunk-QC2RICQ4.cjs.map} +1 -1
- package/dist/{chunk-3WX24RCH.js → chunk-QMJHLDGY.js} +3 -3
- package/dist/{chunk-3WX24RCH.js.map → chunk-QMJHLDGY.js.map} +1 -1
- package/dist/{chunk-DVRB64CN.js → chunk-RCR4UGKV.js} +179 -5
- package/dist/chunk-RCR4UGKV.js.map +1 -0
- package/dist/{chunk-C4Y24X3U.cjs → chunk-T55OQILI.cjs} +4 -4
- package/dist/{chunk-C4Y24X3U.cjs.map → chunk-T55OQILI.cjs.map} +1 -1
- package/dist/{chunk-3K4WCRVI.js → chunk-T5QOVX2I.js} +1953 -566
- package/dist/chunk-T5QOVX2I.js.map +1 -0
- package/dist/{chunk-6ZD7TCWO.cjs → chunk-TFUVNQNA.cjs} +15 -15
- package/dist/{chunk-6ZD7TCWO.cjs.map → chunk-TFUVNQNA.cjs.map} +1 -1
- package/dist/{chunk-4AC6I7KJ.cjs → chunk-TXWVJAMR.cjs} +4 -4
- package/dist/{chunk-4AC6I7KJ.cjs.map → chunk-TXWVJAMR.cjs.map} +1 -1
- package/dist/{chunk-4OC3CTGE.cjs → chunk-UHRNFBWY.cjs} +4 -4
- package/dist/{chunk-4OC3CTGE.cjs.map → chunk-UHRNFBWY.cjs.map} +1 -1
- package/dist/{chunk-B4CMWA6Y.js → chunk-W4UVDMUC.js} +3 -3
- package/dist/{chunk-B4CMWA6Y.js.map → chunk-W4UVDMUC.js.map} +1 -1
- package/dist/{chunk-5ZQRHDMQ.cjs → chunk-YDEQZSPQ.cjs} +4 -4
- package/dist/{chunk-5ZQRHDMQ.cjs.map → chunk-YDEQZSPQ.cjs.map} +1 -1
- package/dist/{chunk-627GHE2N.cjs → chunk-YFPDZVB7.cjs} +5 -5
- package/dist/{chunk-627GHE2N.cjs.map → chunk-YFPDZVB7.cjs.map} +1 -1
- package/dist/{chunk-NKYR4PAS.js → chunk-YXEPF3SL.js} +3 -3
- package/dist/{chunk-NKYR4PAS.js.map → chunk-YXEPF3SL.js.map} +1 -1
- package/dist/{chunk-AXMBXAEA.js → chunk-ZRFKIERV.js} +3 -3
- package/dist/{chunk-AXMBXAEA.js.map → chunk-ZRFKIERV.js.map} +1 -1
- package/dist/{diagnostics-5bVLlGNj.d.cts → diagnostics-D2qkBfFx.d.cts} +1 -1
- package/dist/{diagnostics-5bVLlGNj.d.ts → diagnostics-D2qkBfFx.d.ts} +1 -1
- package/dist/diagrams/blockdiagram/index.cjs +6 -6
- package/dist/diagrams/blockdiagram/index.d.cts +1 -1
- package/dist/diagrams/blockdiagram/index.d.ts +1 -1
- package/dist/diagrams/blockdiagram/index.js +2 -2
- 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.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +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-syc0E5Ss.d.ts → index-D9u0YRxL.d.ts} +1 -1
- package/dist/{index-Cmf4Rcve.d.cts → index-JZlLiE6K.d.cts} +1 -1
- package/dist/index.cjs +91 -87
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +22 -22
- package/dist/react.cjs +18 -18
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +17 -17
- package/dist/{tools-B98iarLm.d.cts → tools-DAKYNcPv.d.cts} +2 -2
- package/dist/{tools-CCZ1IcIN.d.ts → tools-DM0aLCbc.d.ts} +2 -2
- package/package.json +1 -1
- package/dist/chunk-3K4WCRVI.js.map +0 -1
- package/dist/chunk-4MRVJI7G.js.map +0 -1
- package/dist/chunk-6X7MVZZT.cjs.map +0 -1
- package/dist/chunk-DVRB64CN.js.map +0 -1
- package/dist/chunk-ECHPMEZX.cjs.map +0 -1
- package/dist/chunk-ENUM7GMZ.cjs.map +0 -1
- package/dist/chunk-II4GLKGF.js.map +0 -1
- package/dist/chunk-N3HU635X.cjs.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, estimateTextWidth } from './chunk-
|
|
12
|
-
import { pedigree } from './chunk-
|
|
1
|
+
import { orgchart } from './chunk-EB7T5SEO.js';
|
|
2
|
+
import { circuit } from './chunk-IHX6J4HF.js';
|
|
3
|
+
import { blockdiagram } from './chunk-P4U565XH.js';
|
|
4
|
+
import { ladder } from './chunk-W4UVDMUC.js';
|
|
5
|
+
import { sld } from './chunk-MVTYPWHO.js';
|
|
6
|
+
import { entity } from './chunk-2IRYXK4M.js';
|
|
7
|
+
import { fishbone } from './chunk-NP7N3DZG.js';
|
|
8
|
+
import { venn } from './chunk-KUSW5I2P.js';
|
|
9
|
+
import { flowchart, layoutFlowchart } from './chunk-ZRFKIERV.js';
|
|
10
|
+
import { genogram } from './chunk-AETIGJXP.js';
|
|
11
|
+
import { ecomap, estimateTextWidth } from './chunk-NB7MFQCZ.js';
|
|
12
|
+
import { pedigree } from './chunk-HIWJMJJB.js';
|
|
13
13
|
import { parseFrontmatter } from './chunk-2KTQ75LN.js';
|
|
14
|
-
import { phylo } from './chunk-
|
|
15
|
-
import { sociogram } from './chunk-
|
|
14
|
+
import { phylo } from './chunk-QMJHLDGY.js';
|
|
15
|
+
import { sociogram } from './chunk-7NO5LYLL.js';
|
|
16
16
|
import { timing } from './chunk-MTIZIHWE.js';
|
|
17
|
-
import { logic } from './chunk-
|
|
18
|
-
import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme,
|
|
17
|
+
import { logic } from './chunk-YXEPF3SL.js';
|
|
18
|
+
import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveComparisonTheme, TITLE, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme, resolvePlaybookTheme } from './chunk-JVNV2T2T.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
|
|
|
@@ -1613,8 +1613,8 @@ function renderDecisionTree(ast, config) {
|
|
|
1613
1613
|
lx += nx * perpOffset * flip;
|
|
1614
1614
|
ly += ny * perpOffset * flip;
|
|
1615
1615
|
}
|
|
1616
|
-
const
|
|
1617
|
-
const w = Math.max(e.label.length *
|
|
1616
|
+
const charW2 = isProb ? 5.5 : 6.2;
|
|
1617
|
+
const w = Math.max(e.label.length * charW2 + 10, 18);
|
|
1618
1618
|
const h = 14;
|
|
1619
1619
|
inner.push(rect({
|
|
1620
1620
|
x: lx - w / 2,
|
|
@@ -7603,8 +7603,8 @@ function renderBox(box2) {
|
|
|
7603
7603
|
);
|
|
7604
7604
|
}
|
|
7605
7605
|
function approxLineWidth(line2) {
|
|
7606
|
-
const
|
|
7607
|
-
return line2.text.length *
|
|
7606
|
+
const charW2 = line2.style === "label" || line2.style === "count" ? 7.4 : 6.4;
|
|
7607
|
+
return line2.text.length * charW2;
|
|
7608
7608
|
}
|
|
7609
7609
|
function renderPrismaLayout(layout, config) {
|
|
7610
7610
|
const t = resolveBaseTheme(config?.theme ?? "default");
|
|
@@ -8271,35 +8271,35 @@ function ellipsePerimeterPoint(cx, cy, rx, ry, tx, ty) {
|
|
|
8271
8271
|
function isWideGlyph(cp) {
|
|
8272
8272
|
return cp >= 12288 && cp <= 40959 || cp >= 44032 && cp <= 55203 || cp >= 65280 && cp <= 65519;
|
|
8273
8273
|
}
|
|
8274
|
-
function estimateTextWidth2(s,
|
|
8274
|
+
function estimateTextWidth2(s, charW2) {
|
|
8275
8275
|
let w = 0;
|
|
8276
8276
|
for (const ch of s) {
|
|
8277
|
-
w += isWideGlyph(ch.codePointAt(0) ?? 0) ?
|
|
8277
|
+
w += isWideGlyph(ch.codePointAt(0) ?? 0) ? charW2 * 1.7 : charW2;
|
|
8278
8278
|
}
|
|
8279
8279
|
return w;
|
|
8280
8280
|
}
|
|
8281
8281
|
function sizeEllipse(uc) {
|
|
8282
|
-
const
|
|
8283
|
-
const nameW = estimateTextWidth2(uc.name,
|
|
8282
|
+
const C3 = USECASE_CONST;
|
|
8283
|
+
const nameW = estimateTextWidth2(uc.name, C3.CHAR_W_NAME);
|
|
8284
8284
|
let widest = nameW;
|
|
8285
8285
|
if (uc.stereotype) {
|
|
8286
|
-
widest = Math.max(widest, estimateTextWidth2(`\xAB${uc.stereotype}\xBB`,
|
|
8286
|
+
widest = Math.max(widest, estimateTextWidth2(`\xAB${uc.stereotype}\xBB`, C3.CHAR_W_EXT));
|
|
8287
8287
|
}
|
|
8288
8288
|
let stack = 0;
|
|
8289
|
-
if (uc.stereotype) stack +=
|
|
8290
|
-
stack +=
|
|
8289
|
+
if (uc.stereotype) stack += C3.STEREO_LH;
|
|
8290
|
+
stack += C3.NAME_LH;
|
|
8291
8291
|
const hasExt = uc.extensionPoints.length > 0;
|
|
8292
8292
|
if (hasExt) {
|
|
8293
8293
|
stack += 8;
|
|
8294
|
-
stack +=
|
|
8295
|
-
stack += uc.extensionPoints.length *
|
|
8296
|
-
widest = Math.max(widest, estimateTextWidth2(
|
|
8294
|
+
stack += C3.EXTPOINT_LH;
|
|
8295
|
+
stack += uc.extensionPoints.length * C3.EXTPOINT_LH;
|
|
8296
|
+
widest = Math.max(widest, estimateTextWidth2(C3.EXT_HEADER, C3.CHAR_W_EXT));
|
|
8297
8297
|
for (const ep of uc.extensionPoints) {
|
|
8298
|
-
widest = Math.max(widest, estimateTextWidth2(ep,
|
|
8298
|
+
widest = Math.max(widest, estimateTextWidth2(ep, C3.CHAR_W_EXT) + 16);
|
|
8299
8299
|
}
|
|
8300
8300
|
}
|
|
8301
|
-
const rx = Math.max(
|
|
8302
|
-
const ry = Math.max(
|
|
8301
|
+
const rx = Math.max(C3.MIN_RX, widest / 2 * Math.SQRT2 + C3.ELLIPSE_PAD_X);
|
|
8302
|
+
const ry = Math.max(C3.MIN_RY, stack / 2 * Math.SQRT2 + C3.ELLIPSE_PAD_Y);
|
|
8303
8303
|
const result = {
|
|
8304
8304
|
rx: Math.round(rx),
|
|
8305
8305
|
ry: Math.round(ry)
|
|
@@ -8384,7 +8384,7 @@ function classifyActorSides(ast) {
|
|
|
8384
8384
|
return sides;
|
|
8385
8385
|
}
|
|
8386
8386
|
function layoutUsecase(ast) {
|
|
8387
|
-
const
|
|
8387
|
+
const C3 = USECASE_CONST;
|
|
8388
8388
|
const ucIds = new Set(ast.usecases.map((u) => u.id));
|
|
8389
8389
|
const actorById = new Map(ast.actors.map((a) => [a.id, a]));
|
|
8390
8390
|
const sizes = /* @__PURE__ */ new Map();
|
|
@@ -8447,18 +8447,18 @@ function layoutUsecase(ast) {
|
|
|
8447
8447
|
}
|
|
8448
8448
|
}
|
|
8449
8449
|
const colMaxRx = columns.map(
|
|
8450
|
-
(col) => col.reduce((m, u) => Math.max(m, sizes.get(u.id).rx),
|
|
8450
|
+
(col) => col.reduce((m, u) => Math.max(m, sizes.get(u.id).rx), C3.MIN_RX)
|
|
8451
8451
|
);
|
|
8452
8452
|
const colCenterX = [];
|
|
8453
8453
|
let cursorX = 0;
|
|
8454
8454
|
for (let d = 0; d <= maxDepth; d++) {
|
|
8455
8455
|
cursorX += colMaxRx[d];
|
|
8456
8456
|
colCenterX[d] = cursorX;
|
|
8457
|
-
cursorX += colMaxRx[d] +
|
|
8457
|
+
cursorX += colMaxRx[d] + C3.COL_GAP;
|
|
8458
8458
|
}
|
|
8459
|
-
let maxRy =
|
|
8459
|
+
let maxRy = C3.MIN_RY;
|
|
8460
8460
|
for (const s of sizes.values()) maxRy = Math.max(maxRy, s.ry);
|
|
8461
|
-
const rowPitch = Math.max(
|
|
8461
|
+
const rowPitch = Math.max(C3.ROW_PITCH, 2 * maxRy + C3.ROW_GAP_MIN);
|
|
8462
8462
|
const ellipses = [];
|
|
8463
8463
|
const ellById = /* @__PURE__ */ new Map();
|
|
8464
8464
|
for (let d = 0; d <= maxDepth; d++) {
|
|
@@ -8481,7 +8481,7 @@ function layoutUsecase(ast) {
|
|
|
8481
8481
|
for (let i = 1; i < colE.length; i++) {
|
|
8482
8482
|
const prev = colE[i - 1];
|
|
8483
8483
|
const cur = colE[i];
|
|
8484
|
-
const minGap = prev.ry + cur.ry +
|
|
8484
|
+
const minGap = prev.ry + cur.ry + C3.ROW_GAP_MIN;
|
|
8485
8485
|
if (cur.cy - prev.cy < minGap) {
|
|
8486
8486
|
cur.cy = prev.cy + minGap;
|
|
8487
8487
|
if (cur.dividerY !== void 0) cur.dividerY = cur.cy + 6;
|
|
@@ -8502,10 +8502,10 @@ function layoutUsecase(ast) {
|
|
|
8502
8502
|
ucMaxY = 100;
|
|
8503
8503
|
}
|
|
8504
8504
|
const hasSubject = !!ast.system;
|
|
8505
|
-
const subjLeft = ucMinX -
|
|
8506
|
-
const subjRight = ucMaxX +
|
|
8507
|
-
const subjTop = ucMinY -
|
|
8508
|
-
const subjBottom = ucMaxY +
|
|
8505
|
+
const subjLeft = ucMinX - C3.SUBJECT_PAD;
|
|
8506
|
+
const subjRight = ucMaxX + C3.SUBJECT_PAD;
|
|
8507
|
+
const subjTop = ucMinY - C3.SUBJECT_TOP_PAD;
|
|
8508
|
+
const subjBottom = ucMaxY + C3.SUBJECT_BOTTOM_PAD;
|
|
8509
8509
|
const leftActors = ast.actors.filter((a) => sides.get(a.id) === "left");
|
|
8510
8510
|
const rightActors = ast.actors.filter((a) => sides.get(a.id) === "right");
|
|
8511
8511
|
const actorBoxes = [];
|
|
@@ -8513,17 +8513,17 @@ function layoutUsecase(ast) {
|
|
|
8513
8513
|
const subjCenterY = (subjTop + subjBottom) / 2;
|
|
8514
8514
|
function placeStack(actors, side) {
|
|
8515
8515
|
if (actors.length === 0) return;
|
|
8516
|
-
const totalH = (actors.length - 1) *
|
|
8516
|
+
const totalH = (actors.length - 1) * C3.ACTOR_PITCH;
|
|
8517
8517
|
let cy = subjCenterY - totalH / 2;
|
|
8518
8518
|
for (const a of actors) {
|
|
8519
8519
|
const isRect = a.kind === "external" || a.kind === "system";
|
|
8520
|
-
const w = isRect ?
|
|
8521
|
-
const h = isRect ?
|
|
8520
|
+
const w = isRect ? C3.EXTERNAL_W : C3.ACTOR_W;
|
|
8521
|
+
const h = isRect ? C3.EXTERNAL_H : C3.ACTOR_H;
|
|
8522
8522
|
let x;
|
|
8523
8523
|
if (side === "left") {
|
|
8524
|
-
x = subjLeft -
|
|
8524
|
+
x = subjLeft - C3.ACTOR_GAP - w;
|
|
8525
8525
|
} else {
|
|
8526
|
-
x = subjRight +
|
|
8526
|
+
x = subjRight + C3.ACTOR_GAP;
|
|
8527
8527
|
}
|
|
8528
8528
|
const y = cy - h / 2;
|
|
8529
8529
|
const anchorX = side === "left" ? x + w : x;
|
|
@@ -8540,7 +8540,7 @@ function layoutUsecase(ast) {
|
|
|
8540
8540
|
};
|
|
8541
8541
|
actorBoxes.push(box2);
|
|
8542
8542
|
actorById2.set(a.id, box2);
|
|
8543
|
-
cy +=
|
|
8543
|
+
cy += C3.ACTOR_PITCH;
|
|
8544
8544
|
}
|
|
8545
8545
|
}
|
|
8546
8546
|
placeStack(leftActors, "left");
|
|
@@ -8559,11 +8559,11 @@ function layoutUsecase(ast) {
|
|
|
8559
8559
|
minX = Math.min(minX, left);
|
|
8560
8560
|
maxX = Math.max(maxX, right);
|
|
8561
8561
|
minY = Math.min(minY, b.y);
|
|
8562
|
-
maxY = Math.max(maxY, b.y + b.height +
|
|
8562
|
+
maxY = Math.max(maxY, b.y + b.height + C3.ACTOR_LABEL_H);
|
|
8563
8563
|
}
|
|
8564
|
-
const titleSpace = ast.title ?
|
|
8565
|
-
const offsetX =
|
|
8566
|
-
const offsetY =
|
|
8564
|
+
const titleSpace = ast.title ? C3.TITLE_H : 0;
|
|
8565
|
+
const offsetX = C3.MARGIN - minX;
|
|
8566
|
+
const offsetY = C3.MARGIN + titleSpace - minY;
|
|
8567
8567
|
const translate = (px, py) => ({ x: px + offsetX, y: py + offsetY });
|
|
8568
8568
|
for (const e of ellipses) {
|
|
8569
8569
|
const p = translate(e.cx, e.cy);
|
|
@@ -8584,8 +8584,8 @@ function layoutUsecase(ast) {
|
|
|
8584
8584
|
height: subjBottom - subjTop
|
|
8585
8585
|
} : void 0;
|
|
8586
8586
|
if (subject && ast.system) subject.name = ast.system;
|
|
8587
|
-
const width = maxX - minX + 2 *
|
|
8588
|
-
const height = maxY - minY + 2 *
|
|
8587
|
+
const width = maxX - minX + 2 * C3.MARGIN;
|
|
8588
|
+
const height = maxY - minY + 2 * C3.MARGIN + titleSpace;
|
|
8589
8589
|
const edges = [];
|
|
8590
8590
|
const trees = [];
|
|
8591
8591
|
const genByParent = /* @__PURE__ */ new Map();
|
|
@@ -8625,7 +8625,7 @@ function layoutUsecase(ast) {
|
|
|
8625
8625
|
return { x, y };
|
|
8626
8626
|
}
|
|
8627
8627
|
for (const [parentId, rels] of genByParent) {
|
|
8628
|
-
const useTree = ast.generalizationTree && rels.length >=
|
|
8628
|
+
const useTree = ast.generalizationTree && rels.length >= C3.GEN_TREE_THRESHOLD;
|
|
8629
8629
|
const allActors = actorById2.has(parentId) && rels.every((r7) => actorById2.has(r7.source));
|
|
8630
8630
|
if (allActors) {
|
|
8631
8631
|
const side = sides.get(parentId) ?? "left";
|
|
@@ -8677,8 +8677,8 @@ function layoutUsecase(ast) {
|
|
|
8677
8677
|
const dirX = avgX - parent.cx;
|
|
8678
8678
|
const dirY = avgY - parent.cy;
|
|
8679
8679
|
const len = Math.hypot(dirX, dirY) || 1;
|
|
8680
|
-
const jx = pPt.x + dirX / len *
|
|
8681
|
-
const jy = pPt.y + dirY / len *
|
|
8680
|
+
const jx = pPt.x + dirX / len * C3.GEN_JUNCTION_OFFSET;
|
|
8681
|
+
const jy = pPt.y + dirY / len * C3.GEN_JUNCTION_OFFSET;
|
|
8682
8682
|
const legPaths = [];
|
|
8683
8683
|
for (const r7 of rels) {
|
|
8684
8684
|
const cPt = perimeter(r7.source, jx, jy);
|
|
@@ -10113,7 +10113,7 @@ function routeEdge2(s, t, direction) {
|
|
|
10113
10113
|
return { d, labelX: mid, labelY: (sy + ty) / 2 };
|
|
10114
10114
|
}
|
|
10115
10115
|
function layoutNetwork(ast, schedule) {
|
|
10116
|
-
const
|
|
10116
|
+
const C3 = PERT_CONST;
|
|
10117
10117
|
const { rank, maxRank } = assignRanks(ast);
|
|
10118
10118
|
const layers = orderLayers(ast, schedule, rank, maxRank);
|
|
10119
10119
|
const dir = ast.direction;
|
|
@@ -10122,16 +10122,16 @@ function layoutNetwork(ast, schedule) {
|
|
|
10122
10122
|
const colExtent = layers.map((layer) => {
|
|
10123
10123
|
const n = layer.length;
|
|
10124
10124
|
if (n === 0) return 0;
|
|
10125
|
-
const unit = dir === "TB" ?
|
|
10126
|
-
const gap = dir === "TB" ?
|
|
10125
|
+
const unit = dir === "TB" ? C3.BOX_W : C3.BOX_H;
|
|
10126
|
+
const gap = dir === "TB" ? C3.H_GAP : C3.V_GAP;
|
|
10127
10127
|
return n * unit + (n - 1) * gap;
|
|
10128
10128
|
});
|
|
10129
10129
|
const maxExtent = Math.max(0, ...colExtent);
|
|
10130
10130
|
const hasSentinels = ast.showSentinels;
|
|
10131
|
-
const sentLead = hasSentinels ?
|
|
10132
|
-
const titleH = ast.title ?
|
|
10133
|
-
const originPrimary = dir === "TB" ?
|
|
10134
|
-
const originCross = dir === "TB" ?
|
|
10131
|
+
const sentLead = hasSentinels ? C3.SENT_R * 2 + C3.H_GAP : 0;
|
|
10132
|
+
const titleH = ast.title ? C3.TITLE_H : 0;
|
|
10133
|
+
const originPrimary = dir === "TB" ? C3.PAD + titleH + sentLead : C3.PAD + sentLead;
|
|
10134
|
+
const originCross = dir === "TB" ? C3.PAD : C3.PAD + titleH;
|
|
10135
10135
|
const boxes = [];
|
|
10136
10136
|
const boxById = /* @__PURE__ */ new Map();
|
|
10137
10137
|
for (let r7 = 0; r7 <= maxRank; r7++) {
|
|
@@ -10142,20 +10142,20 @@ function layoutNetwork(ast, schedule) {
|
|
|
10142
10142
|
for (const id of layer) {
|
|
10143
10143
|
const t = byId.get(id);
|
|
10144
10144
|
const computed = schedule.computed.get(id);
|
|
10145
|
-
const w = t.milestone ?
|
|
10146
|
-
const h =
|
|
10145
|
+
const w = t.milestone ? C3.MS_W : C3.BOX_W;
|
|
10146
|
+
const h = C3.BOX_H;
|
|
10147
10147
|
let x;
|
|
10148
10148
|
let y;
|
|
10149
10149
|
if (dir === "TB") {
|
|
10150
|
-
const rankPos = originPrimary + r7 * (
|
|
10151
|
-
x = cursor + (
|
|
10150
|
+
const rankPos = originPrimary + r7 * (C3.BOX_H + C3.H_GAP);
|
|
10151
|
+
x = cursor + (C3.BOX_W - w) / 2;
|
|
10152
10152
|
y = rankPos;
|
|
10153
|
-
cursor +=
|
|
10153
|
+
cursor += C3.BOX_W + C3.H_GAP;
|
|
10154
10154
|
} else {
|
|
10155
|
-
const rankPos = originPrimary + r7 * (
|
|
10156
|
-
x = rankPos + (
|
|
10155
|
+
const rankPos = originPrimary + r7 * (C3.BOX_W + C3.H_GAP);
|
|
10156
|
+
x = rankPos + (C3.BOX_W - w) / 2;
|
|
10157
10157
|
y = cursor;
|
|
10158
|
-
cursor +=
|
|
10158
|
+
cursor += C3.BOX_H + C3.V_GAP;
|
|
10159
10159
|
}
|
|
10160
10160
|
const box2 = {
|
|
10161
10161
|
id,
|
|
@@ -10202,11 +10202,11 @@ function layoutNetwork(ast, schedule) {
|
|
|
10202
10202
|
const sentinels = [];
|
|
10203
10203
|
if (hasSentinels) {
|
|
10204
10204
|
const contentMidCross = originCross + maxExtent / 2;
|
|
10205
|
-
const startPrimary =
|
|
10206
|
-
const finishPrimary = maxPrimary +
|
|
10205
|
+
const startPrimary = C3.PAD + C3.SENT_R;
|
|
10206
|
+
const finishPrimary = maxPrimary + C3.H_GAP + C3.SENT_R;
|
|
10207
10207
|
const place = (primary) => dir === "TB" ? { cx: contentMidCross, cy: primary } : { cx: primary, cy: contentMidCross };
|
|
10208
|
-
sentinels.push({ id: "__start__", label: "Start", r:
|
|
10209
|
-
sentinels.push({ id: "__finish__", label: "Finish", r:
|
|
10208
|
+
sentinels.push({ id: "__start__", label: "Start", r: C3.SENT_R, ...place(startPrimary) });
|
|
10209
|
+
sentinels.push({ id: "__finish__", label: "Finish", r: C3.SENT_R, ...place(finishPrimary) });
|
|
10210
10210
|
const usedAsPred = /* @__PURE__ */ new Set();
|
|
10211
10211
|
for (const t of ast.tasks) for (const dep of t.deps) usedAsPred.add(dep.pred);
|
|
10212
10212
|
const startSent = sentinels[0];
|
|
@@ -10220,19 +10220,19 @@ function layoutNetwork(ast, schedule) {
|
|
|
10220
10220
|
edges.push(sentinelEdge(finishSent, b, dir, false, schedule.computed.get(t.id)));
|
|
10221
10221
|
}
|
|
10222
10222
|
}
|
|
10223
|
-
maxPrimary = finishPrimary +
|
|
10223
|
+
maxPrimary = finishPrimary + C3.SENT_R;
|
|
10224
10224
|
}
|
|
10225
10225
|
const crossEnd = Math.max(maxCross, originCross + maxExtent);
|
|
10226
10226
|
let width;
|
|
10227
10227
|
let footerY;
|
|
10228
10228
|
if (dir === "TB") {
|
|
10229
|
-
width = crossEnd +
|
|
10229
|
+
width = crossEnd + C3.PAD;
|
|
10230
10230
|
footerY = maxPrimary + 14;
|
|
10231
10231
|
} else {
|
|
10232
|
-
width = maxPrimary +
|
|
10232
|
+
width = maxPrimary + C3.PAD;
|
|
10233
10233
|
footerY = crossEnd + 14;
|
|
10234
10234
|
}
|
|
10235
|
-
const height = footerY +
|
|
10235
|
+
const height = footerY + C3.FOOTER_H;
|
|
10236
10236
|
return {
|
|
10237
10237
|
width: Math.ceil(width),
|
|
10238
10238
|
height: Math.ceil(height),
|
|
@@ -10310,7 +10310,7 @@ function buildDependencyEdges(ast, schedule, boxById, dir) {
|
|
|
10310
10310
|
return edges;
|
|
10311
10311
|
}
|
|
10312
10312
|
function layoutSwimlane2(ast, schedule) {
|
|
10313
|
-
const
|
|
10313
|
+
const C3 = PERT_CONST;
|
|
10314
10314
|
const { rank, maxRank } = assignRanks(ast);
|
|
10315
10315
|
const byId = /* @__PURE__ */ new Map();
|
|
10316
10316
|
for (const t of ast.tasks) byId.set(t.id, t);
|
|
@@ -10341,9 +10341,9 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10341
10341
|
return declIndex.get(a) - declIndex.get(b);
|
|
10342
10342
|
});
|
|
10343
10343
|
}
|
|
10344
|
-
const titleH = ast.title ?
|
|
10345
|
-
const topY =
|
|
10346
|
-
const colX = (r7) =>
|
|
10344
|
+
const titleH = ast.title ? C3.TITLE_H : 0;
|
|
10345
|
+
const topY = C3.PAD + titleH;
|
|
10346
|
+
const colX = (r7) => C3.LANE_LABEL_W + C3.PAD + r7 * (C3.BOX_W + C3.H_GAP);
|
|
10347
10347
|
const lanes = [];
|
|
10348
10348
|
const laneY = /* @__PURE__ */ new Map();
|
|
10349
10349
|
const laneH = /* @__PURE__ */ new Map();
|
|
@@ -10354,7 +10354,7 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10354
10354
|
const arr = cell.get(key(lane, r7));
|
|
10355
10355
|
if (arr) stack = Math.max(stack, arr.length);
|
|
10356
10356
|
}
|
|
10357
|
-
const bandH = stack *
|
|
10357
|
+
const bandH = stack * C3.BOX_H + (stack - 1) * C3.V_GAP + 2 * C3.LANE_PAD;
|
|
10358
10358
|
laneY.set(lane, cursor);
|
|
10359
10359
|
laneH.set(lane, bandH);
|
|
10360
10360
|
lanes.push({ name: lane, y: cursor, height: bandH, alt: i % 2 === 1 });
|
|
@@ -10369,13 +10369,13 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10369
10369
|
for (let r7 = 0; r7 <= maxRank; r7++) {
|
|
10370
10370
|
const arr = cell.get(key(lane, r7));
|
|
10371
10371
|
if (!arr) continue;
|
|
10372
|
-
const colInnerH = arr.length *
|
|
10372
|
+
const colInnerH = arr.length * C3.BOX_H + (arr.length - 1) * C3.V_GAP;
|
|
10373
10373
|
const y0 = bandTop + (bandH - colInnerH) / 2;
|
|
10374
10374
|
arr.forEach((id, idx) => {
|
|
10375
10375
|
const t = byId.get(id);
|
|
10376
|
-
const w = t.milestone ?
|
|
10377
|
-
const x = colX(r7) + (
|
|
10378
|
-
const y = y0 + idx * (
|
|
10376
|
+
const w = t.milestone ? C3.MS_W : C3.BOX_W;
|
|
10377
|
+
const x = colX(r7) + (C3.BOX_W - w) / 2;
|
|
10378
|
+
const y = y0 + idx * (C3.BOX_H + C3.V_GAP);
|
|
10379
10379
|
const box2 = {
|
|
10380
10380
|
id,
|
|
10381
10381
|
task: t,
|
|
@@ -10383,7 +10383,7 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10383
10383
|
x,
|
|
10384
10384
|
y,
|
|
10385
10385
|
width: w,
|
|
10386
|
-
height:
|
|
10386
|
+
height: C3.BOX_H,
|
|
10387
10387
|
milestone: t.milestone,
|
|
10388
10388
|
rank: r7
|
|
10389
10389
|
};
|
|
@@ -10393,9 +10393,9 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10393
10393
|
}
|
|
10394
10394
|
}
|
|
10395
10395
|
const edges = buildDependencyEdges(ast, schedule, boxById, "LR");
|
|
10396
|
-
const width = colX(maxRank) +
|
|
10396
|
+
const width = colX(maxRank) + C3.BOX_W + C3.PAD;
|
|
10397
10397
|
const footerY = contentBottom + 14;
|
|
10398
|
-
const height = footerY +
|
|
10398
|
+
const height = footerY + C3.FOOTER_H;
|
|
10399
10399
|
return {
|
|
10400
10400
|
width: Math.ceil(width),
|
|
10401
10401
|
height: Math.ceil(height),
|
|
@@ -10413,20 +10413,20 @@ function layoutSwimlane2(ast, schedule) {
|
|
|
10413
10413
|
};
|
|
10414
10414
|
}
|
|
10415
10415
|
function layoutTimescaled(ast, schedule) {
|
|
10416
|
-
const
|
|
10416
|
+
const C3 = PERT_CONST;
|
|
10417
10417
|
const T = schedule.projectDuration || 1;
|
|
10418
10418
|
const pxPerUnit = Math.min(56, Math.max(16, 1300 / T));
|
|
10419
|
-
const titleH = ast.title ?
|
|
10420
|
-
const leftPad =
|
|
10421
|
-
const topPad =
|
|
10419
|
+
const titleH = ast.title ? C3.TITLE_H : 0;
|
|
10420
|
+
const leftPad = C3.PAD;
|
|
10421
|
+
const topPad = C3.PAD + titleH + 14;
|
|
10422
10422
|
const intervals = ast.tasks.map((t) => {
|
|
10423
10423
|
const c = schedule.computed.get(t.id);
|
|
10424
10424
|
if (t.milestone) {
|
|
10425
10425
|
const cx = leftPad + c.es * pxPerUnit;
|
|
10426
|
-
return { id: t.id, x: cx -
|
|
10426
|
+
return { id: t.id, x: cx - C3.TS_MS_W / 2, w: C3.TS_MS_W };
|
|
10427
10427
|
}
|
|
10428
10428
|
const x = leftPad + c.es * pxPerUnit;
|
|
10429
|
-
const w = Math.max(
|
|
10429
|
+
const w = Math.max(C3.TS_MIN_W, t.duration * pxPerUnit);
|
|
10430
10430
|
return { id: t.id, x, w };
|
|
10431
10431
|
});
|
|
10432
10432
|
intervals.sort((a, b) => a.x - b.x || a.w - b.w);
|
|
@@ -10435,7 +10435,7 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10435
10435
|
for (const iv of intervals) {
|
|
10436
10436
|
let placed = -1;
|
|
10437
10437
|
for (let l = 0; l < laneRight.length; l++) {
|
|
10438
|
-
if (iv.x >= laneRight[l] +
|
|
10438
|
+
if (iv.x >= laneRight[l] + C3.TS_GAP) {
|
|
10439
10439
|
placed = l;
|
|
10440
10440
|
break;
|
|
10441
10441
|
}
|
|
@@ -10457,7 +10457,7 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10457
10457
|
for (const t of ast.tasks) {
|
|
10458
10458
|
const iv = intervalById.get(t.id);
|
|
10459
10459
|
const lane = laneOf.get(t.id);
|
|
10460
|
-
const y = topPad + lane * (
|
|
10460
|
+
const y = topPad + lane * (C3.TS_BOX_H + C3.TS_LANE_GAP);
|
|
10461
10461
|
const box2 = {
|
|
10462
10462
|
id: t.id,
|
|
10463
10463
|
task: t,
|
|
@@ -10465,7 +10465,7 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10465
10465
|
x: iv.x,
|
|
10466
10466
|
y,
|
|
10467
10467
|
width: iv.w,
|
|
10468
|
-
height:
|
|
10468
|
+
height: C3.TS_BOX_H,
|
|
10469
10469
|
milestone: t.milestone,
|
|
10470
10470
|
rank: 0
|
|
10471
10471
|
};
|
|
@@ -10493,7 +10493,7 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10493
10493
|
edges.push(edge);
|
|
10494
10494
|
}
|
|
10495
10495
|
}
|
|
10496
|
-
const contentBottom = topPad + laneCount *
|
|
10496
|
+
const contentBottom = topPad + laneCount * C3.TS_BOX_H + (laneCount - 1) * C3.TS_LANE_GAP;
|
|
10497
10497
|
const axisBaseline = contentBottom + 22;
|
|
10498
10498
|
const timeEnd = leftPad + T * pxPerUnit;
|
|
10499
10499
|
let maxRight = timeEnd;
|
|
@@ -10509,8 +10509,8 @@ function layoutTimescaled(ast, schedule) {
|
|
|
10509
10509
|
major: Math.abs(rounded % majorStep) < 1e-9
|
|
10510
10510
|
});
|
|
10511
10511
|
}
|
|
10512
|
-
const width = Math.ceil(Math.max(maxRight, timeEnd) +
|
|
10513
|
-
const height = Math.ceil(axisBaseline +
|
|
10512
|
+
const width = Math.ceil(Math.max(maxRight, timeEnd) + C3.PAD);
|
|
10513
|
+
const height = Math.ceil(axisBaseline + C3.AXIS_H + C3.FOOTER_H);
|
|
10514
10514
|
return {
|
|
10515
10515
|
width,
|
|
10516
10516
|
height,
|
|
@@ -12163,7 +12163,7 @@ var SequenceLayout = class {
|
|
|
12163
12163
|
left = mid - 20;
|
|
12164
12164
|
right = mid + 20;
|
|
12165
12165
|
}
|
|
12166
|
-
const
|
|
12166
|
+
const frame2 = {
|
|
12167
12167
|
op,
|
|
12168
12168
|
x: left,
|
|
12169
12169
|
y: frameTop,
|
|
@@ -12171,8 +12171,8 @@ var SequenceLayout = class {
|
|
|
12171
12171
|
height: frameBottom - frameTop,
|
|
12172
12172
|
operands: operandGeom
|
|
12173
12173
|
};
|
|
12174
|
-
if (messageSet && messageSet.length)
|
|
12175
|
-
this.fragments.push(
|
|
12174
|
+
if (messageSet && messageSet.length) frame2.messageSet = messageSet;
|
|
12175
|
+
this.fragments.push(frame2);
|
|
12176
12176
|
this.noteRight(left, right - left);
|
|
12177
12177
|
this.y = frameBottom;
|
|
12178
12178
|
}
|
|
@@ -12840,7 +12840,7 @@ var PETRI_CONST = {
|
|
|
12840
12840
|
CHAR_W: 6.2
|
|
12841
12841
|
};
|
|
12842
12842
|
function layoutPetri(ast) {
|
|
12843
|
-
const
|
|
12843
|
+
const C3 = PETRI_CONST;
|
|
12844
12844
|
const dir = ast.direction;
|
|
12845
12845
|
const warnings = [...ast.warnings];
|
|
12846
12846
|
const kindOf = /* @__PURE__ */ new Map();
|
|
@@ -12924,10 +12924,10 @@ function layoutPetri(ast) {
|
|
|
12924
12924
|
}
|
|
12925
12925
|
}
|
|
12926
12926
|
const sizeOf5 = (id) => {
|
|
12927
|
-
if (kindOf.get(id) === "place") return { halfW:
|
|
12927
|
+
if (kindOf.get(id) === "place") return { halfW: C3.PLACE_R, halfH: C3.PLACE_R, r: C3.PLACE_R };
|
|
12928
12928
|
const tr = ast.transitions.find((t) => t.id === id);
|
|
12929
|
-
const long = tr.kind === "timed" ?
|
|
12930
|
-
const thin = tr.kind === "timed" ?
|
|
12929
|
+
const long = tr.kind === "timed" ? C3.TRANS_BOX_H : C3.TRANS_BAR_H;
|
|
12930
|
+
const thin = tr.kind === "timed" ? C3.TRANS_BOX_W : C3.TRANS_BAR_W;
|
|
12931
12931
|
const halfW = dir === "lr" ? thin / 2 : long / 2;
|
|
12932
12932
|
const halfH = dir === "lr" ? long / 2 : thin / 2;
|
|
12933
12933
|
return { halfW, halfH, r: 0 };
|
|
@@ -12941,15 +12941,15 @@ function layoutPetri(ast) {
|
|
|
12941
12941
|
return dir === "lr" ? s.halfH : s.halfW;
|
|
12942
12942
|
};
|
|
12943
12943
|
const layerHalf = layers.map((arr) => Math.max(0, ...arr.map(flowHalf)));
|
|
12944
|
-
const slot = Math.max(0, ...ids.map(crossHalf)) * 2 +
|
|
12944
|
+
const slot = Math.max(0, ...ids.map(crossHalf)) * 2 + C3.RANK_GAP;
|
|
12945
12945
|
const maxCount = Math.max(1, ...layers.map((a) => a.length));
|
|
12946
|
-
const crossCenter =
|
|
12946
|
+
const crossCenter = C3.MARGIN + C3.LABEL_LINE_H * 2 + maxCount * slot / 2;
|
|
12947
12947
|
const flowCenter = [];
|
|
12948
|
-
let acc =
|
|
12948
|
+
let acc = C3.MARGIN + C3.LABEL_LINE_H * 2;
|
|
12949
12949
|
for (let L = 0; L < layers.length; L++) {
|
|
12950
12950
|
acc += layerHalf[L];
|
|
12951
12951
|
flowCenter[L] = acc;
|
|
12952
|
-
acc += layerHalf[L] +
|
|
12952
|
+
acc += layerHalf[L] + C3.LAYER_GAP;
|
|
12953
12953
|
}
|
|
12954
12954
|
const geom = /* @__PURE__ */ new Map();
|
|
12955
12955
|
layers.forEach((arr, L) => {
|
|
@@ -13032,12 +13032,12 @@ function layoutPetri(ast) {
|
|
|
13032
13032
|
if (dir === "lr") {
|
|
13033
13033
|
const pA = boundary(A, 0, 1);
|
|
13034
13034
|
const pB = boundary(B, 0, 1);
|
|
13035
|
-
const bowY = bandMaxCross +
|
|
13035
|
+
const bowY = bandMaxCross + C3.BACKEDGE_BOW;
|
|
13036
13036
|
points = [pA, { x: pA.x, y: bowY }, { x: pB.x, y: bowY }, pB];
|
|
13037
13037
|
} else {
|
|
13038
13038
|
const pA = boundary(A, 1, 0);
|
|
13039
13039
|
const pB = boundary(B, 1, 0);
|
|
13040
|
-
const bowX = bandMaxCross +
|
|
13040
|
+
const bowX = bandMaxCross + C3.BACKEDGE_BOW;
|
|
13041
13041
|
points = [pA, { x: bowX, y: pA.y }, { x: bowX, y: pB.y }, pB];
|
|
13042
13042
|
}
|
|
13043
13043
|
}
|
|
@@ -13048,8 +13048,8 @@ function layoutPetri(ast) {
|
|
|
13048
13048
|
const ddx = p1.x - p0.x;
|
|
13049
13049
|
const ddy = p1.y - p0.y;
|
|
13050
13050
|
const dl = Math.hypot(ddx, ddy) || 1;
|
|
13051
|
-
const labelX = mx - ddy / dl *
|
|
13052
|
-
const labelY = my + ddx / dl *
|
|
13051
|
+
const labelX = mx - ddy / dl * C3.ARC_WEIGHT_OFFSET;
|
|
13052
|
+
const labelY = my + ddx / dl * C3.ARC_WEIGHT_OFFSET;
|
|
13053
13053
|
return { arc: a, type: a.type, weight: a.weight, points, reversed, labelX, labelY };
|
|
13054
13054
|
});
|
|
13055
13055
|
const hasIncoming = (pid2) => ast.arcs.some((a) => a.to === pid2);
|
|
@@ -13086,24 +13086,24 @@ function layoutPetri(ast) {
|
|
|
13086
13086
|
bb.maxY = Math.max(bb.maxY, y0, y1);
|
|
13087
13087
|
};
|
|
13088
13088
|
const addPt = (p) => addBox(p.x, p.y, p.x, p.y);
|
|
13089
|
-
const labelW = (s) => s ? s.length *
|
|
13089
|
+
const labelW = (s) => s ? s.length * C3.CHAR_W : 0;
|
|
13090
13090
|
for (const pb of placeBoxes) {
|
|
13091
13091
|
addBox(pb.cx - pb.r, pb.cy - pb.r, pb.cx + pb.r, pb.cy + pb.r);
|
|
13092
13092
|
const lw = Math.max(labelW(pb.place.id), labelW(pb.place.label)) / 2;
|
|
13093
|
-
addBox(pb.cx - lw, pb.cy - pb.r -
|
|
13094
|
-
if (pb.place.capacity !== void 0) addBox(pb.cx, pb.cy + pb.r, pb.cx + 24, pb.cy + pb.r +
|
|
13093
|
+
addBox(pb.cx - lw, pb.cy - pb.r - C3.LABEL_LINE_H * 2, pb.cx + lw, pb.cy);
|
|
13094
|
+
if (pb.place.capacity !== void 0) addBox(pb.cx, pb.cy + pb.r, pb.cx + 24, pb.cy + pb.r + C3.LABEL_LINE_H);
|
|
13095
13095
|
}
|
|
13096
13096
|
for (const tb of transBoxes) {
|
|
13097
13097
|
addBox(tb.cx - tb.w / 2, tb.cy - tb.h / 2, tb.cx + tb.w / 2, tb.cy + tb.h / 2);
|
|
13098
13098
|
const lw = Math.max(labelW(tb.transition.id), labelW(tb.transition.label)) / 2;
|
|
13099
|
-
addBox(tb.cx - lw, tb.cy - tb.h / 2 -
|
|
13099
|
+
addBox(tb.cx - lw, tb.cy - tb.h / 2 - C3.LABEL_LINE_H * 2, tb.cx + lw, tb.cy);
|
|
13100
13100
|
}
|
|
13101
13101
|
for (const ag of arcGeoms) {
|
|
13102
13102
|
ag.points.forEach(addPt);
|
|
13103
13103
|
if (ag.weight > 1) addBox(ag.labelX - 6, ag.labelY - 8, ag.labelX + 6, ag.labelY + 4);
|
|
13104
13104
|
}
|
|
13105
|
-
const dx =
|
|
13106
|
-
const dy =
|
|
13105
|
+
const dx = C3.MARGIN - bb.minX;
|
|
13106
|
+
const dy = C3.MARGIN - bb.minY;
|
|
13107
13107
|
const shift = (p) => ({ x: p.x + dx, y: p.y + dy });
|
|
13108
13108
|
placeBoxes.forEach((pb) => {
|
|
13109
13109
|
pb.cx += dx;
|
|
@@ -13118,8 +13118,8 @@ function layoutPetri(ast) {
|
|
|
13118
13118
|
ag.labelX += dx;
|
|
13119
13119
|
ag.labelY += dy;
|
|
13120
13120
|
});
|
|
13121
|
-
const width = bb.maxX - bb.minX + 2 *
|
|
13122
|
-
const height = bb.maxY - bb.minY + 2 *
|
|
13121
|
+
const width = bb.maxX - bb.minX + 2 * C3.MARGIN;
|
|
13122
|
+
const height = bb.maxY - bb.minY + 2 * C3.MARGIN;
|
|
13123
13123
|
const subclass = detectSubclass(ast);
|
|
13124
13124
|
return {
|
|
13125
13125
|
width: Math.round(width),
|
|
@@ -17727,7 +17727,7 @@ var FAULTTREE_CONST = {
|
|
|
17727
17727
|
TITLE_H: 34
|
|
17728
17728
|
};
|
|
17729
17729
|
function eventBox(label) {
|
|
17730
|
-
const
|
|
17730
|
+
const C3 = FAULTTREE_CONST;
|
|
17731
17731
|
const words = label.split(/\s+/).filter(Boolean);
|
|
17732
17732
|
const lines = [];
|
|
17733
17733
|
let cur = "";
|
|
@@ -17745,11 +17745,11 @@ function eventBox(label) {
|
|
|
17745
17745
|
else if (cur && lines.length === 2) lines[1] = `${lines[1]} ${cur}`;
|
|
17746
17746
|
if (lines.length === 0) lines.push(label);
|
|
17747
17747
|
const longest = Math.max(...lines.map((l) => l.length), 1);
|
|
17748
|
-
const width = Math.min(
|
|
17748
|
+
const width = Math.min(C3.EVENT_MAX_W, Math.max(C3.EVENT_MIN_W, Math.ceil(longest * C3.CHAR_W) + 2 * C3.EVENT_PAD_X));
|
|
17749
17749
|
return { width, lines };
|
|
17750
17750
|
}
|
|
17751
17751
|
function layoutFaultTree(ast) {
|
|
17752
|
-
const
|
|
17752
|
+
const C3 = FAULTTREE_CONST;
|
|
17753
17753
|
const analysis = analyseFaultTree(ast);
|
|
17754
17754
|
const byId = new Map(ast.events.map((e) => [e.id, e]));
|
|
17755
17755
|
const refCount = /* @__PURE__ */ new Map();
|
|
@@ -17765,13 +17765,13 @@ function layoutFaultTree(ast) {
|
|
|
17765
17765
|
case "intermediate":
|
|
17766
17766
|
return eventBox(label).width;
|
|
17767
17767
|
case "basic":
|
|
17768
|
-
return 2 *
|
|
17768
|
+
return 2 * C3.BASIC_R;
|
|
17769
17769
|
case "undeveloped":
|
|
17770
|
-
return
|
|
17770
|
+
return C3.DIAMOND_W;
|
|
17771
17771
|
case "house":
|
|
17772
|
-
return
|
|
17772
|
+
return C3.HOUSE_W;
|
|
17773
17773
|
case "condition":
|
|
17774
|
-
return
|
|
17774
|
+
return C3.COND_W;
|
|
17775
17775
|
}
|
|
17776
17776
|
};
|
|
17777
17777
|
const build = (eventId, depth, onPath) => {
|
|
@@ -17818,9 +17818,9 @@ function layoutFaultTree(ast) {
|
|
|
17818
17818
|
}
|
|
17819
17819
|
let x = originX;
|
|
17820
17820
|
for (const c of n.children) {
|
|
17821
|
-
x += place(c, x) +
|
|
17821
|
+
x += place(c, x) + C3.SIBLING_GAP;
|
|
17822
17822
|
}
|
|
17823
|
-
const childrenWidth = x - originX -
|
|
17823
|
+
const childrenWidth = x - originX - C3.SIBLING_GAP;
|
|
17824
17824
|
if (n.width <= childrenWidth) {
|
|
17825
17825
|
n.cx = (n.children[0].cx + n.children[n.children.length - 1].cx) / 2;
|
|
17826
17826
|
return childrenWidth;
|
|
@@ -17830,24 +17830,24 @@ function layoutFaultTree(ast) {
|
|
|
17830
17830
|
n.cx = originX + n.width / 2;
|
|
17831
17831
|
return n.width;
|
|
17832
17832
|
};
|
|
17833
|
-
const baseY =
|
|
17834
|
-
place(root,
|
|
17835
|
-
const ROW_PITCH =
|
|
17833
|
+
const baseY = C3.CANVAS_PAD + (ast.title ? C3.TITLE_H : 0) + (ast.analysis.probability ? 18 : 0);
|
|
17834
|
+
place(root, C3.CANVAS_PAD);
|
|
17835
|
+
const ROW_PITCH = C3.EVENT_H + C3.GATE_GAP + C3.GATE_H + C3.LEVEL_GAP;
|
|
17836
17836
|
const rowY = (depth) => baseY + depth * ROW_PITCH;
|
|
17837
|
-
const centerY = (n) => n.topY +
|
|
17837
|
+
const centerY = (n) => n.topY + C3.EVENT_H / 2;
|
|
17838
17838
|
const halfH = (role) => {
|
|
17839
17839
|
switch (role) {
|
|
17840
17840
|
case "top":
|
|
17841
17841
|
case "intermediate":
|
|
17842
|
-
return
|
|
17842
|
+
return C3.EVENT_H / 2;
|
|
17843
17843
|
case "basic":
|
|
17844
|
-
return
|
|
17844
|
+
return C3.BASIC_R;
|
|
17845
17845
|
case "undeveloped":
|
|
17846
|
-
return
|
|
17846
|
+
return C3.DIAMOND_W / 2;
|
|
17847
17847
|
case "house":
|
|
17848
|
-
return
|
|
17848
|
+
return C3.HOUSE_H / 2;
|
|
17849
17849
|
case "condition":
|
|
17850
|
-
return
|
|
17850
|
+
return C3.COND_H / 2;
|
|
17851
17851
|
}
|
|
17852
17852
|
};
|
|
17853
17853
|
const topAnchorY = (n) => n.role === "top" || n.role === "intermediate" ? n.topY : centerY(n) - halfH(n.role);
|
|
@@ -17861,7 +17861,7 @@ function layoutFaultTree(ast) {
|
|
|
17861
17861
|
cx: n.cx,
|
|
17862
17862
|
topY: n.topY,
|
|
17863
17863
|
width: n.width,
|
|
17864
|
-
height: n.role === "top" || n.role === "intermediate" ?
|
|
17864
|
+
height: n.role === "top" || n.role === "intermediate" ? C3.EVENT_H : 2 * halfH(n.role),
|
|
17865
17865
|
depth: n.depth,
|
|
17866
17866
|
shared: n.shared
|
|
17867
17867
|
};
|
|
@@ -17869,28 +17869,28 @@ function layoutFaultTree(ast) {
|
|
|
17869
17869
|
(instancesByEvent.get(n.eventId) ?? instancesByEvent.set(n.eventId, []).get(n.eventId)).push(lay);
|
|
17870
17870
|
if (n.gate && n.children.length > 0) {
|
|
17871
17871
|
const gx = n.cx;
|
|
17872
|
-
const gy = n.topY +
|
|
17872
|
+
const gy = n.topY + C3.EVENT_H + C3.GATE_GAP + C3.GATE_H / 2;
|
|
17873
17873
|
const glay = {
|
|
17874
17874
|
gate: n.gate,
|
|
17875
17875
|
ownerInstanceId: n.instanceId,
|
|
17876
17876
|
cx: gx,
|
|
17877
17877
|
cy: gy,
|
|
17878
|
-
width:
|
|
17879
|
-
height:
|
|
17878
|
+
width: C3.GATE_W,
|
|
17879
|
+
height: C3.GATE_H
|
|
17880
17880
|
};
|
|
17881
17881
|
if ((n.gate.kind === "inhibit" || n.gate.kind === "pand") && (n.gate.condition || n.gate.order)) {
|
|
17882
17882
|
const condEv = n.gate.condition ? byId.get(n.gate.condition) : void 0;
|
|
17883
17883
|
const text2 = n.gate.order ? n.gate.order.join(" \u227A ") : condEv?.label ?? n.gate.condition ?? "";
|
|
17884
17884
|
glay.cond = {
|
|
17885
|
-
x: gx +
|
|
17885
|
+
x: gx + C3.GATE_W / 2 + C3.COND_GAP + C3.COND_W / 2,
|
|
17886
17886
|
y: gy,
|
|
17887
|
-
w:
|
|
17888
|
-
h:
|
|
17887
|
+
w: C3.COND_W,
|
|
17888
|
+
h: C3.COND_H,
|
|
17889
17889
|
text: text2
|
|
17890
17890
|
};
|
|
17891
17891
|
}
|
|
17892
17892
|
gates.push(glay);
|
|
17893
|
-
const gateBaseY = gy +
|
|
17893
|
+
const gateBaseY = gy + C3.GATE_H / 2;
|
|
17894
17894
|
for (const c of n.children) {
|
|
17895
17895
|
c.topY = rowY(c.depth);
|
|
17896
17896
|
const childTop = topAnchorY(c);
|
|
@@ -17908,7 +17908,7 @@ function layoutFaultTree(ast) {
|
|
|
17908
17908
|
const cutSetBoxes = [];
|
|
17909
17909
|
let boxIndex = 0;
|
|
17910
17910
|
const visualBounds = (e) => {
|
|
17911
|
-
const cy = e.topY +
|
|
17911
|
+
const cy = e.topY + C3.EVENT_H / 2;
|
|
17912
17912
|
const hw = e.width / 2;
|
|
17913
17913
|
const hh = halfH(e.role);
|
|
17914
17914
|
return { minX: e.cx - hw, maxX: e.cx + hw, minY: cy - hh, maxY: cy + hh };
|
|
@@ -17922,7 +17922,7 @@ function layoutFaultTree(ast) {
|
|
|
17922
17922
|
maxX2 = Math.max(maxX2, b.maxX);
|
|
17923
17923
|
maxY2 = Math.max(maxY2, b.maxY);
|
|
17924
17924
|
}
|
|
17925
|
-
const pad =
|
|
17925
|
+
const pad = C3.CUTSET_PAD + idx % 3 * C3.CUTSET_OFFSET_STEP;
|
|
17926
17926
|
return {
|
|
17927
17927
|
cutSet: cs,
|
|
17928
17928
|
index: idx,
|
|
@@ -17953,7 +17953,7 @@ function layoutFaultTree(ast) {
|
|
|
17953
17953
|
ownerInstanceId: inst.instanceId,
|
|
17954
17954
|
name: tr.name,
|
|
17955
17955
|
x: inst.cx,
|
|
17956
|
-
y: inst.topY +
|
|
17956
|
+
y: inst.topY + C3.EVENT_H + C3.GATE_GAP
|
|
17957
17957
|
});
|
|
17958
17958
|
}
|
|
17959
17959
|
let maxX = 0, maxY = 0;
|
|
@@ -17967,7 +17967,7 @@ function layoutFaultTree(ast) {
|
|
|
17967
17967
|
if (e.role === "basic" || e.role === "undeveloped" || e.role === "house") {
|
|
17968
17968
|
const hasLabel = !!e.event.label && e.event.label !== e.event.id;
|
|
17969
17969
|
const hasProb = ast.analysis.probability && e.event.prob !== void 0;
|
|
17970
|
-
if (hasLabel || hasProb) bump(b.maxX, b.maxY + (hasLabel && hasProb ?
|
|
17970
|
+
if (hasLabel || hasProb) bump(b.maxX, b.maxY + (hasLabel && hasProb ? C3.CAP_GAP + C3.CAP_LINE_H + 8 : C3.CAP_GAP + 6));
|
|
17971
17971
|
}
|
|
17972
17972
|
}
|
|
17973
17973
|
for (const g of gates) {
|
|
@@ -17976,7 +17976,7 @@ function layoutFaultTree(ast) {
|
|
|
17976
17976
|
}
|
|
17977
17977
|
for (const box2 of cutSetBoxes) bump(box2.x + box2.width, box2.y + box2.height);
|
|
17978
17978
|
for (const t of transfers) bump(t.x + 22, t.y + 30);
|
|
17979
|
-
if (ast.title) bump(
|
|
17979
|
+
if (ast.title) bump(C3.CANVAS_PAD + ast.title.length * 8.5, 0);
|
|
17980
17980
|
if (ast.analysis.probability) {
|
|
17981
17981
|
const top = events.find((e) => e.role === "top");
|
|
17982
17982
|
if (top) bump(top.cx + 95, 0);
|
|
@@ -17989,8 +17989,8 @@ function layoutFaultTree(ast) {
|
|
|
17989
17989
|
edges,
|
|
17990
17990
|
cutSetBoxes,
|
|
17991
17991
|
transfers,
|
|
17992
|
-
width: Math.ceil(maxX +
|
|
17993
|
-
height: Math.ceil(maxY +
|
|
17992
|
+
width: Math.ceil(maxX + C3.CANVAS_PAD),
|
|
17993
|
+
height: Math.ceil(maxY + C3.CANVAS_PAD)
|
|
17994
17994
|
};
|
|
17995
17995
|
}
|
|
17996
17996
|
function r(n) {
|
|
@@ -18516,40 +18516,40 @@ var BOWTIE_CONST = {
|
|
|
18516
18516
|
LEGEND_H: 30
|
|
18517
18517
|
};
|
|
18518
18518
|
function dropBottom(barrier) {
|
|
18519
|
-
const
|
|
18520
|
-
if (barrier.escalations.length === 0) return
|
|
18521
|
-
let cursor =
|
|
18522
|
-
let lastBottom =
|
|
18519
|
+
const C3 = BOWTIE_CONST;
|
|
18520
|
+
if (barrier.escalations.length === 0) return C3.NODE_H / 2;
|
|
18521
|
+
let cursor = C3.EF_DROP;
|
|
18522
|
+
let lastBottom = C3.NODE_H / 2;
|
|
18523
18523
|
for (const esc of barrier.escalations) {
|
|
18524
|
-
lastBottom = cursor +
|
|
18525
|
-
cursor +=
|
|
18524
|
+
lastBottom = cursor + C3.NODE_H / 2;
|
|
18525
|
+
cursor += C3.NODE_H + C3.EF_GAP;
|
|
18526
18526
|
for (const _ef of esc.barriers) {
|
|
18527
|
-
lastBottom = cursor +
|
|
18528
|
-
cursor +=
|
|
18527
|
+
lastBottom = cursor + C3.NODE_H / 2;
|
|
18528
|
+
cursor += C3.NODE_H + C3.EF_GAP;
|
|
18529
18529
|
}
|
|
18530
18530
|
}
|
|
18531
18531
|
return lastBottom;
|
|
18532
18532
|
}
|
|
18533
18533
|
function bandLayout(lines) {
|
|
18534
|
-
const
|
|
18535
|
-
const below = lines.map((l) => Math.max(...l.barriers.map(dropBottom),
|
|
18534
|
+
const C3 = BOWTIE_CONST;
|
|
18535
|
+
const below = lines.map((l) => Math.max(...l.barriers.map(dropBottom), C3.NODE_H / 2));
|
|
18536
18536
|
const rel = [];
|
|
18537
18537
|
for (let k = 0; k < lines.length; k++) {
|
|
18538
18538
|
if (k === 0) {
|
|
18539
18539
|
rel.push(0);
|
|
18540
18540
|
continue;
|
|
18541
18541
|
}
|
|
18542
|
-
const pitch = Math.max(
|
|
18542
|
+
const pitch = Math.max(C3.ROW_BAND_H, below[k - 1] + C3.ROW_GAP + C3.NODE_H / 2);
|
|
18543
18543
|
rel.push(rel[k - 1] + pitch);
|
|
18544
18544
|
}
|
|
18545
18545
|
const last = lines.length - 1;
|
|
18546
18546
|
const blockMid = lines.length ? rel[last] / 2 : 0;
|
|
18547
|
-
const aboveExtent = lines.length ? blockMid +
|
|
18547
|
+
const aboveExtent = lines.length ? blockMid + C3.NODE_H / 2 : 0;
|
|
18548
18548
|
const belowExtent = lines.length ? rel[last] - blockMid + below[last] : 0;
|
|
18549
18549
|
return { rel, below, blockMid, aboveExtent, belowExtent };
|
|
18550
18550
|
}
|
|
18551
18551
|
function layoutBowtie(ast) {
|
|
18552
|
-
const
|
|
18552
|
+
const C3 = BOWTIE_CONST;
|
|
18553
18553
|
const boxes = [];
|
|
18554
18554
|
const lines = [];
|
|
18555
18555
|
const escalationLines = [];
|
|
@@ -18557,42 +18557,42 @@ function layoutBowtie(ast) {
|
|
|
18557
18557
|
const conseqLines = ast.consequences.map((c) => ({ id: c.id, label: c.label, barriers: c.barriers }));
|
|
18558
18558
|
const left = bandLayout(threatLines);
|
|
18559
18559
|
const right = bandLayout(conseqLines);
|
|
18560
|
-
const aboveCy = Math.max(left.aboveExtent, right.aboveExtent,
|
|
18560
|
+
const aboveCy = Math.max(left.aboveExtent, right.aboveExtent, C3.TOPEVENT_R);
|
|
18561
18561
|
const maxLeftChain = Math.max(0, ...threatLines.map((l) => l.barriers.length));
|
|
18562
|
-
const innerOffset =
|
|
18563
|
-
const cx =
|
|
18564
|
-
const titleZone = ast.title ?
|
|
18565
|
-
const hazardReserve = ast.hazard ?
|
|
18566
|
-
const cy =
|
|
18567
|
-
const topEvent = { cx, cy, r:
|
|
18562
|
+
const innerOffset = C3.TOPEVENT_R + C3.CENTER_GUTTER + C3.BARRIER_W / 2;
|
|
18563
|
+
const cx = C3.PAGE_PAD + C3.NODE_W / 2 + maxLeftChain * C3.WING_X_STEP + innerOffset;
|
|
18564
|
+
const titleZone = ast.title ? C3.TITLE_H : 0;
|
|
18565
|
+
const hazardReserve = ast.hazard ? C3.NODE_H + C3.HAZARD_GAP : 0;
|
|
18566
|
+
const cy = C3.PAGE_PAD + titleZone + hazardReserve + aboveCy;
|
|
18567
|
+
const topEvent = { cx, cy, r: C3.TOPEVENT_R, label: ast.topEvent };
|
|
18568
18568
|
let hazardTie;
|
|
18569
18569
|
if (ast.hazard) {
|
|
18570
|
-
const hcy =
|
|
18571
|
-
boxes.push({ id: "hazard", role: "hazard", label: ast.hazard, cx, cy: hcy, width:
|
|
18572
|
-
hazardTie = { x: cx, y1: hcy +
|
|
18570
|
+
const hcy = C3.PAGE_PAD + titleZone + C3.NODE_H / 2;
|
|
18571
|
+
boxes.push({ id: "hazard", role: "hazard", label: ast.hazard, cx, cy: hcy, width: C3.HAZARD_W, height: C3.NODE_H });
|
|
18572
|
+
hazardTie = { x: cx, y1: hcy + C3.NODE_H / 2, y2: cy - C3.TOPEVENT_R };
|
|
18573
18573
|
}
|
|
18574
18574
|
const emitWing = (wing, band, side) => {
|
|
18575
|
-
const innerX = side === "prevent" ? cx -
|
|
18575
|
+
const innerX = side === "prevent" ? cx - C3.TOPEVENT_R - C3.CENTER_GUTTER - C3.BARRIER_W / 2 : cx + C3.TOPEVENT_R + C3.CENTER_GUTTER + C3.BARRIER_W / 2;
|
|
18576
18576
|
wing.forEach((line2, k) => {
|
|
18577
18577
|
const by = cy + (band.rel[k] - band.blockMid);
|
|
18578
18578
|
const n = line2.barriers.length;
|
|
18579
18579
|
const barrierX = line2.barriers.map((_b, j) => {
|
|
18580
18580
|
if (side === "prevent") {
|
|
18581
18581
|
const stepsFromInner = n - 1 - j;
|
|
18582
|
-
return innerX - stepsFromInner *
|
|
18582
|
+
return innerX - stepsFromInner * C3.WING_X_STEP;
|
|
18583
18583
|
}
|
|
18584
|
-
return innerX + j *
|
|
18584
|
+
return innerX + j * C3.WING_X_STEP;
|
|
18585
18585
|
});
|
|
18586
18586
|
const outerBarrierX = side === "prevent" ? Math.min(...barrierX) : Math.max(...barrierX);
|
|
18587
|
-
const headX = side === "prevent" ? outerBarrierX -
|
|
18587
|
+
const headX = side === "prevent" ? outerBarrierX - C3.WING_X_STEP : outerBarrierX + C3.WING_X_STEP;
|
|
18588
18588
|
boxes.push({
|
|
18589
18589
|
id: line2.id,
|
|
18590
18590
|
role: side === "prevent" ? "threat" : "consequence",
|
|
18591
18591
|
label: line2.label,
|
|
18592
18592
|
cx: headX,
|
|
18593
18593
|
cy: by,
|
|
18594
|
-
width:
|
|
18595
|
-
height:
|
|
18594
|
+
width: C3.NODE_W,
|
|
18595
|
+
height: C3.NODE_H
|
|
18596
18596
|
});
|
|
18597
18597
|
line2.barriers.forEach((b, j) => {
|
|
18598
18598
|
const bx = barrierX[j];
|
|
@@ -18602,71 +18602,71 @@ function layoutBowtie(ast) {
|
|
|
18602
18602
|
label: b.label,
|
|
18603
18603
|
cx: bx,
|
|
18604
18604
|
cy: by,
|
|
18605
|
-
width:
|
|
18606
|
-
height:
|
|
18605
|
+
width: C3.BARRIER_W,
|
|
18606
|
+
height: C3.NODE_H,
|
|
18607
18607
|
side,
|
|
18608
18608
|
lineId: line2.id,
|
|
18609
18609
|
order: j
|
|
18610
18610
|
});
|
|
18611
18611
|
if (b.escalations.length) {
|
|
18612
|
-
let cursor =
|
|
18613
|
-
let connectFromY = by +
|
|
18612
|
+
let cursor = C3.EF_DROP;
|
|
18613
|
+
let connectFromY = by + C3.NODE_H / 2;
|
|
18614
18614
|
for (const esc of b.escalations) {
|
|
18615
18615
|
const escCy = by + cursor;
|
|
18616
|
-
escalationLines.push({ x: bx, y1: connectFromY, y2: escCy -
|
|
18616
|
+
escalationLines.push({ x: bx, y1: connectFromY, y2: escCy - C3.NODE_H / 2 });
|
|
18617
18617
|
boxes.push({
|
|
18618
18618
|
id: esc.id,
|
|
18619
18619
|
role: "escalation",
|
|
18620
18620
|
label: esc.label,
|
|
18621
18621
|
cx: bx,
|
|
18622
18622
|
cy: escCy,
|
|
18623
|
-
width:
|
|
18624
|
-
height:
|
|
18623
|
+
width: C3.NODE_W,
|
|
18624
|
+
height: C3.NODE_H,
|
|
18625
18625
|
lineId: line2.id,
|
|
18626
18626
|
barrierId: b.id
|
|
18627
18627
|
});
|
|
18628
|
-
connectFromY = escCy +
|
|
18629
|
-
cursor +=
|
|
18628
|
+
connectFromY = escCy + C3.NODE_H / 2;
|
|
18629
|
+
cursor += C3.NODE_H + C3.EF_GAP;
|
|
18630
18630
|
for (const ef of esc.barriers) {
|
|
18631
18631
|
const efCy = by + cursor;
|
|
18632
|
-
escalationLines.push({ x: bx, y1: connectFromY, y2: efCy -
|
|
18632
|
+
escalationLines.push({ x: bx, y1: connectFromY, y2: efCy - C3.NODE_H / 2 });
|
|
18633
18633
|
boxes.push({
|
|
18634
18634
|
id: ef.id,
|
|
18635
18635
|
role: "ef-barrier",
|
|
18636
18636
|
label: ef.label,
|
|
18637
18637
|
cx: bx,
|
|
18638
18638
|
cy: efCy,
|
|
18639
|
-
width:
|
|
18640
|
-
height:
|
|
18639
|
+
width: C3.BARRIER_W,
|
|
18640
|
+
height: C3.NODE_H,
|
|
18641
18641
|
lineId: line2.id,
|
|
18642
18642
|
escalationId: esc.id
|
|
18643
18643
|
});
|
|
18644
|
-
connectFromY = efCy +
|
|
18645
|
-
cursor +=
|
|
18644
|
+
connectFromY = efCy + C3.NODE_H / 2;
|
|
18645
|
+
cursor += C3.NODE_H + C3.EF_GAP;
|
|
18646
18646
|
}
|
|
18647
18647
|
}
|
|
18648
18648
|
}
|
|
18649
18649
|
});
|
|
18650
18650
|
const dy = by - cy;
|
|
18651
|
-
const clampedDy = Math.max(-48, Math.min(
|
|
18652
|
-
const knotDx = Math.sqrt(Math.max(0,
|
|
18651
|
+
const clampedDy = Math.max(-48, Math.min(C3.TOPEVENT_R - 4, dy));
|
|
18652
|
+
const knotDx = Math.sqrt(Math.max(0, C3.TOPEVENT_R * C3.TOPEVENT_R - clampedDy * clampedDy));
|
|
18653
18653
|
const pts = [];
|
|
18654
18654
|
if (side === "prevent") {
|
|
18655
|
-
pts.push([headX +
|
|
18655
|
+
pts.push([headX + C3.NODE_W / 2, by]);
|
|
18656
18656
|
const ordered = [...line2.barriers].map((_b, j) => barrierX[j]).sort((a, b) => a - b);
|
|
18657
18657
|
for (const bx of ordered) {
|
|
18658
|
-
pts.push([bx -
|
|
18659
|
-
pts.push([bx +
|
|
18658
|
+
pts.push([bx - C3.BARRIER_W / 2, by]);
|
|
18659
|
+
pts.push([bx + C3.BARRIER_W / 2, by]);
|
|
18660
18660
|
}
|
|
18661
18661
|
pts.push([cx - knotDx, cy + clampedDy]);
|
|
18662
18662
|
} else {
|
|
18663
18663
|
pts.push([cx + knotDx, cy + clampedDy]);
|
|
18664
18664
|
const ordered = [...line2.barriers].map((_b, j) => barrierX[j]).sort((a, b) => a - b);
|
|
18665
18665
|
for (const bx of ordered) {
|
|
18666
|
-
pts.push([bx -
|
|
18667
|
-
pts.push([bx +
|
|
18666
|
+
pts.push([bx - C3.BARRIER_W / 2, by]);
|
|
18667
|
+
pts.push([bx + C3.BARRIER_W / 2, by]);
|
|
18668
18668
|
}
|
|
18669
|
-
pts.push([headX -
|
|
18669
|
+
pts.push([headX - C3.NODE_W / 2, by]);
|
|
18670
18670
|
}
|
|
18671
18671
|
const path2 = pts.map((p, idx) => `${idx === 0 ? "M" : "L"} ${r3(p[0])} ${r3(p[1])}`).join(" ");
|
|
18672
18672
|
const ax = side === "prevent" ? cx - knotDx : cx + knotDx;
|
|
@@ -18681,9 +18681,9 @@ function layoutBowtie(ast) {
|
|
|
18681
18681
|
maxY = Math.max(maxY, y);
|
|
18682
18682
|
};
|
|
18683
18683
|
for (const b of boxes) bump(b.cx + b.width / 2, b.cy + b.height / 2);
|
|
18684
|
-
bump(cx +
|
|
18685
|
-
if (ast.title) bump(
|
|
18686
|
-
const legendBand = ast.legend === "off" ? 0 :
|
|
18684
|
+
bump(cx + C3.TOPEVENT_R, cy + C3.TOPEVENT_R);
|
|
18685
|
+
if (ast.title) bump(C3.PAGE_PAD + ast.title.length * 9, 0);
|
|
18686
|
+
const legendBand = ast.legend === "off" ? 0 : C3.LEGEND_H;
|
|
18687
18687
|
return {
|
|
18688
18688
|
ast,
|
|
18689
18689
|
topEvent,
|
|
@@ -18691,8 +18691,8 @@ function layoutBowtie(ast) {
|
|
|
18691
18691
|
lines,
|
|
18692
18692
|
escalationLines,
|
|
18693
18693
|
...hazardTie ? { hazardTie } : {},
|
|
18694
|
-
width: Math.ceil(maxX +
|
|
18695
|
-
height: Math.ceil(maxY +
|
|
18694
|
+
width: Math.ceil(maxX + C3.PAGE_PAD),
|
|
18695
|
+
height: Math.ceil(maxY + C3.PAGE_PAD + legendBand)
|
|
18696
18696
|
};
|
|
18697
18697
|
}
|
|
18698
18698
|
function r3(n) {
|
|
@@ -19183,7 +19183,7 @@ var EVENTTREE_CONST = {
|
|
|
19183
19183
|
LEAF_LINE_H: 14
|
|
19184
19184
|
};
|
|
19185
19185
|
function layoutEventTree(ast) {
|
|
19186
|
-
const
|
|
19186
|
+
const C3 = EVENTTREE_CONST;
|
|
19187
19187
|
const analysis = analyseEventTree(ast);
|
|
19188
19188
|
const seqs = analysis.sequences;
|
|
19189
19189
|
const root = { children: [], y: 0, rowLo: 0, rowHi: 0 };
|
|
@@ -19215,15 +19215,15 @@ function layoutEventTree(ast) {
|
|
|
19215
19215
|
};
|
|
19216
19216
|
assignRows(root);
|
|
19217
19217
|
const rowCount = Math.max(nextRow, 1);
|
|
19218
|
-
const titleH = ast.title ?
|
|
19219
|
-
const headerY =
|
|
19220
|
-
const bodyTopY =
|
|
19221
|
-
const rowY = (row) => bodyTopY +
|
|
19222
|
-
const ieX2 =
|
|
19223
|
-
const gridX = (col) => ieX2 + (col + 1) *
|
|
19218
|
+
const titleH = ast.title ? C3.TITLE_H : 0;
|
|
19219
|
+
const headerY = C3.CANVAS_PAD + titleH + C3.HEADER_H * 0.6;
|
|
19220
|
+
const bodyTopY = C3.CANVAS_PAD + titleH + C3.HEADER_H;
|
|
19221
|
+
const rowY = (row) => bodyTopY + C3.ROW_H / 2 + row * C3.ROW_H;
|
|
19222
|
+
const ieX2 = C3.IE_LEFT + C3.IE_STUB;
|
|
19223
|
+
const gridX = (col) => ieX2 + (col + 1) * C3.COL_W;
|
|
19224
19224
|
const nFns = ast.functions.length;
|
|
19225
19225
|
const lastGridX = nFns > 0 ? gridX(nFns - 1) : ieX2;
|
|
19226
|
-
const outcomeX = lastGridX +
|
|
19226
|
+
const outcomeX = lastGridX + C3.OUTCOME_GAP;
|
|
19227
19227
|
const assignY = (n) => {
|
|
19228
19228
|
for (const c of n.children) assignY(c);
|
|
19229
19229
|
n.y = (rowY(n.rowLo) + rowY(n.rowHi)) / 2;
|
|
@@ -19246,7 +19246,7 @@ function layoutEventTree(ast) {
|
|
|
19246
19246
|
path: path2,
|
|
19247
19247
|
// Label sits above the horizontal run leading into this node.
|
|
19248
19248
|
labelX: (riserX + parentX) / 2,
|
|
19249
|
-
labelY: c.y +
|
|
19249
|
+
labelY: c.y + C3.FORK_LABEL_DY
|
|
19250
19250
|
});
|
|
19251
19251
|
walk(c, x, c.y);
|
|
19252
19252
|
}
|
|
@@ -19280,7 +19280,7 @@ function layoutEventTree(ast) {
|
|
|
19280
19280
|
headers.push({
|
|
19281
19281
|
kind: "initiating",
|
|
19282
19282
|
label: "Initiating Event",
|
|
19283
|
-
cx:
|
|
19283
|
+
cx: C3.IE_LEFT + C3.IE_STUB / 2
|
|
19284
19284
|
});
|
|
19285
19285
|
ast.functions.forEach((fn, col) => {
|
|
19286
19286
|
headers.push({
|
|
@@ -19290,16 +19290,16 @@ function layoutEventTree(ast) {
|
|
|
19290
19290
|
gridX: gridX(col)
|
|
19291
19291
|
});
|
|
19292
19292
|
});
|
|
19293
|
-
headers.push({ kind: "outcome", label: "Outcome", cx: outcomeX +
|
|
19294
|
-
headers.push({ kind: "frequency", label: "Frequency", cx: outcomeX +
|
|
19295
|
-
const gridBottom = rowY(rowCount - 1) +
|
|
19293
|
+
headers.push({ kind: "outcome", label: "Outcome", cx: outcomeX + C3.OUTCOME_W * 0.32 });
|
|
19294
|
+
headers.push({ kind: "frequency", label: "Frequency", cx: outcomeX + C3.OUTCOME_W * 0.82 });
|
|
19295
|
+
const gridBottom = rowY(rowCount - 1) + C3.ROW_H / 2;
|
|
19296
19296
|
const gridLines = ast.functions.map((_, col) => ({
|
|
19297
19297
|
x: gridX(col),
|
|
19298
19298
|
y1: bodyTopY,
|
|
19299
19299
|
y2: gridBottom
|
|
19300
19300
|
}));
|
|
19301
|
-
const width = outcomeX +
|
|
19302
|
-
const height = gridBottom +
|
|
19301
|
+
const width = outcomeX + C3.OUTCOME_W + C3.CANVAS_PAD;
|
|
19302
|
+
const height = gridBottom + C3.CANVAS_PAD;
|
|
19303
19303
|
return {
|
|
19304
19304
|
ast,
|
|
19305
19305
|
analysis,
|
|
@@ -19307,10 +19307,10 @@ function layoutEventTree(ast) {
|
|
|
19307
19307
|
forks,
|
|
19308
19308
|
leaves,
|
|
19309
19309
|
initiating: {
|
|
19310
|
-
x1:
|
|
19310
|
+
x1: C3.IE_LEFT,
|
|
19311
19311
|
x2: ieX2,
|
|
19312
19312
|
y: root.y,
|
|
19313
|
-
labelX:
|
|
19313
|
+
labelX: C3.IE_LEFT,
|
|
19314
19314
|
labelY: root.y - 10,
|
|
19315
19315
|
freqY: root.y + 16
|
|
19316
19316
|
},
|
|
@@ -20061,7 +20061,7 @@ var FMEA_CONST = {
|
|
|
20061
20061
|
AP_COL_W: 46
|
|
20062
20062
|
};
|
|
20063
20063
|
function buildColumnSpecs(hasActions, rank) {
|
|
20064
|
-
const
|
|
20064
|
+
const C3 = FMEA_CONST;
|
|
20065
20065
|
const specs = [
|
|
20066
20066
|
{ key: "no", label: "#", width: 28, align: "middle", numeric: true, field: (r7) => String(r7.index) },
|
|
20067
20067
|
{
|
|
@@ -20082,9 +20082,9 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20082
20082
|
numeric: false,
|
|
20083
20083
|
field: (r7) => r7.effects.length > 1 ? r7.effects.join("; ") : r7.effect
|
|
20084
20084
|
},
|
|
20085
|
-
{ key: "sev", label: "S", width:
|
|
20085
|
+
{ key: "sev", label: "S", width: C3.NUM_COL_W, align: "middle", band: "before", numeric: true, field: (r7) => String(r7.sev) },
|
|
20086
20086
|
{ key: "cause", label: "Cause(s)", width: 120, align: "start", numeric: false, field: (r7) => r7.cause },
|
|
20087
|
-
{ key: "occ", label: "O", width:
|
|
20087
|
+
{ key: "occ", label: "O", width: C3.NUM_COL_W, align: "middle", band: "before", numeric: true, field: (r7) => String(r7.occ) },
|
|
20088
20088
|
{
|
|
20089
20089
|
key: "controls",
|
|
20090
20090
|
label: "Current Controls",
|
|
@@ -20093,9 +20093,9 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20093
20093
|
numeric: false,
|
|
20094
20094
|
field: (r7) => controlsText(r7)
|
|
20095
20095
|
},
|
|
20096
|
-
{ key: "det", label: "D", width:
|
|
20097
|
-
{ key: "rpn", label: "RPN", width:
|
|
20098
|
-
{ key: "ap", label: "AP", width:
|
|
20096
|
+
{ key: "det", label: "D", width: C3.NUM_COL_W, align: "middle", band: "before", numeric: true, field: (r7) => String(r7.det) },
|
|
20097
|
+
{ key: "rpn", label: "RPN", width: C3.RPN_COL_W, align: "middle", band: "before", numeric: true, field: (r7) => String(r7.rpn) },
|
|
20098
|
+
{ key: "ap", label: "AP", width: C3.AP_COL_W, align: "middle", band: "before", numeric: true, field: (r7) => r7.ap }
|
|
20099
20099
|
];
|
|
20100
20100
|
if (hasActions) {
|
|
20101
20101
|
specs.push(
|
|
@@ -20111,7 +20111,7 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20111
20111
|
{
|
|
20112
20112
|
key: "rsev",
|
|
20113
20113
|
label: "S",
|
|
20114
|
-
width:
|
|
20114
|
+
width: C3.NUM_COL_W,
|
|
20115
20115
|
align: "middle",
|
|
20116
20116
|
band: "after",
|
|
20117
20117
|
numeric: true,
|
|
@@ -20121,7 +20121,7 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20121
20121
|
{
|
|
20122
20122
|
key: "rocc",
|
|
20123
20123
|
label: "O",
|
|
20124
|
-
width:
|
|
20124
|
+
width: C3.NUM_COL_W,
|
|
20125
20125
|
align: "middle",
|
|
20126
20126
|
band: "after",
|
|
20127
20127
|
numeric: true,
|
|
@@ -20131,7 +20131,7 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20131
20131
|
{
|
|
20132
20132
|
key: "rdet",
|
|
20133
20133
|
label: "D",
|
|
20134
|
-
width:
|
|
20134
|
+
width: C3.NUM_COL_W,
|
|
20135
20135
|
align: "middle",
|
|
20136
20136
|
band: "after",
|
|
20137
20137
|
numeric: true,
|
|
@@ -20141,7 +20141,7 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20141
20141
|
{
|
|
20142
20142
|
key: "rrpn",
|
|
20143
20143
|
label: "RPN",
|
|
20144
|
-
width:
|
|
20144
|
+
width: C3.RPN_COL_W,
|
|
20145
20145
|
align: "middle",
|
|
20146
20146
|
band: "after",
|
|
20147
20147
|
numeric: true,
|
|
@@ -20151,7 +20151,7 @@ function buildColumnSpecs(hasActions, rank) {
|
|
|
20151
20151
|
{
|
|
20152
20152
|
key: "rap",
|
|
20153
20153
|
label: "AP",
|
|
20154
|
-
width:
|
|
20154
|
+
width: C3.AP_COL_W,
|
|
20155
20155
|
align: "middle",
|
|
20156
20156
|
band: "after",
|
|
20157
20157
|
numeric: true,
|
|
@@ -20223,15 +20223,15 @@ function apRiskClass(ap) {
|
|
|
20223
20223
|
return "ap-low";
|
|
20224
20224
|
}
|
|
20225
20225
|
function layoutFmea(ast, analysisIn) {
|
|
20226
|
-
const
|
|
20226
|
+
const C3 = FMEA_CONST;
|
|
20227
20227
|
const analysis = analysisIn ?? analyseFmea(ast);
|
|
20228
20228
|
const specs = buildColumnSpecs(analysis.hasActions, ast.rank);
|
|
20229
20229
|
const metaKeys = Object.keys(ast.metadata);
|
|
20230
20230
|
const legendLines = (ast.target !== void 0 || ast.acceptable !== void 0 ? 1 : 0) + (ast.flag ? 1 : 0);
|
|
20231
20231
|
const metaRows = Math.ceil(metaKeys.length / 2) + legendLines;
|
|
20232
|
-
const titleH = ast.title ?
|
|
20233
|
-
const metaH = metaRows > 0 ? metaRows *
|
|
20234
|
-
const startX =
|
|
20232
|
+
const titleH = ast.title ? C3.TITLE_H : 0;
|
|
20233
|
+
const metaH = metaRows > 0 ? metaRows * C3.META_LINE_H + 6 : 0;
|
|
20234
|
+
const startX = C3.CANVAS_PAD;
|
|
20235
20235
|
let x = startX;
|
|
20236
20236
|
const columns = [];
|
|
20237
20237
|
const colX = {};
|
|
@@ -20250,15 +20250,15 @@ function layoutFmea(ast, analysisIn) {
|
|
|
20250
20250
|
}
|
|
20251
20251
|
const tableW = x - startX;
|
|
20252
20252
|
const hasBands = analysis.hasActions;
|
|
20253
|
-
const bandH = hasBands ?
|
|
20254
|
-
const topY =
|
|
20253
|
+
const bandH = hasBands ? C3.BAND_H : 0;
|
|
20254
|
+
const topY = C3.CANVAS_PAD + titleH + metaH;
|
|
20255
20255
|
const bandY = topY;
|
|
20256
20256
|
const headerY = topY + bandH;
|
|
20257
|
-
const bodyY = headerY +
|
|
20257
|
+
const bodyY = headerY + C3.HEADER_H;
|
|
20258
20258
|
const cells = [];
|
|
20259
20259
|
const rowHeights = [];
|
|
20260
20260
|
const rowY = [];
|
|
20261
|
-
const innerW = (s) => s.width -
|
|
20261
|
+
const innerW = (s) => s.width - C3.CELL_PAD_X * 2;
|
|
20262
20262
|
const wrapped = analysis.rows.map(
|
|
20263
20263
|
(row) => specs.map((s) => ({ spec: s, row, lines: wrapText3(s.field(row), innerW(s)) }))
|
|
20264
20264
|
);
|
|
@@ -20274,7 +20274,7 @@ function layoutFmea(ast, analysisIn) {
|
|
|
20274
20274
|
return w.lines.length;
|
|
20275
20275
|
});
|
|
20276
20276
|
const maxLines = Math.max(1, ...lineCounts);
|
|
20277
|
-
const h = Math.max(
|
|
20277
|
+
const h = Math.max(C3.ROW_MIN_H, maxLines * C3.LINE_H + C3.CELL_PAD_Y * 2);
|
|
20278
20278
|
rowHeights.push(h);
|
|
20279
20279
|
});
|
|
20280
20280
|
let yCursor = bodyY;
|
|
@@ -20324,14 +20324,14 @@ function layoutFmea(ast, analysisIn) {
|
|
|
20324
20324
|
if (before) bands.push({ label: "BEFORE ACTION", x: before.x, width: before.w, y: bandY, height: bandH });
|
|
20325
20325
|
if (after) bands.push({ label: "AFTER ACTION", x: after.x, width: after.w, y: bandY, height: bandH });
|
|
20326
20326
|
}
|
|
20327
|
-
const totalH = (rowY.length ? rowY[rowY.length - 1] + rowHeights[rowHeights.length - 1] : bodyY) +
|
|
20328
|
-
const width = tableW +
|
|
20327
|
+
const totalH = (rowY.length ? rowY[rowY.length - 1] + rowHeights[rowHeights.length - 1] : bodyY) + C3.CANVAS_PAD;
|
|
20328
|
+
const width = tableW + C3.CANVAS_PAD * 2;
|
|
20329
20329
|
return {
|
|
20330
20330
|
ast,
|
|
20331
20331
|
analysis,
|
|
20332
20332
|
columns,
|
|
20333
20333
|
headerY,
|
|
20334
|
-
headerH:
|
|
20334
|
+
headerH: C3.HEADER_H,
|
|
20335
20335
|
bodyY,
|
|
20336
20336
|
rowHeights,
|
|
20337
20337
|
rowY,
|
|
@@ -21054,11 +21054,11 @@ var RBD_CONST = {
|
|
|
21054
21054
|
HEADER_H: 26
|
|
21055
21055
|
};
|
|
21056
21056
|
function blockWidth(label) {
|
|
21057
|
-
const
|
|
21058
|
-
return Math.min(
|
|
21057
|
+
const C3 = RBD_CONST;
|
|
21058
|
+
return Math.min(C3.BLOCK_MAX_W, Math.max(C3.BLOCK_MIN_W, Math.ceil(label.length * C3.CHAR_W) + 2 * C3.PAD_X));
|
|
21059
21059
|
}
|
|
21060
21060
|
function layoutRbd(ast) {
|
|
21061
|
-
const
|
|
21061
|
+
const C3 = RBD_CONST;
|
|
21062
21062
|
const analysis = analyseRbd(ast);
|
|
21063
21063
|
const spof = new Set(analysis.blocks.filter((b) => b.isSpof).map((b) => b.id));
|
|
21064
21064
|
const rById = new Map(analysis.blocks.map((b) => [b.id, b.R]));
|
|
@@ -21068,18 +21068,18 @@ function layoutRbd(ast) {
|
|
|
21068
21068
|
const marks = [];
|
|
21069
21069
|
const measure = (s) => {
|
|
21070
21070
|
if (s.kind === "block") {
|
|
21071
|
-
return { s, w: blockWidth(s.label ?? s.id), h:
|
|
21071
|
+
return { s, w: blockWidth(s.label ?? s.id), h: C3.BLOCK_H, children: [] };
|
|
21072
21072
|
}
|
|
21073
21073
|
const children = s.children.map(measure);
|
|
21074
|
-
if (children.length === 0) return { s, w:
|
|
21074
|
+
if (children.length === 0) return { s, w: C3.BLOCK_MIN_W, h: C3.BLOCK_H, children };
|
|
21075
21075
|
if (s.kind === "series") {
|
|
21076
|
-
const w2 = children.reduce((acc, c) => acc + c.w, 0) +
|
|
21076
|
+
const w2 = children.reduce((acc, c) => acc + c.w, 0) + C3.H_GAP * (children.length - 1);
|
|
21077
21077
|
const h2 = Math.max(...children.map((c) => c.h));
|
|
21078
21078
|
return { s, w: w2, h: h2, children };
|
|
21079
21079
|
}
|
|
21080
21080
|
const innerW = Math.max(...children.map((c) => c.w));
|
|
21081
|
-
const w = innerW + 2 *
|
|
21082
|
-
const h = children.reduce((acc, c) => acc + c.h, 0) +
|
|
21081
|
+
const w = innerW + 2 * C3.SPLIT_STUB;
|
|
21082
|
+
const h = children.reduce((acc, c) => acc + c.h, 0) + C3.V_GAP * (children.length - 1);
|
|
21083
21083
|
return { s, w, h, children };
|
|
21084
21084
|
};
|
|
21085
21085
|
const place = (m, x, yc2) => {
|
|
@@ -21089,9 +21089,9 @@ function layoutRbd(ast) {
|
|
|
21089
21089
|
blocks.push({
|
|
21090
21090
|
block: s,
|
|
21091
21091
|
x,
|
|
21092
|
-
y: yc2 -
|
|
21092
|
+
y: yc2 - C3.BLOCK_H / 2,
|
|
21093
21093
|
width: m.w,
|
|
21094
|
-
height:
|
|
21094
|
+
height: C3.BLOCK_H,
|
|
21095
21095
|
...r7 !== void 0 ? { R: r7 } : {},
|
|
21096
21096
|
isSpof: spof.has(s.id),
|
|
21097
21097
|
critical: analysis.criticalBlock === s.id
|
|
@@ -21109,14 +21109,14 @@ function layoutRbd(ast) {
|
|
|
21109
21109
|
if (prevExit !== null) wires.push({ path: `M ${r6(prevExit)} ${r6(yc2)} L ${r6(ep2.entryX)} ${r6(yc2)}` });
|
|
21110
21110
|
prevExit = ep2.exitX;
|
|
21111
21111
|
lastExit = ep2.exitX;
|
|
21112
|
-
cursor = ep2.exitX +
|
|
21112
|
+
cursor = ep2.exitX + C3.H_GAP;
|
|
21113
21113
|
});
|
|
21114
21114
|
return { entryX: firstEntry, exitX: lastExit };
|
|
21115
21115
|
}
|
|
21116
21116
|
const innerW = Math.max(...m.children.map((c) => c.w));
|
|
21117
|
-
const splitX = x +
|
|
21118
|
-
const mergeX = x + m.w -
|
|
21119
|
-
const childBandX = x +
|
|
21117
|
+
const splitX = x + C3.NODE_R;
|
|
21118
|
+
const mergeX = x + m.w - C3.NODE_R;
|
|
21119
|
+
const childBandX = x + C3.SPLIT_STUB;
|
|
21120
21120
|
nodes.push({ kind: "split", x: splitX, y: yc2 });
|
|
21121
21121
|
nodes.push({ kind: "join", x: mergeX, y: yc2 });
|
|
21122
21122
|
let runY = yc2 - m.h / 2;
|
|
@@ -21126,7 +21126,7 @@ function layoutRbd(ast) {
|
|
|
21126
21126
|
const ep2 = place(cm, childStartX, childYc);
|
|
21127
21127
|
wires.push({ path: `M ${r6(splitX)} ${r6(yc2)} L ${r6(splitX)} ${r6(childYc)} L ${r6(ep2.entryX)} ${r6(childYc)}` });
|
|
21128
21128
|
wires.push({ path: `M ${r6(ep2.exitX)} ${r6(childYc)} L ${r6(mergeX)} ${r6(childYc)} L ${r6(mergeX)} ${r6(yc2)}` });
|
|
21129
|
-
runY += cm.h +
|
|
21129
|
+
runY += cm.h + C3.V_GAP;
|
|
21130
21130
|
}
|
|
21131
21131
|
if (s.kind === "kofn") {
|
|
21132
21132
|
marks.push({ x: mergeX + 6, y: yc2 - m.h / 2 - 6, text: `${s.k}/${s.n ?? m.children.length}` });
|
|
@@ -21134,12 +21134,12 @@ function layoutRbd(ast) {
|
|
|
21134
21134
|
return { entryX: splitX, exitX: mergeX };
|
|
21135
21135
|
};
|
|
21136
21136
|
const rootM = measure(ast.root);
|
|
21137
|
-
const headerH = (ast.title ?
|
|
21138
|
-
const yc =
|
|
21139
|
-
const originX =
|
|
21137
|
+
const headerH = (ast.title ? C3.TITLE_H : 0) + C3.HEADER_H;
|
|
21138
|
+
const yc = C3.CANVAS_PAD + headerH + rootM.h / 2;
|
|
21139
|
+
const originX = C3.CANVAS_PAD + C3.TERM_STUB;
|
|
21140
21140
|
const ep = place(rootM, originX, yc);
|
|
21141
|
-
const inX =
|
|
21142
|
-
const outX = ep.exitX +
|
|
21141
|
+
const inX = C3.CANVAS_PAD;
|
|
21142
|
+
const outX = ep.exitX + C3.TERM_STUB;
|
|
21143
21143
|
nodes.push({ kind: "in", x: inX, y: yc });
|
|
21144
21144
|
nodes.push({ kind: "out", x: outX, y: yc });
|
|
21145
21145
|
wires.push({ path: `M ${r6(inX)} ${r6(yc)} L ${r6(ep.entryX)} ${r6(yc)}` });
|
|
@@ -21149,7 +21149,7 @@ function layoutRbd(ast) {
|
|
|
21149
21149
|
for (const b of blocks) {
|
|
21150
21150
|
maxX = Math.max(maxX, b.x + b.width);
|
|
21151
21151
|
const capLines = (b.R !== void 0 ? 1 : 0) + (b.block.label && b.block.label !== b.block.id ? 1 : 0);
|
|
21152
|
-
maxY = Math.max(maxY, b.y + b.height + (capLines > 0 ?
|
|
21152
|
+
maxY = Math.max(maxY, b.y + b.height + (capLines > 0 ? C3.CAP_GAP + capLines * C3.CAP_LINE_H : 0));
|
|
21153
21153
|
}
|
|
21154
21154
|
for (const mk of marks) maxX = Math.max(maxX, mk.x + 24);
|
|
21155
21155
|
return {
|
|
@@ -21159,8 +21159,8 @@ function layoutRbd(ast) {
|
|
|
21159
21159
|
nodes,
|
|
21160
21160
|
wires,
|
|
21161
21161
|
marks,
|
|
21162
|
-
width: Math.ceil(maxX +
|
|
21163
|
-
height: Math.ceil(maxY +
|
|
21162
|
+
width: Math.ceil(maxX + C3.CANVAS_PAD),
|
|
21163
|
+
height: Math.ceil(maxY + C3.CANVAS_PAD)
|
|
21164
21164
|
};
|
|
21165
21165
|
}
|
|
21166
21166
|
function r6(n) {
|
|
@@ -21359,6 +21359,1245 @@ var rbd = {
|
|
|
21359
21359
|
}
|
|
21360
21360
|
};
|
|
21361
21361
|
|
|
21362
|
+
// src/diagrams/comparison/parser.ts
|
|
21363
|
+
var ComparisonParseError = class extends Error {
|
|
21364
|
+
constructor(message, line2) {
|
|
21365
|
+
super(line2 ? `Line ${line2}: ${message}` : message);
|
|
21366
|
+
this.line = line2;
|
|
21367
|
+
this.name = "ComparisonParseError";
|
|
21368
|
+
}
|
|
21369
|
+
line;
|
|
21370
|
+
};
|
|
21371
|
+
var MODES = [
|
|
21372
|
+
"tchart",
|
|
21373
|
+
"pros-cons",
|
|
21374
|
+
"matrix",
|
|
21375
|
+
"decision",
|
|
21376
|
+
"double-bubble"
|
|
21377
|
+
];
|
|
21378
|
+
function parseComparison(text2) {
|
|
21379
|
+
const ast = {
|
|
21380
|
+
type: "comparison",
|
|
21381
|
+
mode: "tchart",
|
|
21382
|
+
columns: [],
|
|
21383
|
+
pros: [],
|
|
21384
|
+
cons: [],
|
|
21385
|
+
options: [],
|
|
21386
|
+
criteria: [],
|
|
21387
|
+
legend: "on",
|
|
21388
|
+
warnings: []
|
|
21389
|
+
};
|
|
21390
|
+
const rawLines = text2.split(/\r?\n/);
|
|
21391
|
+
let i = 0;
|
|
21392
|
+
let explicitMode;
|
|
21393
|
+
while (i < rawLines.length) {
|
|
21394
|
+
const t = stripComment8(rawLines[i] ?? "").trim();
|
|
21395
|
+
if (t === "") {
|
|
21396
|
+
i++;
|
|
21397
|
+
continue;
|
|
21398
|
+
}
|
|
21399
|
+
const h = /^(comparison|compare|vs|tchart|t-chart|pugh|decision-matrix|decisionmatrix)\b(.*)$/i.exec(t);
|
|
21400
|
+
if (h) {
|
|
21401
|
+
const head = h[1].toLowerCase();
|
|
21402
|
+
if (head === "tchart" || head === "t-chart") explicitMode = "tchart";
|
|
21403
|
+
else if (head === "pugh" || head === "decision-matrix" || head === "decisionmatrix")
|
|
21404
|
+
explicitMode = "decision";
|
|
21405
|
+
const q = matchQuoted6(h[2].trim());
|
|
21406
|
+
if (q) ast.title = q.value;
|
|
21407
|
+
i++;
|
|
21408
|
+
}
|
|
21409
|
+
break;
|
|
21410
|
+
}
|
|
21411
|
+
let curColumn = null;
|
|
21412
|
+
let curCriterion = null;
|
|
21413
|
+
let colCount = 0;
|
|
21414
|
+
let optCount = 0;
|
|
21415
|
+
let critCount = 0;
|
|
21416
|
+
const bubble = { left: "", right: "", shared: [], leftOnly: [], rightOnly: [] };
|
|
21417
|
+
for (; i < rawLines.length; i++) {
|
|
21418
|
+
const t = stripComment8(rawLines[i] ?? "").trim();
|
|
21419
|
+
if (t === "") continue;
|
|
21420
|
+
const lineNo = i + 1;
|
|
21421
|
+
if (/^mode\s*:/i.test(t)) {
|
|
21422
|
+
const v = afterColon6(t).toLowerCase().replace(/\s+/g, "-");
|
|
21423
|
+
const m = normaliseMode(v);
|
|
21424
|
+
if (m) explicitMode = m;
|
|
21425
|
+
else ast.warnings.push(`Line ${lineNo}: unknown mode "${afterColon6(t)}" \u2014 using ${explicitMode ?? "inferred"}.`);
|
|
21426
|
+
continue;
|
|
21427
|
+
}
|
|
21428
|
+
if (/^legend\s*:/i.test(t)) {
|
|
21429
|
+
const v = afterColon6(t).toLowerCase();
|
|
21430
|
+
if (v === "on" || v === "off") ast.legend = v;
|
|
21431
|
+
continue;
|
|
21432
|
+
}
|
|
21433
|
+
if (/^baseline\s*:/i.test(t)) {
|
|
21434
|
+
const q = matchQuoted6(afterColon6(t));
|
|
21435
|
+
ast.baseline = q ? q.value : afterColon6(t);
|
|
21436
|
+
continue;
|
|
21437
|
+
}
|
|
21438
|
+
if (/^(title|subject)\s*:/i.test(t)) {
|
|
21439
|
+
const q = matchQuoted6(afterColon6(t));
|
|
21440
|
+
const val = q ? q.value : afterColon6(t);
|
|
21441
|
+
if (/^subject/i.test(t)) ast.subject = val;
|
|
21442
|
+
else if (!ast.title) ast.title = val;
|
|
21443
|
+
continue;
|
|
21444
|
+
}
|
|
21445
|
+
if (/^theme\s*:/i.test(t)) continue;
|
|
21446
|
+
if (curCriterion) {
|
|
21447
|
+
const ref = cellPrefix(t);
|
|
21448
|
+
if (ref !== null) {
|
|
21449
|
+
const valTok = t.slice(t.indexOf(":") + 1).trim();
|
|
21450
|
+
const opt = findOption(ast.options, ref);
|
|
21451
|
+
if (!opt) {
|
|
21452
|
+
ast.warnings.push(
|
|
21453
|
+
`Line ${lineNo}: "${ref}" is not a declared option \u2014 add \`option "${ref}"\` or fix the name.`
|
|
21454
|
+
);
|
|
21455
|
+
} else {
|
|
21456
|
+
curCriterion.cells[opt.id] = parseCellValue(valTok);
|
|
21457
|
+
}
|
|
21458
|
+
continue;
|
|
21459
|
+
}
|
|
21460
|
+
}
|
|
21461
|
+
const kw = /^([a-z][a-z-]*)\b/i.exec(t);
|
|
21462
|
+
const keyword = kw ? kw[1].toLowerCase() : "";
|
|
21463
|
+
const rest = kw ? t.slice(kw[0].length).trim() : t;
|
|
21464
|
+
switch (keyword) {
|
|
21465
|
+
case "column":
|
|
21466
|
+
case "col": {
|
|
21467
|
+
const label = requireLabel2(rest, keyword, lineNo);
|
|
21468
|
+
curColumn = { id: `col${++colCount}`, label, items: [] };
|
|
21469
|
+
ast.columns.push(curColumn);
|
|
21470
|
+
curCriterion = null;
|
|
21471
|
+
break;
|
|
21472
|
+
}
|
|
21473
|
+
case "item": {
|
|
21474
|
+
const label = requireLabel2(rest, keyword, lineNo);
|
|
21475
|
+
if (!curColumn) {
|
|
21476
|
+
ast.warnings.push(`Line ${lineNo}: \`item\` before any \`column\` \u2014 ignored.`);
|
|
21477
|
+
} else curColumn.items.push(label);
|
|
21478
|
+
break;
|
|
21479
|
+
}
|
|
21480
|
+
case "pro": {
|
|
21481
|
+
ast.pros.push(requireLabel2(rest, keyword, lineNo));
|
|
21482
|
+
break;
|
|
21483
|
+
}
|
|
21484
|
+
case "con": {
|
|
21485
|
+
ast.cons.push(requireLabel2(rest, keyword, lineNo));
|
|
21486
|
+
break;
|
|
21487
|
+
}
|
|
21488
|
+
case "option":
|
|
21489
|
+
case "opt": {
|
|
21490
|
+
const label = requireLabel2(rest, keyword, lineNo);
|
|
21491
|
+
ast.options.push({ id: `o${++optCount}`, label });
|
|
21492
|
+
curCriterion = null;
|
|
21493
|
+
break;
|
|
21494
|
+
}
|
|
21495
|
+
case "criterion":
|
|
21496
|
+
case "criteria":
|
|
21497
|
+
case "row": {
|
|
21498
|
+
curCriterion = parseCriterion(rest, `c${++critCount}`, keyword, lineNo, ast.options, ast.warnings, lineNo);
|
|
21499
|
+
ast.criteria.push(curCriterion);
|
|
21500
|
+
curColumn = null;
|
|
21501
|
+
break;
|
|
21502
|
+
}
|
|
21503
|
+
case "left":
|
|
21504
|
+
bubble.left = requireLabel2(rest, keyword, lineNo);
|
|
21505
|
+
break;
|
|
21506
|
+
case "right":
|
|
21507
|
+
bubble.right = requireLabel2(rest, keyword, lineNo);
|
|
21508
|
+
break;
|
|
21509
|
+
case "shared":
|
|
21510
|
+
case "both":
|
|
21511
|
+
bubble.shared.push(requireLabel2(rest, keyword, lineNo));
|
|
21512
|
+
break;
|
|
21513
|
+
case "left-only":
|
|
21514
|
+
case "leftonly":
|
|
21515
|
+
bubble.leftOnly.push(requireLabel2(rest, keyword, lineNo));
|
|
21516
|
+
break;
|
|
21517
|
+
case "right-only":
|
|
21518
|
+
case "rightonly":
|
|
21519
|
+
bubble.rightOnly.push(requireLabel2(rest, keyword, lineNo));
|
|
21520
|
+
break;
|
|
21521
|
+
default: {
|
|
21522
|
+
const bullet = /^[-*•]\s+(.*)$/.exec(t);
|
|
21523
|
+
if (bullet && curColumn) {
|
|
21524
|
+
curColumn.items.push(unquoteLoose(bullet[1].trim()));
|
|
21525
|
+
} else if (bullet) {
|
|
21526
|
+
ast.warnings.push(`Line ${lineNo}: bullet "${truncate7(t, 60)}" before any \`column\` \u2014 ignored.`);
|
|
21527
|
+
} else {
|
|
21528
|
+
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${truncate7(t, 80)}"`);
|
|
21529
|
+
}
|
|
21530
|
+
}
|
|
21531
|
+
}
|
|
21532
|
+
}
|
|
21533
|
+
if (bubble.left || bubble.right || bubble.shared.length) {
|
|
21534
|
+
ast.bubble = bubble;
|
|
21535
|
+
}
|
|
21536
|
+
ast.mode = explicitMode ?? inferMode(ast);
|
|
21537
|
+
validate4(ast);
|
|
21538
|
+
return ast;
|
|
21539
|
+
}
|
|
21540
|
+
function parseCriterion(rest, id, keyword, lineNo, options, warnings, ln) {
|
|
21541
|
+
const pipeIdx = rest.indexOf("|");
|
|
21542
|
+
let head = pipeIdx >= 0 ? rest.slice(0, pipeIdx).trim() : rest;
|
|
21543
|
+
const cells = {};
|
|
21544
|
+
let weight;
|
|
21545
|
+
const wm = /\b(?:weight|w)\s*[:=]\s*(\d+(?:\.\d+)?)/i.exec(head);
|
|
21546
|
+
if (wm) {
|
|
21547
|
+
weight = Number(wm[1]);
|
|
21548
|
+
head = head.slice(0, wm.index).trim() + head.slice(wm.index + wm[0].length).trim();
|
|
21549
|
+
head = head.trim();
|
|
21550
|
+
}
|
|
21551
|
+
const label = requireLabel2(head, keyword, lineNo);
|
|
21552
|
+
if (pipeIdx >= 0) {
|
|
21553
|
+
const parts = rest.slice(pipeIdx + 1).split("|").map((p) => p.trim());
|
|
21554
|
+
parts.forEach((p, k) => {
|
|
21555
|
+
const opt = options[k];
|
|
21556
|
+
if (!opt) {
|
|
21557
|
+
if (p) warnings.push(`Line ${ln}: pipe value "${p}" has no matching option (only ${options.length} declared).`);
|
|
21558
|
+
return;
|
|
21559
|
+
}
|
|
21560
|
+
if (p) cells[opt.id] = parseCellValue(p);
|
|
21561
|
+
});
|
|
21562
|
+
}
|
|
21563
|
+
const crit = { id, label, cells };
|
|
21564
|
+
if (weight !== void 0) crit.weight = weight;
|
|
21565
|
+
return crit;
|
|
21566
|
+
}
|
|
21567
|
+
function parseCellValue(tok) {
|
|
21568
|
+
const raw = tok;
|
|
21569
|
+
const q = matchQuoted6(tok);
|
|
21570
|
+
if (q) return { text: q.value, raw };
|
|
21571
|
+
const low = tok.toLowerCase();
|
|
21572
|
+
if (/^(yes|y|true|✓|✔|check)$/.test(low)) return { glyph: "yes", raw };
|
|
21573
|
+
if (/^(no|n|false|✗|✘|x|cross)$/.test(low)) return { glyph: "no", raw };
|
|
21574
|
+
if (/^(partial|part|~|maybe|some|half)$/.test(low)) return { glyph: "partial", raw };
|
|
21575
|
+
if (/^(na|n\/a|-|—|none)$/.test(low)) return { glyph: "na", raw };
|
|
21576
|
+
const num4 = Number(tok);
|
|
21577
|
+
if (tok !== "" && !Number.isNaN(num4)) return { score: num4, raw };
|
|
21578
|
+
return { text: tok, raw };
|
|
21579
|
+
}
|
|
21580
|
+
function inferMode(ast) {
|
|
21581
|
+
if (ast.bubble) return "double-bubble";
|
|
21582
|
+
if (ast.pros.length || ast.cons.length) return "pros-cons";
|
|
21583
|
+
if (ast.options.length || ast.criteria.length) {
|
|
21584
|
+
const anyScore = ast.criteria.some(
|
|
21585
|
+
(c) => Object.values(c.cells).some((v) => typeof v.score === "number")
|
|
21586
|
+
);
|
|
21587
|
+
const anyWeight = ast.criteria.some((c) => c.weight !== void 0);
|
|
21588
|
+
return anyScore || anyWeight || ast.baseline ? "decision" : "matrix";
|
|
21589
|
+
}
|
|
21590
|
+
return "tchart";
|
|
21591
|
+
}
|
|
21592
|
+
function normaliseMode(v) {
|
|
21593
|
+
if (v === "ychart" || v === "y-chart") return "tchart";
|
|
21594
|
+
if (v === "proscons" || v === "pro-con" || v === "pros-and-cons") return "pros-cons";
|
|
21595
|
+
if (v === "pugh" || v === "decision-matrix") return "decision";
|
|
21596
|
+
if (v === "doublebubble" || v === "double-bubble" || v === "compare-contrast") return "double-bubble";
|
|
21597
|
+
return MODES.includes(v) ? v : void 0;
|
|
21598
|
+
}
|
|
21599
|
+
function validate4(ast) {
|
|
21600
|
+
switch (ast.mode) {
|
|
21601
|
+
case "tchart": {
|
|
21602
|
+
if (ast.columns.length === 0) {
|
|
21603
|
+
throw new ComparisonParseError(
|
|
21604
|
+
`a tchart needs at least one \`column "\u2026"\` \u2014 declare the columns you want to compare.`
|
|
21605
|
+
);
|
|
21606
|
+
}
|
|
21607
|
+
break;
|
|
21608
|
+
}
|
|
21609
|
+
case "pros-cons": {
|
|
21610
|
+
if (ast.pros.length === 0 && ast.cons.length === 0) {
|
|
21611
|
+
throw new ComparisonParseError(
|
|
21612
|
+
`a pros-cons needs at least one \`pro "\u2026"\` or \`con "\u2026"\` line.`
|
|
21613
|
+
);
|
|
21614
|
+
}
|
|
21615
|
+
break;
|
|
21616
|
+
}
|
|
21617
|
+
case "matrix":
|
|
21618
|
+
case "decision": {
|
|
21619
|
+
if (ast.options.length === 0) {
|
|
21620
|
+
throw new ComparisonParseError(
|
|
21621
|
+
`a ${ast.mode} matrix needs at least one \`option "\u2026"\` \u2014 declare the options (columns) you are comparing.`
|
|
21622
|
+
);
|
|
21623
|
+
}
|
|
21624
|
+
if (ast.criteria.length === 0) {
|
|
21625
|
+
throw new ComparisonParseError(
|
|
21626
|
+
`a ${ast.mode} matrix needs at least one \`criterion "\u2026"\` \u2014 declare the criteria (rows) to compare on.`
|
|
21627
|
+
);
|
|
21628
|
+
}
|
|
21629
|
+
if (ast.mode === "decision") {
|
|
21630
|
+
const anyScore = ast.criteria.some(
|
|
21631
|
+
(c) => Object.values(c.cells).some((v) => typeof v.score === "number")
|
|
21632
|
+
);
|
|
21633
|
+
if (!anyScore) {
|
|
21634
|
+
ast.warnings.push(
|
|
21635
|
+
`decision mode but no numeric scores found \u2014 add \`Option: <number>\` lines under each criterion, or switch to \`mode: matrix\`.`
|
|
21636
|
+
);
|
|
21637
|
+
}
|
|
21638
|
+
if (ast.baseline) {
|
|
21639
|
+
const ok = ast.options.some((o) => o.label === ast.baseline || o.id === ast.baseline);
|
|
21640
|
+
if (!ok) {
|
|
21641
|
+
ast.warnings.push(
|
|
21642
|
+
`baseline "${ast.baseline}" is not one of the declared options \u2014 ignored.`
|
|
21643
|
+
);
|
|
21644
|
+
delete ast.baseline;
|
|
21645
|
+
}
|
|
21646
|
+
}
|
|
21647
|
+
}
|
|
21648
|
+
break;
|
|
21649
|
+
}
|
|
21650
|
+
case "double-bubble": {
|
|
21651
|
+
if (!ast.bubble || !ast.bubble.left || !ast.bubble.right) {
|
|
21652
|
+
throw new ComparisonParseError(
|
|
21653
|
+
`a double-bubble needs both a \`left "\u2026"\` and a \`right "\u2026"\` centre to compare.`
|
|
21654
|
+
);
|
|
21655
|
+
}
|
|
21656
|
+
break;
|
|
21657
|
+
}
|
|
21658
|
+
}
|
|
21659
|
+
}
|
|
21660
|
+
var RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
21661
|
+
"comparison",
|
|
21662
|
+
"compare",
|
|
21663
|
+
"vs",
|
|
21664
|
+
"tchart",
|
|
21665
|
+
"t-chart",
|
|
21666
|
+
"pugh",
|
|
21667
|
+
"decision-matrix",
|
|
21668
|
+
"decisionmatrix",
|
|
21669
|
+
"mode",
|
|
21670
|
+
"legend",
|
|
21671
|
+
"baseline",
|
|
21672
|
+
"title",
|
|
21673
|
+
"subject",
|
|
21674
|
+
"theme",
|
|
21675
|
+
"column",
|
|
21676
|
+
"col",
|
|
21677
|
+
"item",
|
|
21678
|
+
"pro",
|
|
21679
|
+
"con",
|
|
21680
|
+
"option",
|
|
21681
|
+
"opt",
|
|
21682
|
+
"criterion",
|
|
21683
|
+
"criteria",
|
|
21684
|
+
"row",
|
|
21685
|
+
"left",
|
|
21686
|
+
"right",
|
|
21687
|
+
"shared",
|
|
21688
|
+
"both",
|
|
21689
|
+
"left-only",
|
|
21690
|
+
"leftonly",
|
|
21691
|
+
"right-only",
|
|
21692
|
+
"rightonly"
|
|
21693
|
+
]);
|
|
21694
|
+
function cellPrefix(t) {
|
|
21695
|
+
const ci = t.indexOf(":");
|
|
21696
|
+
if (ci <= 0) return null;
|
|
21697
|
+
const pre = t.slice(0, ci).trim();
|
|
21698
|
+
if (!pre) return null;
|
|
21699
|
+
const firstWord2 = (pre.split(/\s+/)[0] ?? "").toLowerCase();
|
|
21700
|
+
if (RESERVED_KEYWORDS.has(firstWord2)) return null;
|
|
21701
|
+
return pre;
|
|
21702
|
+
}
|
|
21703
|
+
function findOption(options, ref) {
|
|
21704
|
+
const r7 = ref.toLowerCase();
|
|
21705
|
+
return options.find((o) => o.label.toLowerCase() === r7 || o.id.toLowerCase() === r7);
|
|
21706
|
+
}
|
|
21707
|
+
function requireLabel2(s, keyword, lineNo) {
|
|
21708
|
+
const q = matchQuoted6(s);
|
|
21709
|
+
if (q) return q.value;
|
|
21710
|
+
const bare = s.trim();
|
|
21711
|
+
if (!bare) throw new ComparisonParseError(`\`${keyword}\` needs a label`, lineNo);
|
|
21712
|
+
return bare;
|
|
21713
|
+
}
|
|
21714
|
+
function unquoteLoose(s) {
|
|
21715
|
+
const q = matchQuoted6(s);
|
|
21716
|
+
return q && q.length === s.length ? q.value : s;
|
|
21717
|
+
}
|
|
21718
|
+
function matchQuoted6(s) {
|
|
21719
|
+
if (!s) return void 0;
|
|
21720
|
+
const open = s[0];
|
|
21721
|
+
if (open !== '"' && open !== "\u300C" && open !== "\u201C" && open !== "\u300E") return void 0;
|
|
21722
|
+
const close = closingQuote6(open);
|
|
21723
|
+
const end = s.indexOf(close, 1);
|
|
21724
|
+
if (end < 0) return void 0;
|
|
21725
|
+
return { value: s.slice(1, end), length: end + 1 };
|
|
21726
|
+
}
|
|
21727
|
+
function closingQuote6(open) {
|
|
21728
|
+
return open === "\u300C" ? "\u300D" : open === "\u300E" ? "\u300F" : open === "\u201C" ? "\u201D" : '"';
|
|
21729
|
+
}
|
|
21730
|
+
function afterColon6(s) {
|
|
21731
|
+
const i = s.indexOf(":");
|
|
21732
|
+
return i < 0 ? "" : s.slice(i + 1).trim();
|
|
21733
|
+
}
|
|
21734
|
+
function stripComment8(line2) {
|
|
21735
|
+
let inQ = false;
|
|
21736
|
+
let qc = "";
|
|
21737
|
+
for (let i = 0; i < line2.length; i++) {
|
|
21738
|
+
const ch = line2[i];
|
|
21739
|
+
if (inQ) {
|
|
21740
|
+
if (ch === qc) inQ = false;
|
|
21741
|
+
continue;
|
|
21742
|
+
}
|
|
21743
|
+
if (ch === '"' || ch === "\u300C" || ch === "\u201C" || ch === "\u300E") {
|
|
21744
|
+
inQ = true;
|
|
21745
|
+
qc = closingQuote6(ch);
|
|
21746
|
+
continue;
|
|
21747
|
+
}
|
|
21748
|
+
if (ch === "#") return line2.slice(0, i);
|
|
21749
|
+
if (ch === "/" && line2[i + 1] === "/") return line2.slice(0, i);
|
|
21750
|
+
}
|
|
21751
|
+
return line2;
|
|
21752
|
+
}
|
|
21753
|
+
function truncate7(s, n) {
|
|
21754
|
+
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
21755
|
+
}
|
|
21756
|
+
|
|
21757
|
+
// src/diagrams/comparison/compute.ts
|
|
21758
|
+
function scoreOf(v) {
|
|
21759
|
+
if (!v) return 0;
|
|
21760
|
+
if (typeof v.score === "number") return v.score;
|
|
21761
|
+
if (v.glyph === "yes") return 1;
|
|
21762
|
+
if (v.glyph === "partial") return 0.5;
|
|
21763
|
+
if (v.glyph === "no" || v.glyph === "na") return 0;
|
|
21764
|
+
return 0;
|
|
21765
|
+
}
|
|
21766
|
+
function computeDecision(ast) {
|
|
21767
|
+
const totals = {};
|
|
21768
|
+
const totalWeight = ast.criteria.reduce((s, c) => s + (c.weight ?? 1), 0);
|
|
21769
|
+
for (const opt of ast.options) {
|
|
21770
|
+
let sum = 0;
|
|
21771
|
+
for (const crit of ast.criteria) {
|
|
21772
|
+
const w = crit.weight ?? 1;
|
|
21773
|
+
sum += w * scoreOf(crit.cells[opt.id]);
|
|
21774
|
+
}
|
|
21775
|
+
totals[opt.id] = round7(sum);
|
|
21776
|
+
}
|
|
21777
|
+
const sorted = [...ast.options].sort((a, b) => totals[b.id] - totals[a.id]);
|
|
21778
|
+
const ranks = {};
|
|
21779
|
+
let lastTotal = Number.POSITIVE_INFINITY;
|
|
21780
|
+
let lastRank = 0;
|
|
21781
|
+
sorted.forEach((opt, i) => {
|
|
21782
|
+
const t = totals[opt.id];
|
|
21783
|
+
if (t < lastTotal) {
|
|
21784
|
+
lastRank = i + 1;
|
|
21785
|
+
lastTotal = t;
|
|
21786
|
+
}
|
|
21787
|
+
ranks[opt.id] = lastRank;
|
|
21788
|
+
});
|
|
21789
|
+
const winnerId = sorted.length ? sorted[0].id : "";
|
|
21790
|
+
const maxTotal = sorted.length ? totals[winnerId] : 0;
|
|
21791
|
+
const result = { totals, ranks, winnerId, maxTotal, totalWeight };
|
|
21792
|
+
if (ast.baseline) {
|
|
21793
|
+
const base = ast.options.find(
|
|
21794
|
+
(o) => o.id === ast.baseline || o.label === ast.baseline
|
|
21795
|
+
);
|
|
21796
|
+
if (base) {
|
|
21797
|
+
const baseTotal = totals[base.id];
|
|
21798
|
+
const deltas = {};
|
|
21799
|
+
for (const opt of ast.options) deltas[opt.id] = round7(totals[opt.id] - baseTotal);
|
|
21800
|
+
result.deltas = deltas;
|
|
21801
|
+
}
|
|
21802
|
+
}
|
|
21803
|
+
return result;
|
|
21804
|
+
}
|
|
21805
|
+
function decisionCaption(ast, r7) {
|
|
21806
|
+
if (!ast.options.length) return "Decision matrix: no options.";
|
|
21807
|
+
const winner = ast.options.find((o) => o.id === r7.winnerId);
|
|
21808
|
+
const tiedIds = ast.options.filter((o) => r7.ranks[o.id] === 1).map((o) => o.id);
|
|
21809
|
+
if (tiedIds.length > 1) {
|
|
21810
|
+
const names = tiedIds.map((id) => ast.options.find((o) => o.id === id)?.label).filter(Boolean).map((n) => `"${n}"`).join(" / ");
|
|
21811
|
+
return `Tie at the top: ${names} (weighted score ${fmt3(r7.maxTotal)} of ${ast.options.length} options).`;
|
|
21812
|
+
}
|
|
21813
|
+
return `Winner: "${winner?.label ?? r7.winnerId}" \u2014 weighted score ${fmt3(
|
|
21814
|
+
r7.maxTotal
|
|
21815
|
+
)}, highest of ${ast.options.length} option${ast.options.length === 1 ? "" : "s"}.`;
|
|
21816
|
+
}
|
|
21817
|
+
function round7(n) {
|
|
21818
|
+
return Math.round(n * 1e3) / 1e3;
|
|
21819
|
+
}
|
|
21820
|
+
function fmt3(n) {
|
|
21821
|
+
const r7 = Math.round(n * 100) / 100;
|
|
21822
|
+
return Number.isInteger(r7) ? String(r7) : String(r7);
|
|
21823
|
+
}
|
|
21824
|
+
|
|
21825
|
+
// src/diagrams/comparison/layout.ts
|
|
21826
|
+
var COMPARISON_CONST = {
|
|
21827
|
+
PAGE_PAD: 24,
|
|
21828
|
+
TITLE_H: 40,
|
|
21829
|
+
CAPTION_H: 30,
|
|
21830
|
+
HEADER_MIN_H: 36,
|
|
21831
|
+
ROW_MIN_H: 34,
|
|
21832
|
+
CELL_PAD_X: 12,
|
|
21833
|
+
CELL_PAD_Y: 8,
|
|
21834
|
+
LINE_H: 16,
|
|
21835
|
+
COL_MIN_W: 140,
|
|
21836
|
+
COL_MAX_W: 264,
|
|
21837
|
+
MARK_COL_MIN_W: 92,
|
|
21838
|
+
MARK_COL_MAX_W: 190,
|
|
21839
|
+
ROWHEAD_MIN_W: 120,
|
|
21840
|
+
ROWHEAD_MAX_W: 230,
|
|
21841
|
+
FONT: 12,
|
|
21842
|
+
HEADER_FONT: 13,
|
|
21843
|
+
// double-bubble — larger, rounder bubbles with generous spacing
|
|
21844
|
+
CENTER_RX: 70,
|
|
21845
|
+
CENTER_RY: 58,
|
|
21846
|
+
SHARED_RX: 60,
|
|
21847
|
+
SHARED_RY: 46,
|
|
21848
|
+
UNIQUE_RX: 70,
|
|
21849
|
+
UNIQUE_RY: 46,
|
|
21850
|
+
BUBBLE_VGAP: 22,
|
|
21851
|
+
// tchart
|
|
21852
|
+
TCHART_COL_GAP: 22,
|
|
21853
|
+
// pros-cons
|
|
21854
|
+
PC_COL_W: 322,
|
|
21855
|
+
PC_COL_GAP: 30,
|
|
21856
|
+
PC_PILL_H: 40,
|
|
21857
|
+
PC_ROW_MIN_H: 40,
|
|
21858
|
+
PC_BADGE_R: 11
|
|
21859
|
+
};
|
|
21860
|
+
function charW(ch, fontSize) {
|
|
21861
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
21862
|
+
if (cp >= 11904 && cp <= 40959) return fontSize;
|
|
21863
|
+
if (cp >= 44032 && cp <= 55203) return fontSize;
|
|
21864
|
+
if (cp >= 65280 && cp <= 65519) return fontSize;
|
|
21865
|
+
if (ch === " ") return fontSize * 0.3;
|
|
21866
|
+
if (/[ilj.,:'!|]/.test(ch)) return fontSize * 0.3;
|
|
21867
|
+
if (/[mwMW]/.test(ch)) return fontSize * 0.85;
|
|
21868
|
+
return fontSize * 0.56;
|
|
21869
|
+
}
|
|
21870
|
+
function measureText(s, fontSize) {
|
|
21871
|
+
let w = 0;
|
|
21872
|
+
for (const ch of s) w += charW(ch, fontSize);
|
|
21873
|
+
return w;
|
|
21874
|
+
}
|
|
21875
|
+
function wrapToWidth(s, maxW, fontSize) {
|
|
21876
|
+
if (!s) return [""];
|
|
21877
|
+
const hasSpaces = /\s/.test(s.trim());
|
|
21878
|
+
const tokens = hasSpaces ? s.split(/\s+/).filter(Boolean) : [...s];
|
|
21879
|
+
const sep = hasSpaces ? " " : "";
|
|
21880
|
+
const lines = [];
|
|
21881
|
+
let cur = "";
|
|
21882
|
+
for (const tok of tokens) {
|
|
21883
|
+
const trial = cur ? cur + sep + tok : tok;
|
|
21884
|
+
if (cur && measureText(trial, fontSize) > maxW) {
|
|
21885
|
+
lines.push(cur);
|
|
21886
|
+
cur = tok;
|
|
21887
|
+
} else {
|
|
21888
|
+
cur = trial;
|
|
21889
|
+
}
|
|
21890
|
+
}
|
|
21891
|
+
if (cur) lines.push(cur);
|
|
21892
|
+
return lines.length ? lines : [""];
|
|
21893
|
+
}
|
|
21894
|
+
var C2 = COMPARISON_CONST;
|
|
21895
|
+
function layoutComparison(ast) {
|
|
21896
|
+
switch (ast.mode) {
|
|
21897
|
+
case "tchart":
|
|
21898
|
+
return layoutColumns(ast);
|
|
21899
|
+
case "pros-cons":
|
|
21900
|
+
return layoutProsCons(ast);
|
|
21901
|
+
case "matrix":
|
|
21902
|
+
case "decision":
|
|
21903
|
+
return layoutGrid(ast);
|
|
21904
|
+
case "double-bubble":
|
|
21905
|
+
return layoutDoubleBubble(ast);
|
|
21906
|
+
}
|
|
21907
|
+
}
|
|
21908
|
+
function frame(ast) {
|
|
21909
|
+
const pad = C2.PAGE_PAD;
|
|
21910
|
+
const top = pad + (ast.title ? C2.TITLE_H : 0);
|
|
21911
|
+
return { top, pad };
|
|
21912
|
+
}
|
|
21913
|
+
function layoutColumns(ast) {
|
|
21914
|
+
const { top, pad } = frame(ast);
|
|
21915
|
+
const cells = [];
|
|
21916
|
+
const connectors = [];
|
|
21917
|
+
const frames = [];
|
|
21918
|
+
const gap = C2.TCHART_COL_GAP;
|
|
21919
|
+
const headGap = 10;
|
|
21920
|
+
const cardPadBottom = 12;
|
|
21921
|
+
const textLeft = 16;
|
|
21922
|
+
const colWidths = ast.columns.map((col) => {
|
|
21923
|
+
const headW = measureText(col.label, C2.HEADER_FONT) + 2 * C2.CELL_PAD_X;
|
|
21924
|
+
const itemW = Math.max(
|
|
21925
|
+
0,
|
|
21926
|
+
...col.items.map((it) => measureText(it, C2.FONT) + textLeft + 16)
|
|
21927
|
+
);
|
|
21928
|
+
return clamp(Math.max(headW, itemW, C2.COL_MIN_W), C2.COL_MIN_W, C2.COL_MAX_W);
|
|
21929
|
+
});
|
|
21930
|
+
const headerLines = ast.columns.map(
|
|
21931
|
+
(col, i) => wrapToWidth(col.label, colWidths[i] - 2 * C2.CELL_PAD_X, C2.HEADER_FONT)
|
|
21932
|
+
);
|
|
21933
|
+
const headerH = Math.max(
|
|
21934
|
+
C2.HEADER_MIN_H,
|
|
21935
|
+
...headerLines.map((l) => l.length * C2.LINE_H + 2 * C2.CELL_PAD_Y)
|
|
21936
|
+
);
|
|
21937
|
+
const colRows = ast.columns.map(
|
|
21938
|
+
(col, i) => col.items.map((it) => {
|
|
21939
|
+
const lines = wrapToWidth(it, colWidths[i] - textLeft - 14, C2.FONT);
|
|
21940
|
+
return { lines, h: Math.max(C2.ROW_MIN_H, lines.length * C2.LINE_H + 2 * C2.CELL_PAD_Y) };
|
|
21941
|
+
})
|
|
21942
|
+
);
|
|
21943
|
+
const itemsTop = top + headerH + headGap;
|
|
21944
|
+
const naturalH = colRows.map(
|
|
21945
|
+
(rows) => headerH + headGap + rows.reduce((s, r7) => s + r7.h, 0) + cardPadBottom
|
|
21946
|
+
);
|
|
21947
|
+
const cardH = Math.max(headerH + 44, ...naturalH);
|
|
21948
|
+
let x = pad;
|
|
21949
|
+
ast.columns.forEach((_col, i) => {
|
|
21950
|
+
const w = colWidths[i];
|
|
21951
|
+
frames.push({ x, y: top, w, h: cardH, rx: 12, variant: "card" });
|
|
21952
|
+
cells.push({
|
|
21953
|
+
x,
|
|
21954
|
+
y: top,
|
|
21955
|
+
w,
|
|
21956
|
+
h: headerH,
|
|
21957
|
+
lines: headerLines[i],
|
|
21958
|
+
variant: "colHeader",
|
|
21959
|
+
align: "middle",
|
|
21960
|
+
bold: true,
|
|
21961
|
+
paletteIndex: i,
|
|
21962
|
+
roundedTop: true
|
|
21963
|
+
});
|
|
21964
|
+
let cy = itemsTop;
|
|
21965
|
+
colRows[i].forEach((r7, k) => {
|
|
21966
|
+
cells.push({ x, y: cy, w, h: r7.h, lines: r7.lines, variant: "body", align: "start", bare: true });
|
|
21967
|
+
if (k < colRows[i].length - 1) {
|
|
21968
|
+
connectors.push({ x1: x + 14, y1: cy + r7.h, x2: x + w - 14, y2: cy + r7.h, light: true });
|
|
21969
|
+
}
|
|
21970
|
+
cy += r7.h;
|
|
21971
|
+
});
|
|
21972
|
+
x += w + gap;
|
|
21973
|
+
});
|
|
21974
|
+
const totalW = x - pad - gap;
|
|
21975
|
+
return {
|
|
21976
|
+
ast,
|
|
21977
|
+
mode: ast.mode,
|
|
21978
|
+
cells,
|
|
21979
|
+
ellipses: [],
|
|
21980
|
+
connectors,
|
|
21981
|
+
frames,
|
|
21982
|
+
width: Math.ceil(totalW + 2 * pad),
|
|
21983
|
+
height: Math.ceil(top + cardH + pad)
|
|
21984
|
+
};
|
|
21985
|
+
}
|
|
21986
|
+
function layoutProsCons(ast) {
|
|
21987
|
+
const { top, pad } = frame(ast);
|
|
21988
|
+
const cells = [];
|
|
21989
|
+
const connectors = [];
|
|
21990
|
+
const colW = C2.PC_COL_W;
|
|
21991
|
+
const leftX = pad;
|
|
21992
|
+
const rightX = pad + colW + C2.PC_COL_GAP;
|
|
21993
|
+
const badgeRoom = 2 * C2.PC_BADGE_R + 14;
|
|
21994
|
+
const pillH = C2.PC_PILL_H;
|
|
21995
|
+
const pill = (label, colX, kind) => {
|
|
21996
|
+
const pw = clamp(measureText(label, C2.HEADER_FONT) + badgeRoom + 30, 130, colW);
|
|
21997
|
+
cells.push({
|
|
21998
|
+
x: colX + (colW - pw) / 2,
|
|
21999
|
+
y: top,
|
|
22000
|
+
w: pw,
|
|
22001
|
+
h: pillH,
|
|
22002
|
+
lines: [label],
|
|
22003
|
+
variant: kind === "pro" ? "pillPos" : "pillNeg",
|
|
22004
|
+
align: "middle",
|
|
22005
|
+
bold: true,
|
|
22006
|
+
rx: pillH / 2,
|
|
22007
|
+
badge: { glyph: kind === "pro" ? "yes" : "no", tone: kind === "pro" ? "pos" : "neg" }
|
|
22008
|
+
});
|
|
22009
|
+
};
|
|
22010
|
+
pill("PROS", leftX, "pro");
|
|
22011
|
+
pill("CONS", rightX, "con");
|
|
22012
|
+
const itemsTop = top + pillH + 14;
|
|
22013
|
+
const itemCell = (txt, x, y, kind) => {
|
|
22014
|
+
const lines = wrapToWidth(txt, colW - badgeRoom - 14, C2.FONT);
|
|
22015
|
+
const h = Math.max(C2.PC_ROW_MIN_H, lines.length * C2.LINE_H + 2 * C2.CELL_PAD_Y);
|
|
22016
|
+
return {
|
|
22017
|
+
x,
|
|
22018
|
+
y,
|
|
22019
|
+
w: colW,
|
|
22020
|
+
h,
|
|
22021
|
+
lines,
|
|
22022
|
+
variant: kind,
|
|
22023
|
+
align: "start",
|
|
22024
|
+
bare: true,
|
|
22025
|
+
badge: { glyph: kind === "pro" ? "yes" : "no", tone: kind === "pro" ? "pos" : "neg" }
|
|
22026
|
+
};
|
|
22027
|
+
};
|
|
22028
|
+
let ly = itemsTop;
|
|
22029
|
+
for (const p of ast.pros) {
|
|
22030
|
+
const cell = itemCell(p, leftX, ly, "pro");
|
|
22031
|
+
cells.push(cell);
|
|
22032
|
+
ly += cell.h;
|
|
22033
|
+
}
|
|
22034
|
+
let ry = itemsTop;
|
|
22035
|
+
for (const c of ast.cons) {
|
|
22036
|
+
const cell = itemCell(c, rightX, ry, "con");
|
|
22037
|
+
cells.push(cell);
|
|
22038
|
+
ry += cell.h;
|
|
22039
|
+
}
|
|
22040
|
+
const bottom = Math.max(ly, ry);
|
|
22041
|
+
const dx = leftX + colW + C2.PC_COL_GAP / 2;
|
|
22042
|
+
connectors.push({ x1: dx, y1: itemsTop, x2: dx, y2: bottom - 6 });
|
|
22043
|
+
return {
|
|
22044
|
+
ast,
|
|
22045
|
+
mode: ast.mode,
|
|
22046
|
+
cells,
|
|
22047
|
+
ellipses: [],
|
|
22048
|
+
connectors,
|
|
22049
|
+
width: colW * 2 + C2.PC_COL_GAP + 2 * pad,
|
|
22050
|
+
height: Math.ceil(bottom + pad)
|
|
22051
|
+
};
|
|
22052
|
+
}
|
|
22053
|
+
function layoutGrid(ast) {
|
|
22054
|
+
const { top, pad } = frame(ast);
|
|
22055
|
+
const cells = [];
|
|
22056
|
+
const isDecision = ast.mode === "decision";
|
|
22057
|
+
const decision = isDecision ? computeDecision(ast) : void 0;
|
|
22058
|
+
const cellText = (oid, cid) => {
|
|
22059
|
+
const v = ast.criteria.find((c) => c.id === cid).cells[oid];
|
|
22060
|
+
if (!v) return { lines: [], variant: "body" };
|
|
22061
|
+
if (v.glyph) {
|
|
22062
|
+
const variant = v.glyph === "yes" ? "pos" : v.glyph === "no" ? "neg" : v.glyph === "partial" ? "warn" : "body";
|
|
22063
|
+
return { lines: v.glyph === "na" ? ["\u2014"] : [], glyph: v.glyph === "na" ? void 0 : v.glyph, variant };
|
|
22064
|
+
}
|
|
22065
|
+
if (typeof v.score === "number") return { lines: [fmt3(v.score)], variant: "body" };
|
|
22066
|
+
return { lines: v.text ? [v.text] : [], variant: "body" };
|
|
22067
|
+
};
|
|
22068
|
+
const rowHeadW = clamp(
|
|
22069
|
+
Math.max(
|
|
22070
|
+
C2.ROWHEAD_MIN_W,
|
|
22071
|
+
...ast.criteria.map((c) => measureText(c.label, C2.FONT) + 2 * C2.CELL_PAD_X + (isDecision ? 30 : 0)),
|
|
22072
|
+
measureText(isDecision ? "Weighted total" : "", C2.FONT) + 2 * C2.CELL_PAD_X
|
|
22073
|
+
),
|
|
22074
|
+
C2.ROWHEAD_MIN_W,
|
|
22075
|
+
C2.ROWHEAD_MAX_W
|
|
22076
|
+
);
|
|
22077
|
+
const optW = ast.options.map((o) => {
|
|
22078
|
+
const headW = measureText(o.label, C2.HEADER_FONT) + 2 * C2.CELL_PAD_X;
|
|
22079
|
+
const bodyW = Math.max(
|
|
22080
|
+
0,
|
|
22081
|
+
...ast.criteria.map((c) => {
|
|
22082
|
+
const ct = cellText(o.id, c.id);
|
|
22083
|
+
return Math.max(0, ...ct.lines.map((l) => measureText(l, C2.FONT))) + 2 * C2.CELL_PAD_X;
|
|
22084
|
+
})
|
|
22085
|
+
);
|
|
22086
|
+
return clamp(Math.max(headW, bodyW, C2.MARK_COL_MIN_W), C2.MARK_COL_MIN_W, C2.MARK_COL_MAX_W);
|
|
22087
|
+
});
|
|
22088
|
+
const headLines = ast.options.map(
|
|
22089
|
+
(o, i) => wrapToWidth(o.label, optW[i] - 2 * C2.CELL_PAD_X, C2.HEADER_FONT)
|
|
22090
|
+
);
|
|
22091
|
+
const headerH = Math.max(C2.HEADER_MIN_H, ...headLines.map((l) => l.length * C2.LINE_H + 2 * C2.CELL_PAD_Y));
|
|
22092
|
+
cells.push({
|
|
22093
|
+
x: pad,
|
|
22094
|
+
y: top,
|
|
22095
|
+
w: rowHeadW,
|
|
22096
|
+
h: headerH,
|
|
22097
|
+
lines: ast.title ? [] : [],
|
|
22098
|
+
variant: "corner",
|
|
22099
|
+
align: "start"
|
|
22100
|
+
});
|
|
22101
|
+
let hx = pad + rowHeadW;
|
|
22102
|
+
const colX = [];
|
|
22103
|
+
ast.options.forEach((o, i) => {
|
|
22104
|
+
colX.push(hx);
|
|
22105
|
+
const isBase = isDecision && ast.baseline && (o.label === ast.baseline || o.id === ast.baseline);
|
|
22106
|
+
cells.push({
|
|
22107
|
+
x: hx,
|
|
22108
|
+
y: top,
|
|
22109
|
+
w: optW[i],
|
|
22110
|
+
h: headerH,
|
|
22111
|
+
lines: headLines[i],
|
|
22112
|
+
variant: isBase ? "baseline" : "colHeader",
|
|
22113
|
+
align: "middle",
|
|
22114
|
+
bold: true,
|
|
22115
|
+
tag: isBase ? "datum" : void 0
|
|
22116
|
+
});
|
|
22117
|
+
hx += optW[i];
|
|
22118
|
+
});
|
|
22119
|
+
const gridW = rowHeadW + optW.reduce((a, b) => a + b, 0);
|
|
22120
|
+
let ry = top + headerH;
|
|
22121
|
+
for (const crit of ast.criteria) {
|
|
22122
|
+
const labelLines = wrapToWidth(crit.label, rowHeadW - 2 * C2.CELL_PAD_X - (isDecision ? 26 : 0), C2.FONT);
|
|
22123
|
+
const bodyLineCounts = ast.options.map((o) => {
|
|
22124
|
+
const ct = cellText(o.id, crit.id);
|
|
22125
|
+
const wrapped = ct.lines.flatMap(
|
|
22126
|
+
(l, idx) => idx === 0 ? wrapToWidth(l, optW[ast.options.indexOf(o)] - 2 * C2.CELL_PAD_X, C2.FONT) : [l]
|
|
22127
|
+
);
|
|
22128
|
+
return wrapped.length;
|
|
22129
|
+
});
|
|
22130
|
+
const rowH = Math.max(
|
|
22131
|
+
C2.ROW_MIN_H,
|
|
22132
|
+
labelLines.length * C2.LINE_H + 2 * C2.CELL_PAD_Y,
|
|
22133
|
+
...bodyLineCounts.map((n) => n * C2.LINE_H + 2 * C2.CELL_PAD_Y)
|
|
22134
|
+
);
|
|
22135
|
+
cells.push({
|
|
22136
|
+
x: pad,
|
|
22137
|
+
y: ry,
|
|
22138
|
+
w: rowHeadW,
|
|
22139
|
+
h: rowH,
|
|
22140
|
+
lines: labelLines,
|
|
22141
|
+
variant: "rowHeader",
|
|
22142
|
+
align: "start",
|
|
22143
|
+
tag: isDecision ? `\xD7${fmt3(crit.weight ?? 1)}` : void 0
|
|
22144
|
+
});
|
|
22145
|
+
ast.options.forEach((o, i) => {
|
|
22146
|
+
const ct = cellText(o.id, crit.id);
|
|
22147
|
+
const wrapped = ct.lines.length ? wrapToWidth(ct.lines[0], optW[i] - 2 * C2.CELL_PAD_X, C2.FONT) : [];
|
|
22148
|
+
cells.push({
|
|
22149
|
+
x: colX[i],
|
|
22150
|
+
y: ry,
|
|
22151
|
+
w: optW[i],
|
|
22152
|
+
h: rowH,
|
|
22153
|
+
lines: wrapped,
|
|
22154
|
+
variant: ct.variant,
|
|
22155
|
+
glyph: ct.glyph,
|
|
22156
|
+
align: "middle"
|
|
22157
|
+
});
|
|
22158
|
+
});
|
|
22159
|
+
ry += rowH;
|
|
22160
|
+
}
|
|
22161
|
+
let caption;
|
|
22162
|
+
if (isDecision && decision) {
|
|
22163
|
+
const totalH = C2.ROW_MIN_H;
|
|
22164
|
+
cells.push({
|
|
22165
|
+
x: pad,
|
|
22166
|
+
y: ry,
|
|
22167
|
+
w: rowHeadW,
|
|
22168
|
+
h: totalH,
|
|
22169
|
+
lines: ["Weighted total"],
|
|
22170
|
+
variant: "rowHeader",
|
|
22171
|
+
align: "start",
|
|
22172
|
+
bold: true
|
|
22173
|
+
});
|
|
22174
|
+
ast.options.forEach((o, i) => {
|
|
22175
|
+
const isWin = decision.ranks[o.id] === 1;
|
|
22176
|
+
cells.push({
|
|
22177
|
+
x: colX[i],
|
|
22178
|
+
y: ry,
|
|
22179
|
+
w: optW[i],
|
|
22180
|
+
h: totalH,
|
|
22181
|
+
lines: [fmt3(decision.totals[o.id])],
|
|
22182
|
+
variant: isWin ? "winner" : "total",
|
|
22183
|
+
align: "middle",
|
|
22184
|
+
bold: true,
|
|
22185
|
+
tag: `#${decision.ranks[o.id]}`
|
|
22186
|
+
});
|
|
22187
|
+
});
|
|
22188
|
+
ry += totalH;
|
|
22189
|
+
if (decision.deltas) {
|
|
22190
|
+
const dH = C2.ROW_MIN_H;
|
|
22191
|
+
cells.push({
|
|
22192
|
+
x: pad,
|
|
22193
|
+
y: ry,
|
|
22194
|
+
w: rowHeadW,
|
|
22195
|
+
h: dH,
|
|
22196
|
+
lines: ["vs datum"],
|
|
22197
|
+
variant: "rowHeader",
|
|
22198
|
+
align: "start"
|
|
22199
|
+
});
|
|
22200
|
+
ast.options.forEach((o, i) => {
|
|
22201
|
+
const d = decision.deltas[o.id];
|
|
22202
|
+
const s = d > 0 ? `+${fmt3(d)}` : fmt3(d);
|
|
22203
|
+
cells.push({
|
|
22204
|
+
x: colX[i],
|
|
22205
|
+
y: ry,
|
|
22206
|
+
w: optW[i],
|
|
22207
|
+
h: dH,
|
|
22208
|
+
lines: [s],
|
|
22209
|
+
variant: d > 0 ? "pos" : d < 0 ? "neg" : "body",
|
|
22210
|
+
align: "middle"
|
|
22211
|
+
});
|
|
22212
|
+
});
|
|
22213
|
+
ry += dH;
|
|
22214
|
+
}
|
|
22215
|
+
caption = decisionCaption(ast, decision);
|
|
22216
|
+
}
|
|
22217
|
+
const captionH = caption ? C2.CAPTION_H : 0;
|
|
22218
|
+
const result = {
|
|
22219
|
+
ast,
|
|
22220
|
+
mode: ast.mode,
|
|
22221
|
+
cells,
|
|
22222
|
+
ellipses: [],
|
|
22223
|
+
connectors: [],
|
|
22224
|
+
width: gridW + 2 * pad,
|
|
22225
|
+
height: ry + captionH + pad
|
|
22226
|
+
};
|
|
22227
|
+
if (decision) result.decision = decision;
|
|
22228
|
+
if (caption) result.caption = caption;
|
|
22229
|
+
return result;
|
|
22230
|
+
}
|
|
22231
|
+
function layoutDoubleBubble(ast) {
|
|
22232
|
+
const { top, pad } = frame(ast);
|
|
22233
|
+
const b = ast.bubble;
|
|
22234
|
+
const rawE = [];
|
|
22235
|
+
const rawC = [];
|
|
22236
|
+
const dxCS = C2.CENTER_RX + 72 + C2.SHARED_RX;
|
|
22237
|
+
const lcx = -dxCS;
|
|
22238
|
+
const rcx = dxCS;
|
|
22239
|
+
rawE.push({ cx: lcx, cy: 0, rx: C2.CENTER_RX, ry: C2.CENTER_RY, lines: wrapToWidth(b.left, C2.CENTER_RX * 1.4, C2.HEADER_FONT), variant: "center", side: "left" });
|
|
22240
|
+
rawE.push({ cx: rcx, cy: 0, rx: C2.CENTER_RX, ry: C2.CENTER_RY, lines: wrapToWidth(b.right, C2.CENTER_RX * 1.4, C2.HEADER_FONT), variant: "center", side: "right" });
|
|
22241
|
+
const sN = b.shared.length;
|
|
22242
|
+
const sStep = 2 * C2.SHARED_RY + C2.BUBBLE_VGAP;
|
|
22243
|
+
const sTotal = sN > 0 ? (sN - 1) * sStep : 0;
|
|
22244
|
+
b.shared.forEach((s, i) => {
|
|
22245
|
+
const sy = -sTotal / 2 + i * sStep;
|
|
22246
|
+
rawE.push({ cx: 0, cy: sy, rx: C2.SHARED_RX, ry: C2.SHARED_RY, lines: wrapToWidth(s, C2.SHARED_RX * 1.6, C2.FONT), variant: "shared" });
|
|
22247
|
+
rawC.push({ x1: lcx, y1: 0, x2: 0, y2: sy });
|
|
22248
|
+
rawC.push({ x1: rcx, y1: 0, x2: 0, y2: sy });
|
|
22249
|
+
});
|
|
22250
|
+
const placeFan = (items, cx, centreDeg, side) => {
|
|
22251
|
+
const n = items.length;
|
|
22252
|
+
if (n === 0) return;
|
|
22253
|
+
const half = Math.min(82, 30 + 17 * (n - 1));
|
|
22254
|
+
const stepDeg = n > 1 ? 2 * half / (n - 1) : 0;
|
|
22255
|
+
const stepRad = stepDeg * Math.PI / 180;
|
|
22256
|
+
const needChord = 2 * C2.UNIQUE_RY + 18;
|
|
22257
|
+
const rOrbit = Math.max(
|
|
22258
|
+
C2.CENTER_RX + 104,
|
|
22259
|
+
n > 1 ? needChord / (2 * Math.sin(stepRad / 2)) : C2.CENTER_RX + 104
|
|
22260
|
+
);
|
|
22261
|
+
items.forEach((it, i) => {
|
|
22262
|
+
const aDeg = n > 1 ? centreDeg - half + stepDeg * i : centreDeg;
|
|
22263
|
+
const a = aDeg * Math.PI / 180;
|
|
22264
|
+
const bx = cx + rOrbit * Math.cos(a);
|
|
22265
|
+
const by = rOrbit * Math.sin(a);
|
|
22266
|
+
rawE.push({ cx: bx, cy: by, rx: C2.UNIQUE_RX, ry: C2.UNIQUE_RY, lines: wrapToWidth(it, C2.UNIQUE_RX * 1.5, C2.FONT), variant: "unique", side });
|
|
22267
|
+
rawC.push({ x1: cx, y1: 0, x2: bx, y2: by });
|
|
22268
|
+
});
|
|
22269
|
+
};
|
|
22270
|
+
placeFan(b.leftOnly, lcx, 180, "left");
|
|
22271
|
+
placeFan(b.rightOnly, rcx, 0, "right");
|
|
22272
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
22273
|
+
for (const e of rawE) {
|
|
22274
|
+
minX = Math.min(minX, e.cx - e.rx);
|
|
22275
|
+
maxX = Math.max(maxX, e.cx + e.rx);
|
|
22276
|
+
minY = Math.min(minY, e.cy - e.ry);
|
|
22277
|
+
maxY = Math.max(maxY, e.cy + e.ry);
|
|
22278
|
+
}
|
|
22279
|
+
const offX = pad - minX;
|
|
22280
|
+
const offY = top - minY;
|
|
22281
|
+
const ellipses = rawE.map((e) => ({
|
|
22282
|
+
cx: round8(e.cx + offX),
|
|
22283
|
+
cy: round8(e.cy + offY),
|
|
22284
|
+
rx: e.rx,
|
|
22285
|
+
ry: e.ry,
|
|
22286
|
+
lines: e.lines,
|
|
22287
|
+
variant: e.variant,
|
|
22288
|
+
...e.side ? { side: e.side } : {}
|
|
22289
|
+
}));
|
|
22290
|
+
const connectors = rawC.map((c) => ({
|
|
22291
|
+
x1: round8(c.x1 + offX),
|
|
22292
|
+
y1: round8(c.y1 + offY),
|
|
22293
|
+
x2: round8(c.x2 + offX),
|
|
22294
|
+
y2: round8(c.y2 + offY)
|
|
22295
|
+
}));
|
|
22296
|
+
return {
|
|
22297
|
+
ast,
|
|
22298
|
+
mode: ast.mode,
|
|
22299
|
+
cells: [],
|
|
22300
|
+
ellipses,
|
|
22301
|
+
connectors,
|
|
22302
|
+
width: Math.ceil(maxX - minX + 2 * pad),
|
|
22303
|
+
height: Math.ceil(maxY - minY + top + pad)
|
|
22304
|
+
};
|
|
22305
|
+
}
|
|
22306
|
+
function clamp(v, lo, hi) {
|
|
22307
|
+
return Math.max(lo, Math.min(hi, v));
|
|
22308
|
+
}
|
|
22309
|
+
function round8(n) {
|
|
22310
|
+
return Math.round(n * 10) / 10;
|
|
22311
|
+
}
|
|
22312
|
+
|
|
22313
|
+
// src/diagrams/comparison/renderer.ts
|
|
22314
|
+
function ellipse(attrs) {
|
|
22315
|
+
return el("ellipse", attrs);
|
|
22316
|
+
}
|
|
22317
|
+
function renderComparison(text2, config) {
|
|
22318
|
+
return renderComparisonLayout(layoutComparison(parseComparison(text2)), config);
|
|
22319
|
+
}
|
|
22320
|
+
function renderComparisonLayout(layout, config) {
|
|
22321
|
+
const themeName = config?.theme ?? "default";
|
|
22322
|
+
const t = resolveComparisonTheme(themeName);
|
|
22323
|
+
const fontFamily = config?.fontFamily ?? DEFAULT_FONT_FAMILY;
|
|
22324
|
+
const pad = config?.padding ?? 0;
|
|
22325
|
+
const { ast } = layout;
|
|
22326
|
+
const width = layout.width + pad * 2;
|
|
22327
|
+
const height = layout.height + pad * 2;
|
|
22328
|
+
const a11y = ast.title ?? `Comparison (${ast.mode})`;
|
|
22329
|
+
const style = el(
|
|
22330
|
+
"style",
|
|
22331
|
+
{},
|
|
22332
|
+
`
|
|
22333
|
+
.sx-cmp-bg { fill: ${t.bg}; }
|
|
22334
|
+
.sx-cmp-title { fill: ${t.text}; font-size: ${TITLE.size}px; font-weight: ${TITLE.weight}; }
|
|
22335
|
+
.sx-cmp-caption { fill: ${t.captionText}; font-size: ${FONT_SIZE.label}px; font-style: italic; }
|
|
22336
|
+
.sx-cmp-tag { fill: ${t.tagText}; font-size: ${FONT_SIZE.small}px; }
|
|
22337
|
+
.sx-cmp-rect { stroke: ${t.gridStroke}; stroke-width: ${STROKE_WIDTH.thin}; }
|
|
22338
|
+
.sx-cmp-txt { font-size: ${FONT_SIZE.label}px; }
|
|
22339
|
+
.sx-cmp-rect-corner { fill: ${t.rowHeaderFill}; stroke: ${t.gridStroke}; }
|
|
22340
|
+
.sx-cmp-rect-colHeader { fill: ${t.headerFill}; stroke: ${t.headerStroke}; }
|
|
22341
|
+
.sx-cmp-txt-colHeader { fill: ${t.headerText}; font-size: ${FONT_SIZE.label}px; font-weight: 700; }
|
|
22342
|
+
${t.columnColors.map((c, i) => `.sx-cmp-ch-${i} { fill: ${c}; stroke: none; }`).join("\n")}
|
|
22343
|
+
.sx-cmp-cht { fill: ${t.columnText}; font-size: ${FONT_SIZE.label}px; font-weight: 700; }
|
|
22344
|
+
.sx-cmp-nostroke { stroke: none; }
|
|
22345
|
+
.sx-cmp-rect-baseline { fill: ${t.baselineFill}; stroke: ${t.gridStroke}; }
|
|
22346
|
+
.sx-cmp-txt-baseline { fill: ${t.cellText}; font-weight: 700; }
|
|
22347
|
+
.sx-cmp-rect-rowHeader { fill: ${t.rowHeaderFill}; stroke: ${t.gridStroke}; }
|
|
22348
|
+
.sx-cmp-txt-rowHeader { fill: ${t.cellText}; font-weight: 600; }
|
|
22349
|
+
.sx-cmp-rect-body { fill: ${t.cellFill}; stroke: ${t.gridStroke}; }
|
|
22350
|
+
.sx-cmp-rect-body-z { fill: ${t.cellAltFill}; stroke: ${t.gridStroke}; }
|
|
22351
|
+
.sx-cmp-txt-body { fill: ${t.cellText}; }
|
|
22352
|
+
.sx-cmp-bullet { fill: ${t.headerFill}; }
|
|
22353
|
+
.sx-cmp-rect-pos { fill: ${t.posFill}; stroke: ${t.gridStroke}; }
|
|
22354
|
+
.sx-cmp-txt-pos { fill: ${t.posText}; font-weight: 600; }
|
|
22355
|
+
.sx-cmp-rect-neg { fill: ${t.negFill}; stroke: ${t.gridStroke}; }
|
|
22356
|
+
.sx-cmp-txt-neg { fill: ${t.negText}; font-weight: 600; }
|
|
22357
|
+
.sx-cmp-rect-warn { fill: ${t.warnFill}; stroke: ${t.gridStroke}; }
|
|
22358
|
+
.sx-cmp-txt-warn { fill: ${t.warnText}; font-weight: 600; }
|
|
22359
|
+
.sx-cmp-txt-pro { fill: ${t.cellText}; }
|
|
22360
|
+
.sx-cmp-txt-con { fill: ${t.cellText}; }
|
|
22361
|
+
.sx-cmp-rect-pillPos { fill: ${t.pillPosFill}; stroke: none; }
|
|
22362
|
+
.sx-cmp-rect-pillNeg { fill: ${t.pillNegFill}; stroke: none; }
|
|
22363
|
+
.sx-cmp-txt-pillPos, .sx-cmp-txt-pillNeg { fill: ${t.pillText}; font-weight: 700; letter-spacing: 0.5px; }
|
|
22364
|
+
.sx-cmp-badge-pos { fill: ${t.pillPosFill}; }
|
|
22365
|
+
.sx-cmp-badge-neg { fill: ${t.pillNegFill}; }
|
|
22366
|
+
.sx-cmp-badge-glyph { fill: ${t.badgeText}; font-size: 13px; font-weight: 700; }
|
|
22367
|
+
.sx-cmp-rect-total { fill: ${t.totalFill}; stroke: ${t.gridStroke}; }
|
|
22368
|
+
.sx-cmp-txt-total { fill: ${t.cellText}; font-weight: 700; }
|
|
22369
|
+
.sx-cmp-rect-winner { fill: ${t.winnerFill}; stroke: ${t.winnerStroke}; stroke-width: ${STROKE_WIDTH.thick}; }
|
|
22370
|
+
.sx-cmp-txt-winner { fill: ${t.winnerText}; font-weight: 700; }
|
|
22371
|
+
.sx-cmp-glyph { font-size: 15px; font-weight: 700; }
|
|
22372
|
+
.sx-cmp-card { fill: ${t.cardFill}; stroke: ${t.cardStroke}; stroke-width: ${STROKE_WIDTH.thin}; }
|
|
22373
|
+
.sx-cmp-conn { fill: none; stroke: ${t.connectorStroke}; stroke-width: ${STROKE_WIDTH.normal}; }
|
|
22374
|
+
.sx-cmp-conn-light { fill: none; stroke: ${t.rowDivider}; stroke-width: ${STROKE_WIDTH.thin}; }
|
|
22375
|
+
.sx-cmp-ell { stroke: ${t.dbStroke}; stroke-width: ${STROKE_WIDTH.normal}; }
|
|
22376
|
+
.sx-cmp-ell-center-left { fill: ${t.dbLeftCenterFill}; }
|
|
22377
|
+
.sx-cmp-ell-center-right { fill: ${t.dbRightCenterFill}; }
|
|
22378
|
+
.sx-cmp-ell-shared { fill: ${t.dbSharedFill}; }
|
|
22379
|
+
.sx-cmp-ell-unique-left { fill: ${t.dbLeftFill}; }
|
|
22380
|
+
.sx-cmp-ell-unique-right { fill: ${t.dbRightFill}; }
|
|
22381
|
+
.sx-cmp-ltxt-center-left { fill: ${t.dbLeftCenterText}; font-size: ${FONT_SIZE.label}px; font-weight: 700; }
|
|
22382
|
+
.sx-cmp-ltxt-center-right { fill: ${t.dbRightCenterText}; font-size: ${FONT_SIZE.label}px; font-weight: 700; }
|
|
22383
|
+
.sx-cmp-ltxt-shared { fill: ${t.dbSharedText}; font-size: ${FONT_SIZE.label}px; font-weight: 600; }
|
|
22384
|
+
.sx-cmp-ltxt-unique-left { fill: ${t.dbLeftText}; font-size: ${FONT_SIZE.small}px; }
|
|
22385
|
+
.sx-cmp-ltxt-unique-right { fill: ${t.dbRightText}; font-size: ${FONT_SIZE.small}px; }
|
|
22386
|
+
`.trim()
|
|
22387
|
+
);
|
|
22388
|
+
const children = [
|
|
22389
|
+
title(a11y),
|
|
22390
|
+
desc(summarise7(layout)),
|
|
22391
|
+
style,
|
|
22392
|
+
rect({ x: 0, y: 0, width, height, class: "sx-cmp-bg" })
|
|
22393
|
+
];
|
|
22394
|
+
const inner = [];
|
|
22395
|
+
if (ast.title) {
|
|
22396
|
+
inner.push(
|
|
22397
|
+
text(
|
|
22398
|
+
{ x: layout.width / 2, y: TITLE.y, class: "sx-cmp-title", "font-family": fontFamily, "text-anchor": "middle" },
|
|
22399
|
+
ast.title
|
|
22400
|
+
)
|
|
22401
|
+
);
|
|
22402
|
+
}
|
|
22403
|
+
for (const f of layout.frames ?? []) {
|
|
22404
|
+
inner.push(rect({ x: f.x, y: f.y, width: f.w, height: f.h, rx: f.rx, class: "sx-cmp-card" }));
|
|
22405
|
+
}
|
|
22406
|
+
for (const c of layout.connectors) {
|
|
22407
|
+
inner.push(line({ x1: c.x1, y1: c.y1, x2: c.x2, y2: c.y2, class: c.light ? "sx-cmp-conn-light" : "sx-cmp-conn" }));
|
|
22408
|
+
}
|
|
22409
|
+
const colN = t.columnColors.length;
|
|
22410
|
+
for (const cell of layout.cells) {
|
|
22411
|
+
inner.push(renderCell(cell, fontFamily, colN));
|
|
22412
|
+
}
|
|
22413
|
+
for (const e of layout.ellipses) {
|
|
22414
|
+
const key = e.variant === "center" || e.variant === "unique" ? `${e.variant}-${e.side ?? "left"}` : e.variant;
|
|
22415
|
+
const cls = `sx-cmp-ell sx-cmp-ell-${key}`;
|
|
22416
|
+
const txtCls = `sx-cmp-ltxt-${key}`;
|
|
22417
|
+
inner.push(
|
|
22418
|
+
group({ class: "sx-cmp-bubble", "data-role": e.variant, ...e.side ? { "data-side": e.side } : {} }, [
|
|
22419
|
+
ellipse({ cx: e.cx, cy: e.cy, rx: e.rx, ry: e.ry, class: cls }),
|
|
22420
|
+
multilineText(
|
|
22421
|
+
{ x: e.cx, y: e.cy, class: txtCls, "font-family": fontFamily, "text-anchor": "middle", "dominant-baseline": "middle" },
|
|
22422
|
+
e.lines.join("<br/>"),
|
|
22423
|
+
C_LINE_H
|
|
22424
|
+
)
|
|
22425
|
+
])
|
|
22426
|
+
);
|
|
22427
|
+
}
|
|
22428
|
+
if (layout.caption) {
|
|
22429
|
+
inner.push(
|
|
22430
|
+
text(
|
|
22431
|
+
{ x: layout.width / 2, y: layout.height - 12, class: "sx-cmp-caption", "font-family": fontFamily, "text-anchor": "middle" },
|
|
22432
|
+
layout.caption
|
|
22433
|
+
)
|
|
22434
|
+
);
|
|
22435
|
+
}
|
|
22436
|
+
children.push(
|
|
22437
|
+
group({ transform: pad ? `translate(${pad}, ${pad})` : void 0, "font-family": fontFamily }, inner)
|
|
22438
|
+
);
|
|
22439
|
+
return svgRoot(
|
|
22440
|
+
{
|
|
22441
|
+
width,
|
|
22442
|
+
height,
|
|
22443
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
22444
|
+
role: "img",
|
|
22445
|
+
"aria-label": a11y,
|
|
22446
|
+
"data-diagram-type": "comparison",
|
|
22447
|
+
"data-mode": ast.mode
|
|
22448
|
+
},
|
|
22449
|
+
children
|
|
22450
|
+
);
|
|
22451
|
+
}
|
|
22452
|
+
var C_LINE_H = 14;
|
|
22453
|
+
function renderCell(cell, fontFamily, colN) {
|
|
22454
|
+
const parts = [];
|
|
22455
|
+
const colHeaderClass = cell.variant === "colHeader" && cell.paletteIndex !== void 0 ? `sx-cmp-ch-${cell.paletteIndex % Math.max(1, colN)}` : void 0;
|
|
22456
|
+
if (!cell.bare) {
|
|
22457
|
+
if (cell.roundedTop) {
|
|
22458
|
+
parts.push(
|
|
22459
|
+
path({
|
|
22460
|
+
d: roundedTopRectPath(cell.x, cell.y, cell.w, cell.h, cell.rx ?? 12),
|
|
22461
|
+
class: colHeaderClass ?? "sx-cmp-rect-colHeader"
|
|
22462
|
+
})
|
|
22463
|
+
);
|
|
22464
|
+
} else {
|
|
22465
|
+
const rectVariant = cell.zebra && cell.variant === "body" ? "body-z" : cell.variant;
|
|
22466
|
+
const rectClass = colHeaderClass ? `sx-cmp-rect ${colHeaderClass}` : `sx-cmp-rect sx-cmp-rect-${rectVariant}${cell.noStroke ? " sx-cmp-nostroke" : ""}`;
|
|
22467
|
+
parts.push(
|
|
22468
|
+
rect({ x: cell.x, y: cell.y, width: cell.w, height: cell.h, rx: cell.rx, class: rectClass })
|
|
22469
|
+
);
|
|
22470
|
+
}
|
|
22471
|
+
}
|
|
22472
|
+
const txtClass = colHeaderClass ? "sx-cmp-txt sx-cmp-cht" : `sx-cmp-txt sx-cmp-txt-${cell.variant}`;
|
|
22473
|
+
const cy = cell.y + cell.h / 2;
|
|
22474
|
+
const br = 11;
|
|
22475
|
+
const drawBadge = (bcx) => {
|
|
22476
|
+
parts.push(circle({ cx: bcx, cy, r: br, class: `sx-cmp-badge-${cell.badge.tone}` }));
|
|
22477
|
+
parts.push(
|
|
22478
|
+
text(
|
|
22479
|
+
{ x: bcx, y: cy + 0.5, class: "sx-cmp-badge-glyph", "font-family": fontFamily, "text-anchor": "middle", "dominant-baseline": "middle" },
|
|
22480
|
+
cell.badge.glyph === "yes" ? "\u2713" : "\u2717"
|
|
22481
|
+
)
|
|
22482
|
+
);
|
|
22483
|
+
};
|
|
22484
|
+
if (cell.align === "start") {
|
|
22485
|
+
let tx = cell.x + 12;
|
|
22486
|
+
if (cell.badge) {
|
|
22487
|
+
const bcx = cell.x + 12 + br;
|
|
22488
|
+
drawBadge(bcx);
|
|
22489
|
+
tx = bcx + br + 10;
|
|
22490
|
+
} else if (cell.tag === "\u2022") {
|
|
22491
|
+
parts.push(circle({ cx: cell.x + 14, cy, r: 2.6, class: "sx-cmp-bullet" }));
|
|
22492
|
+
tx = cell.x + 24;
|
|
22493
|
+
}
|
|
22494
|
+
if (cell.lines.length) {
|
|
22495
|
+
parts.push(
|
|
22496
|
+
multilineText(
|
|
22497
|
+
{ x: tx, y: cy, class: txtClass, "font-family": fontFamily, "dominant-baseline": "middle" },
|
|
22498
|
+
cell.lines.join("<br/>"),
|
|
22499
|
+
C_LINE_H
|
|
22500
|
+
)
|
|
22501
|
+
);
|
|
22502
|
+
}
|
|
22503
|
+
} else if (cell.badge) {
|
|
22504
|
+
const bcx = cell.x + 18 + br;
|
|
22505
|
+
drawBadge(bcx);
|
|
22506
|
+
const labelCx = (bcx + br + 6 + (cell.x + cell.w - 12)) / 2;
|
|
22507
|
+
parts.push(
|
|
22508
|
+
multilineText(
|
|
22509
|
+
{ x: labelCx, y: cy, class: txtClass, "font-family": fontFamily, "text-anchor": "middle", "dominant-baseline": "middle" },
|
|
22510
|
+
cell.lines.join("<br/>"),
|
|
22511
|
+
C_LINE_H
|
|
22512
|
+
)
|
|
22513
|
+
);
|
|
22514
|
+
} else if (cell.glyph && cell.lines.length === 0) {
|
|
22515
|
+
parts.push(
|
|
22516
|
+
text(
|
|
22517
|
+
{ x: cell.x + cell.w / 2, y: cy, class: `sx-cmp-glyph ${glyphTextClass(cell.glyph)}`, "font-family": fontFamily, "text-anchor": "middle", "dominant-baseline": "middle" },
|
|
22518
|
+
glyphChar(cell.glyph)
|
|
22519
|
+
)
|
|
22520
|
+
);
|
|
22521
|
+
} else if (cell.lines.length) {
|
|
22522
|
+
parts.push(
|
|
22523
|
+
multilineText(
|
|
22524
|
+
{ x: cell.x + cell.w / 2, y: cy, class: txtClass, "font-family": fontFamily, "text-anchor": "middle", "dominant-baseline": "middle" },
|
|
22525
|
+
cell.lines.join("<br/>"),
|
|
22526
|
+
C_LINE_H
|
|
22527
|
+
)
|
|
22528
|
+
);
|
|
22529
|
+
}
|
|
22530
|
+
if (cell.tag && cell.tag !== "\u2022") {
|
|
22531
|
+
if (cell.variant === "baseline") {
|
|
22532
|
+
parts.push(
|
|
22533
|
+
text(
|
|
22534
|
+
{ x: cell.x + cell.w / 2, y: cell.y + cell.h - 5, class: "sx-cmp-tag", "font-family": fontFamily, "text-anchor": "middle" },
|
|
22535
|
+
cell.tag
|
|
22536
|
+
)
|
|
22537
|
+
);
|
|
22538
|
+
} else {
|
|
22539
|
+
parts.push(
|
|
22540
|
+
text(
|
|
22541
|
+
{ x: cell.x + cell.w - 5, y: cell.y + 13, class: "sx-cmp-tag", "font-family": fontFamily, "text-anchor": "end" },
|
|
22542
|
+
cell.tag
|
|
22543
|
+
)
|
|
22544
|
+
);
|
|
22545
|
+
}
|
|
22546
|
+
}
|
|
22547
|
+
const attrs = { class: "sx-cmp-cell-g", "data-variant": cell.variant };
|
|
22548
|
+
if (cell.glyph) attrs["data-glyph"] = cell.glyph;
|
|
22549
|
+
return group(attrs, parts);
|
|
22550
|
+
}
|
|
22551
|
+
function roundedTopRectPath(x, y, w, h, r7) {
|
|
22552
|
+
const rr = Math.max(0, Math.min(r7, w / 2, h));
|
|
22553
|
+
return `M ${x} ${y + h} L ${x} ${y + rr} Q ${x} ${y} ${x + rr} ${y} L ${x + w - rr} ${y} Q ${x + w} ${y} ${x + w} ${y + rr} L ${x + w} ${y + h} Z`;
|
|
22554
|
+
}
|
|
22555
|
+
function glyphChar(g) {
|
|
22556
|
+
return g === "yes" ? "\u2713" : g === "no" ? "\u2717" : g === "partial" ? "~" : "\u2014";
|
|
22557
|
+
}
|
|
22558
|
+
function glyphTextClass(g) {
|
|
22559
|
+
return g === "yes" ? "sx-cmp-txt-pos" : g === "no" ? "sx-cmp-txt-neg" : g === "partial" ? "sx-cmp-txt-warn" : "sx-cmp-txt-body";
|
|
22560
|
+
}
|
|
22561
|
+
function summarise7(layout) {
|
|
22562
|
+
const { ast } = layout;
|
|
22563
|
+
const parts = [];
|
|
22564
|
+
switch (ast.mode) {
|
|
22565
|
+
case "tchart":
|
|
22566
|
+
parts.push(`T-chart comparing ${ast.columns.length} column${plural2(ast.columns.length)}: ${ast.columns.map((c) => `"${c.label}"`).join(", ")}.`);
|
|
22567
|
+
break;
|
|
22568
|
+
case "pros-cons":
|
|
22569
|
+
parts.push(`Pros/cons${ast.subject ? ` for "${ast.subject}"` : ""}: ${ast.pros.length} pro${plural2(ast.pros.length)}, ${ast.cons.length} con${plural2(ast.cons.length)}.`);
|
|
22570
|
+
break;
|
|
22571
|
+
case "matrix":
|
|
22572
|
+
parts.push(`Comparison matrix: ${ast.options.length} option${plural2(ast.options.length)} \xD7 ${ast.criteria.length} criteria.`);
|
|
22573
|
+
break;
|
|
22574
|
+
case "decision":
|
|
22575
|
+
parts.push(`Decision matrix: ${ast.options.length} option${plural2(ast.options.length)} \xD7 ${ast.criteria.length} weighted criteria.`);
|
|
22576
|
+
if (layout.caption) parts.push(layout.caption);
|
|
22577
|
+
break;
|
|
22578
|
+
case "double-bubble":
|
|
22579
|
+
parts.push(`Double-bubble comparing "${ast.bubble?.left}" and "${ast.bubble?.right}": ${ast.bubble?.shared.length ?? 0} shared, ${ast.bubble?.leftOnly.length ?? 0}/${ast.bubble?.rightOnly.length ?? 0} unique.`);
|
|
22580
|
+
break;
|
|
22581
|
+
}
|
|
22582
|
+
for (const w of ast.warnings) parts.push(w);
|
|
22583
|
+
return parts.join(" ");
|
|
22584
|
+
}
|
|
22585
|
+
function plural2(n) {
|
|
22586
|
+
return n === 1 ? "" : "s";
|
|
22587
|
+
}
|
|
22588
|
+
|
|
22589
|
+
// src/diagrams/comparison/index.ts
|
|
22590
|
+
var comparison = {
|
|
22591
|
+
type: "comparison",
|
|
22592
|
+
detect(text2) {
|
|
22593
|
+
return /^\s*(comparison|compare|vs|tchart|t-chart|pugh|decision-matrix|decisionmatrix)\b/i.test(text2);
|
|
22594
|
+
},
|
|
22595
|
+
parse: parseComparison,
|
|
22596
|
+
render(text2, config) {
|
|
22597
|
+
return renderComparison(text2, config);
|
|
22598
|
+
}
|
|
22599
|
+
};
|
|
22600
|
+
|
|
21362
22601
|
// src/diagrams/causalloop/parser.ts
|
|
21363
22602
|
var CausalLoopParseError = class extends Error {
|
|
21364
22603
|
constructor(message, line2) {
|
|
@@ -21381,7 +22620,7 @@ function parseCausalLoop(text2) {
|
|
|
21381
22620
|
let i = 0;
|
|
21382
22621
|
let headerSeen = false;
|
|
21383
22622
|
for (; i < lines.length; i++) {
|
|
21384
|
-
const t =
|
|
22623
|
+
const t = stripComment9(lines[i] ?? "").trim();
|
|
21385
22624
|
if (t === "") continue;
|
|
21386
22625
|
const h = /^(causalloop|cld)\b(.*)$/i.exec(t);
|
|
21387
22626
|
if (h) {
|
|
@@ -21402,7 +22641,7 @@ function parseCausalLoop(text2) {
|
|
|
21402
22641
|
);
|
|
21403
22642
|
}
|
|
21404
22643
|
for (; i < lines.length; i++) {
|
|
21405
|
-
const t =
|
|
22644
|
+
const t = stripComment9(lines[i] ?? "").trim();
|
|
21406
22645
|
if (t === "") continue;
|
|
21407
22646
|
const lineNo = i + 1;
|
|
21408
22647
|
const lm = /^layout\s*:\s*(\S+)/i.exec(t);
|
|
@@ -21424,9 +22663,9 @@ function parseCausalLoop(text2) {
|
|
|
21424
22663
|
parseLinkLine(ast, t, lineNo);
|
|
21425
22664
|
continue;
|
|
21426
22665
|
}
|
|
21427
|
-
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${
|
|
22666
|
+
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${truncate8(t, 80)}"`);
|
|
21428
22667
|
}
|
|
21429
|
-
|
|
22668
|
+
validate5(ast);
|
|
21430
22669
|
return ast;
|
|
21431
22670
|
}
|
|
21432
22671
|
function parseVarLine(ast, rest, lineNo) {
|
|
@@ -21438,7 +22677,7 @@ function parseLoopLine(ast, rest, lineNo) {
|
|
|
21438
22677
|
const m = /^([RB]\d+)\s+(.+)$/i.exec(rest);
|
|
21439
22678
|
if (!m) {
|
|
21440
22679
|
throw new CausalLoopParseError(
|
|
21441
|
-
`'loop' needs a loop id (e.g. R1, B1) and a phrase, got "${
|
|
22680
|
+
`'loop' needs a loop id (e.g. R1, B1) and a phrase, got "${truncate8(rest, 60)}"`,
|
|
21442
22681
|
lineNo
|
|
21443
22682
|
);
|
|
21444
22683
|
}
|
|
@@ -21449,7 +22688,7 @@ function parseLoopLine(ast, rest, lineNo) {
|
|
|
21449
22688
|
function parseLinkLine(ast, t, lineNo) {
|
|
21450
22689
|
const arrowIdx = topLevelArrow(t);
|
|
21451
22690
|
if (arrowIdx < 0) {
|
|
21452
|
-
throw new CausalLoopParseError(`malformed link (expected '->'): "${
|
|
22691
|
+
throw new CausalLoopParseError(`malformed link (expected '->'): "${truncate8(t, 60)}"`, lineNo);
|
|
21453
22692
|
}
|
|
21454
22693
|
const fromRaw = t.slice(0, arrowIdx).trim();
|
|
21455
22694
|
let rhs = t.slice(arrowIdx + 2).trim();
|
|
@@ -21491,7 +22730,7 @@ function parseLinkLine(ast, t, lineNo) {
|
|
|
21491
22730
|
if (label) link.label = label;
|
|
21492
22731
|
ast.links.push(link);
|
|
21493
22732
|
}
|
|
21494
|
-
function
|
|
22733
|
+
function validate5(ast) {
|
|
21495
22734
|
if (ast.links.length === 0) {
|
|
21496
22735
|
throw new CausalLoopParseError(
|
|
21497
22736
|
"a causal loop diagram needs at least one causal link (A -> B : +)"
|
|
@@ -21571,7 +22810,7 @@ function stripLeadingQuoted(s) {
|
|
|
21571
22810
|
return void 0;
|
|
21572
22811
|
}
|
|
21573
22812
|
}
|
|
21574
|
-
function
|
|
22813
|
+
function stripComment9(line2) {
|
|
21575
22814
|
let i = 0;
|
|
21576
22815
|
while (i < line2.length) {
|
|
21577
22816
|
const ch = line2[i];
|
|
@@ -21587,7 +22826,7 @@ function stripComment8(line2) {
|
|
|
21587
22826
|
}
|
|
21588
22827
|
return line2;
|
|
21589
22828
|
}
|
|
21590
|
-
function
|
|
22829
|
+
function truncate8(s, n) {
|
|
21591
22830
|
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
21592
22831
|
}
|
|
21593
22832
|
|
|
@@ -21744,17 +22983,17 @@ var CAUSALLOOP_CONST = {
|
|
|
21744
22983
|
};
|
|
21745
22984
|
function layoutCausalLoop(ast) {
|
|
21746
22985
|
const analysis = analyseCausalLoop(ast);
|
|
21747
|
-
const
|
|
22986
|
+
const C3 = CAUSALLOOP_CONST;
|
|
21748
22987
|
const nodes = /* @__PURE__ */ new Map();
|
|
21749
22988
|
for (const v of ast.variables) {
|
|
21750
|
-
const halfW = Math.max(20, v.label.length *
|
|
22989
|
+
const halfW = Math.max(20, v.label.length * C3.CHAR_W / 2);
|
|
21751
22990
|
nodes.set(v.id, {
|
|
21752
22991
|
id: v.id,
|
|
21753
22992
|
label: v.label,
|
|
21754
22993
|
cx: 0,
|
|
21755
22994
|
cy: 0,
|
|
21756
22995
|
halfW,
|
|
21757
|
-
halfH:
|
|
22996
|
+
halfH: C3.LABEL_H / 2
|
|
21758
22997
|
});
|
|
21759
22998
|
}
|
|
21760
22999
|
const ringIds = ringOrder(ast, analysis);
|
|
@@ -21764,9 +23003,9 @@ function layoutCausalLoop(ast) {
|
|
|
21764
23003
|
let cy0 = 0;
|
|
21765
23004
|
let ringR = 0;
|
|
21766
23005
|
if (ringIds.length > 0) {
|
|
21767
|
-
ringR = Math.max(
|
|
21768
|
-
cx0 =
|
|
21769
|
-
cy0 =
|
|
23006
|
+
ringR = Math.max(C3.MIN_RING_R, C3.RING_GAP * ringIds.length / (2 * Math.PI));
|
|
23007
|
+
cx0 = C3.CANVAS_PAD + ringR + maxHalfW(ringIds, nodes);
|
|
23008
|
+
cy0 = C3.CANVAS_PAD + ringR + C3.LABEL_H;
|
|
21770
23009
|
const step = 2 * Math.PI / ringIds.length;
|
|
21771
23010
|
ringIds.forEach((id, i) => {
|
|
21772
23011
|
const ang = -Math.PI / 2 + i * step;
|
|
@@ -21776,14 +23015,14 @@ function layoutCausalLoop(ast) {
|
|
|
21776
23015
|
});
|
|
21777
23016
|
}
|
|
21778
23017
|
if (openIds.length > 0) {
|
|
21779
|
-
const rowY = ringIds.length > 0 ? cy0 + ringR +
|
|
21780
|
-
let x =
|
|
23018
|
+
const rowY = ringIds.length > 0 ? cy0 + ringR + C3.OPEN_ROW_GAP : C3.CANVAS_PAD + C3.LABEL_H;
|
|
23019
|
+
let x = C3.CANVAS_PAD;
|
|
21781
23020
|
for (const id of openIds) {
|
|
21782
23021
|
const node = nodes.get(id);
|
|
21783
23022
|
x += node.halfW;
|
|
21784
23023
|
node.cx = x;
|
|
21785
23024
|
node.cy = rowY;
|
|
21786
|
-
x += node.halfW +
|
|
23025
|
+
x += node.halfW + C3.OPEN_ROW_GAP;
|
|
21787
23026
|
}
|
|
21788
23027
|
}
|
|
21789
23028
|
const center = { x: cx0, y: cy0 };
|
|
@@ -21811,8 +23050,8 @@ function layoutCausalLoop(ast) {
|
|
|
21811
23050
|
maxX = Math.max(maxX, l.polarityX + 12);
|
|
21812
23051
|
maxY = Math.max(maxY, l.polarityY + 12);
|
|
21813
23052
|
}
|
|
21814
|
-
const width = Math.ceil(maxX +
|
|
21815
|
-
const height = Math.ceil(maxY +
|
|
23053
|
+
const width = Math.ceil(maxX + C3.CANVAS_PAD);
|
|
23054
|
+
const height = Math.ceil(maxY + C3.CANVAS_PAD);
|
|
21816
23055
|
return {
|
|
21817
23056
|
ast,
|
|
21818
23057
|
analysis,
|
|
@@ -21850,7 +23089,7 @@ function ringOrder(ast, analysis) {
|
|
|
21850
23089
|
return order;
|
|
21851
23090
|
}
|
|
21852
23091
|
function layoutLink(l, idx, nodes, center, ringSet, pairIndex) {
|
|
21853
|
-
const
|
|
23092
|
+
const C3 = CAUSALLOOP_CONST;
|
|
21854
23093
|
const a = nodes.get(l.from);
|
|
21855
23094
|
const b = nodes.get(l.to);
|
|
21856
23095
|
if (l.from === l.to) {
|
|
@@ -21878,17 +23117,17 @@ function layoutLink(l, idx, nodes, center, ringSet, pairIndex) {
|
|
|
21878
23117
|
ny = ux;
|
|
21879
23118
|
}
|
|
21880
23119
|
const side = pairIndex % 2 === 0 ? 1 : -1;
|
|
21881
|
-
const bow = dist *
|
|
23120
|
+
const bow = dist * C3.CURVE_BOW * side;
|
|
21882
23121
|
const ctrlX = mx + nx * bow;
|
|
21883
23122
|
const ctrlY = my + ny * bow;
|
|
21884
|
-
const path2 = `M ${
|
|
23123
|
+
const path2 = `M ${fmt4(start.x)} ${fmt4(start.y)} Q ${fmt4(ctrlX)} ${fmt4(ctrlY)} ${fmt4(end.x)} ${fmt4(end.y)}`;
|
|
21885
23124
|
let tx = end.x - ctrlX;
|
|
21886
23125
|
let ty = end.y - ctrlY;
|
|
21887
23126
|
const tn = Math.hypot(tx, ty) || 1;
|
|
21888
23127
|
tx /= tn;
|
|
21889
23128
|
ty /= tn;
|
|
21890
|
-
const polarityX = end.x - tx *
|
|
21891
|
-
const polarityY = end.y - ty *
|
|
23129
|
+
const polarityX = end.x - tx * C3.HEAD_OFFSET + nx * side * 10;
|
|
23130
|
+
const polarityY = end.y - ty * C3.HEAD_OFFSET + ny * side * 10;
|
|
21892
23131
|
const result = {
|
|
21893
23132
|
linkIndex: idx,
|
|
21894
23133
|
from: l.from,
|
|
@@ -21925,9 +23164,9 @@ function selfLink(l, idx, a) {
|
|
|
21925
23164
|
const x1 = a.cx - a.halfW * 0.4;
|
|
21926
23165
|
const x2 = a.cx + a.halfW * 0.4;
|
|
21927
23166
|
const loopTop = topY - 34;
|
|
21928
|
-
const path2 = `M ${
|
|
23167
|
+
const path2 = `M ${fmt4(x1)} ${fmt4(topY)} C ${fmt4(x1 - 12)} ${fmt4(loopTop)} ${fmt4(
|
|
21929
23168
|
x2 + 12
|
|
21930
|
-
)} ${
|
|
23169
|
+
)} ${fmt4(loopTop)} ${fmt4(x2)} ${fmt4(topY)}`;
|
|
21931
23170
|
const result = {
|
|
21932
23171
|
linkIndex: idx,
|
|
21933
23172
|
from: l.from,
|
|
@@ -21954,7 +23193,7 @@ function boxExit(node, ux, uy) {
|
|
|
21954
23193
|
return { x: node.cx + ux * t, y: node.cy + uy * t };
|
|
21955
23194
|
}
|
|
21956
23195
|
function layoutGlyph(loop, nodes, center) {
|
|
21957
|
-
const
|
|
23196
|
+
const C3 = CAUSALLOOP_CONST;
|
|
21958
23197
|
const pts = loop.variables.map((id) => nodes.get(id));
|
|
21959
23198
|
let sx = 0;
|
|
21960
23199
|
let sy = 0;
|
|
@@ -21994,10 +23233,10 @@ function layoutGlyph(loop, nodes, center) {
|
|
|
21994
23233
|
loop,
|
|
21995
23234
|
cx: gx,
|
|
21996
23235
|
cy: gy,
|
|
21997
|
-
r:
|
|
23236
|
+
r: C3.GLYPH_R,
|
|
21998
23237
|
clockwise,
|
|
21999
23238
|
phraseX: gx,
|
|
22000
|
-
phraseY: gy +
|
|
23239
|
+
phraseY: gy + C3.GLYPH_R + 13
|
|
22001
23240
|
};
|
|
22002
23241
|
}
|
|
22003
23242
|
function maxHalfW(ids, nodes) {
|
|
@@ -22005,7 +23244,7 @@ function maxHalfW(ids, nodes) {
|
|
|
22005
23244
|
for (const id of ids) m = Math.max(m, nodes.get(id).halfW);
|
|
22006
23245
|
return m;
|
|
22007
23246
|
}
|
|
22008
|
-
function
|
|
23247
|
+
function fmt4(n) {
|
|
22009
23248
|
return (Math.round(n * 100) / 100).toString();
|
|
22010
23249
|
}
|
|
22011
23250
|
|
|
@@ -22052,7 +23291,7 @@ function renderCausalLoopLayout(layout, config) {
|
|
|
22052
23291
|
);
|
|
22053
23292
|
const children = [
|
|
22054
23293
|
title(a11y),
|
|
22055
|
-
desc(
|
|
23294
|
+
desc(summarise8(layout)),
|
|
22056
23295
|
styleBlock,
|
|
22057
23296
|
el("rect", { x: 0, y: 0, width, height, class: "sx-cld-bg" })
|
|
22058
23297
|
];
|
|
@@ -22160,9 +23399,9 @@ function arrowhead(x, y, tx, ty) {
|
|
|
22160
23399
|
const by = y - ty * len;
|
|
22161
23400
|
const px = -ty;
|
|
22162
23401
|
const py = tx;
|
|
22163
|
-
const p1 = `${
|
|
22164
|
-
const p2 = `${
|
|
22165
|
-
const tip = `${
|
|
23402
|
+
const p1 = `${fmt5(bx + px * wide)},${fmt5(by + py * wide)}`;
|
|
23403
|
+
const p2 = `${fmt5(bx - px * wide)},${fmt5(by - py * wide)}`;
|
|
23404
|
+
const tip = `${fmt5(x)},${fmt5(y)}`;
|
|
22166
23405
|
return polygon({ points: `${tip} ${p1} ${p2}`, class: "sx-cld-arrow" });
|
|
22167
23406
|
}
|
|
22168
23407
|
function renderGlyph(g) {
|
|
@@ -22176,7 +23415,7 @@ function renderGlyph(g) {
|
|
|
22176
23415
|
const ey = g.cy + r7 * Math.sin(endAng);
|
|
22177
23416
|
const largeArc = 1;
|
|
22178
23417
|
const sweepFlag = g.clockwise ? 1 : 0;
|
|
22179
|
-
const arc = `M ${
|
|
23418
|
+
const arc = `M ${fmt5(sx)} ${fmt5(sy)} A ${r7} ${r7} 0 ${largeArc} ${sweepFlag} ${fmt5(ex)} ${fmt5(ey)}`;
|
|
22180
23419
|
const dir = g.clockwise ? 1 : -1;
|
|
22181
23420
|
const tx = -Math.sin(endAng) * dir;
|
|
22182
23421
|
const ty = Math.cos(endAng) * dir;
|
|
@@ -22217,11 +23456,11 @@ function glyphArrow(x, y, tx, ty) {
|
|
|
22217
23456
|
const by = y - ty * len;
|
|
22218
23457
|
const px = -ty;
|
|
22219
23458
|
const py = tx;
|
|
22220
|
-
const p1 = `${
|
|
22221
|
-
const p2 = `${
|
|
22222
|
-
return polygon({ points: `${
|
|
23459
|
+
const p1 = `${fmt5(bx + px * wide)},${fmt5(by + py * wide)}`;
|
|
23460
|
+
const p2 = `${fmt5(bx - px * wide)},${fmt5(by - py * wide)}`;
|
|
23461
|
+
return polygon({ points: `${fmt5(x)},${fmt5(y)} ${p1} ${p2}`, class: "sx-cld-glyph-head" });
|
|
22223
23462
|
}
|
|
22224
|
-
function
|
|
23463
|
+
function summarise8(layout) {
|
|
22225
23464
|
const { ast, analysis } = layout;
|
|
22226
23465
|
const parts = [
|
|
22227
23466
|
`Causal loop diagram${ast.title ? ` "${ast.title}"` : ""}: ${ast.variables.length} variable${ast.variables.length === 1 ? "" : "s"}, ${ast.links.length} causal link${ast.links.length === 1 ? "" : "s"}.`
|
|
@@ -22247,7 +23486,7 @@ function summarise7(layout) {
|
|
|
22247
23486
|
for (const w of ast.warnings) parts.push(w);
|
|
22248
23487
|
return parts.join(" ");
|
|
22249
23488
|
}
|
|
22250
|
-
function
|
|
23489
|
+
function fmt5(n) {
|
|
22251
23490
|
return (Math.round(n * 100) / 100).toString();
|
|
22252
23491
|
}
|
|
22253
23492
|
|
|
@@ -22475,11 +23714,11 @@ function analyseMarkov(ast) {
|
|
|
22475
23714
|
if (Math.abs(sum - 1) > ROW_SUM_TOL) {
|
|
22476
23715
|
if (ast.normalize) {
|
|
22477
23716
|
for (let j = 0; j < n; j++) P[i][j] /= sum;
|
|
22478
|
-
notes.push(`State "${order[i]}" out-edges summed to ${
|
|
23717
|
+
notes.push(`State "${order[i]}" out-edges summed to ${round9(sum)}; normalised to 1 (normalize: true).`);
|
|
22479
23718
|
} else {
|
|
22480
23719
|
const st = ast.states[i];
|
|
22481
23720
|
throw new MarkovParseError(
|
|
22482
|
-
`state "${order[i]}": outgoing probabilities sum to ${
|
|
23721
|
+
`state "${order[i]}": outgoing probabilities sum to ${round9(sum)}, must be 1.0 \u2014 fix the values or set "normalize: true"`,
|
|
22483
23722
|
st.line
|
|
22484
23723
|
);
|
|
22485
23724
|
}
|
|
@@ -22523,17 +23762,17 @@ function classifyStates(P, order, ast) {
|
|
|
22523
23762
|
if (indexOf[s] !== -1) continue;
|
|
22524
23763
|
const work = [{ v: s, pi: 0 }];
|
|
22525
23764
|
while (work.length > 0) {
|
|
22526
|
-
const
|
|
22527
|
-
const v =
|
|
22528
|
-
if (
|
|
23765
|
+
const frame2 = work[work.length - 1];
|
|
23766
|
+
const v = frame2.v;
|
|
23767
|
+
if (frame2.pi === 0) {
|
|
22529
23768
|
indexOf[v] = lowlink[v] = counter++;
|
|
22530
23769
|
stack.push(v);
|
|
22531
23770
|
onStack[v] = true;
|
|
22532
23771
|
}
|
|
22533
23772
|
let recursed = false;
|
|
22534
|
-
while (
|
|
22535
|
-
const w = adj[v][
|
|
22536
|
-
|
|
23773
|
+
while (frame2.pi < adj[v].length) {
|
|
23774
|
+
const w = adj[v][frame2.pi];
|
|
23775
|
+
frame2.pi++;
|
|
22537
23776
|
if (indexOf[w] === -1) {
|
|
22538
23777
|
work.push({ v: w, pi: 0 });
|
|
22539
23778
|
recursed = true;
|
|
@@ -22812,7 +24051,7 @@ function invert(mat) {
|
|
|
22812
24051
|
}
|
|
22813
24052
|
return A.map((row) => row.slice(m));
|
|
22814
24053
|
}
|
|
22815
|
-
function
|
|
24054
|
+
function round9(x) {
|
|
22816
24055
|
return Math.round(x * 1e6) / 1e6;
|
|
22817
24056
|
}
|
|
22818
24057
|
|
|
@@ -22836,7 +24075,7 @@ var MARKOV_CONST = {
|
|
|
22836
24075
|
};
|
|
22837
24076
|
function layoutMarkov(ast) {
|
|
22838
24077
|
const analysis = analyseMarkov(ast);
|
|
22839
|
-
const
|
|
24078
|
+
const C3 = MARKOV_CONST;
|
|
22840
24079
|
const states = ast.states;
|
|
22841
24080
|
states.length;
|
|
22842
24081
|
const classOf = analysis.classification?.byState;
|
|
@@ -22849,9 +24088,9 @@ function layoutMarkov(ast) {
|
|
|
22849
24088
|
let width = 0;
|
|
22850
24089
|
let height = 0;
|
|
22851
24090
|
if (ast.layout === "layered" && analysis.classification) {
|
|
22852
|
-
({ width, height } = placeLayered(ast, analysis.classification, centres,
|
|
24091
|
+
({ width, height } = placeLayered(ast, analysis.classification, centres, C3));
|
|
22853
24092
|
} else {
|
|
22854
|
-
({ width, height } = placeRing(states.map((s) => s.id), centres,
|
|
24093
|
+
({ width, height } = placeRing(states.map((s) => s.id), centres, C3));
|
|
22855
24094
|
}
|
|
22856
24095
|
const ringCenter = { x: width / 2, y: height / 2 };
|
|
22857
24096
|
const boxes = states.map((st) => {
|
|
@@ -22863,7 +24102,7 @@ function layoutMarkov(ast) {
|
|
|
22863
24102
|
state: st,
|
|
22864
24103
|
cx: c.x,
|
|
22865
24104
|
cy: c.y,
|
|
22866
|
-
r:
|
|
24105
|
+
r: C3.STATE_R,
|
|
22867
24106
|
isAbsorbing
|
|
22868
24107
|
};
|
|
22869
24108
|
if (tag) box2.classTag = tag;
|
|
@@ -22875,18 +24114,18 @@ function layoutMarkov(ast) {
|
|
|
22875
24114
|
for (const tr of ast.transitions) present.add(arcKey(tr.from, tr.to));
|
|
22876
24115
|
const arcs = ast.transitions.map((tr) => {
|
|
22877
24116
|
if (tr.self) {
|
|
22878
|
-
return selfLoopGeom(tr, centres.get(tr.from), ringCenter, ast.layout,
|
|
24117
|
+
return selfLoopGeom(tr, centres.get(tr.from), ringCenter, ast.layout, C3);
|
|
22879
24118
|
}
|
|
22880
24119
|
const a = centres.get(tr.from);
|
|
22881
24120
|
const b = centres.get(tr.to);
|
|
22882
24121
|
const hasReverse = present.has(arcKey(tr.to, tr.from));
|
|
22883
24122
|
const sign = hasReverse ? tr.from < tr.to ? 1 : -1 : 1;
|
|
22884
|
-
const bow = hasReverse ?
|
|
22885
|
-
return curvedArcGeom(tr, a, b, sign * bow,
|
|
24123
|
+
const bow = hasReverse ? C3.BOW : C3.SOLO_BOW;
|
|
24124
|
+
return curvedArcGeom(tr, a, b, sign * bow, C3);
|
|
22886
24125
|
});
|
|
22887
24126
|
return {
|
|
22888
24127
|
width,
|
|
22889
|
-
height: height + (ast.title ?
|
|
24128
|
+
height: height + (ast.title ? C3.TITLE_BAND : 0),
|
|
22890
24129
|
...ast.title ? { title: ast.title } : {},
|
|
22891
24130
|
states: boxes,
|
|
22892
24131
|
arcs,
|
|
@@ -22894,28 +24133,28 @@ function layoutMarkov(ast) {
|
|
|
22894
24133
|
ast
|
|
22895
24134
|
};
|
|
22896
24135
|
}
|
|
22897
|
-
function placeRing(ids, centres,
|
|
24136
|
+
function placeRing(ids, centres, C3) {
|
|
22898
24137
|
const n = ids.length;
|
|
22899
24138
|
if (n === 1) {
|
|
22900
|
-
const cx =
|
|
22901
|
-
const cy =
|
|
24139
|
+
const cx = C3.PADDING + C3.STATE_R + C3.LOOP_OUT;
|
|
24140
|
+
const cy = C3.PADDING + C3.STATE_R + C3.LOOP_OUT;
|
|
22902
24141
|
centres.set(ids[0], { x: cx, y: cy });
|
|
22903
24142
|
const dim2 = 2 * cx;
|
|
22904
24143
|
return { width: dim2, height: dim2 };
|
|
22905
24144
|
}
|
|
22906
24145
|
if (n === 2) {
|
|
22907
|
-
const cy =
|
|
22908
|
-
const dx = 2 *
|
|
22909
|
-
const cx02 =
|
|
24146
|
+
const cy = C3.PADDING + C3.STATE_R + C3.LOOP_OUT;
|
|
24147
|
+
const dx = 2 * C3.STATE_R + C3.RING_GAP + 60;
|
|
24148
|
+
const cx02 = C3.PADDING + C3.STATE_R + C3.LOOP_OUT;
|
|
22910
24149
|
centres.set(ids[0], { x: cx02, y: cy });
|
|
22911
24150
|
centres.set(ids[1], { x: cx02 + dx, y: cy });
|
|
22912
|
-
const width = cx02 + dx +
|
|
22913
|
-
const height = cy +
|
|
24151
|
+
const width = cx02 + dx + C3.STATE_R + C3.LOOP_OUT + C3.PADDING;
|
|
24152
|
+
const height = cy + C3.STATE_R + C3.LOOP_OUT + C3.PADDING;
|
|
22914
24153
|
return { width, height };
|
|
22915
24154
|
}
|
|
22916
|
-
const minArc = 2 *
|
|
22917
|
-
const radius = Math.max(minArc / (2 * Math.sin(Math.PI / n)), 2 *
|
|
22918
|
-
const margin =
|
|
24155
|
+
const minArc = 2 * C3.STATE_R + C3.RING_GAP;
|
|
24156
|
+
const radius = Math.max(minArc / (2 * Math.sin(Math.PI / n)), 2 * C3.STATE_R);
|
|
24157
|
+
const margin = C3.PADDING + C3.STATE_R + C3.LOOP_OUT;
|
|
22919
24158
|
const cx0 = margin + radius;
|
|
22920
24159
|
const cy0 = margin + radius;
|
|
22921
24160
|
ids.forEach((id, i) => {
|
|
@@ -22928,7 +24167,7 @@ function placeRing(ids, centres, C2) {
|
|
|
22928
24167
|
const dim = 2 * (radius + margin);
|
|
22929
24168
|
return { width: dim, height: dim };
|
|
22930
24169
|
}
|
|
22931
|
-
function placeLayered(ast, classification, centres,
|
|
24170
|
+
function placeLayered(ast, classification, centres, C3) {
|
|
22932
24171
|
const tier = (id) => {
|
|
22933
24172
|
const k = classification.byState[id];
|
|
22934
24173
|
if (k === "absorbing") return 2;
|
|
@@ -22938,18 +24177,18 @@ function placeLayered(ast, classification, centres, C2) {
|
|
|
22938
24177
|
const cols = [[], [], []];
|
|
22939
24178
|
for (const st of ast.states) cols[tier(st.id)].push(st.id);
|
|
22940
24179
|
const maxRows = Math.max(1, ...cols.map((c) => c.length));
|
|
22941
|
-
const margin =
|
|
24180
|
+
const margin = C3.PADDING + C3.STATE_R + C3.LOOP_OUT;
|
|
22942
24181
|
cols.forEach((col, ci) => {
|
|
22943
|
-
const x = margin + ci *
|
|
22944
|
-
const totalH = (col.length - 1) *
|
|
22945
|
-
const startY = margin + ((maxRows - 1) *
|
|
22946
|
-
col.forEach((id, ri) => centres.set(id, { x, y: startY + ri *
|
|
24182
|
+
const x = margin + ci * C3.TIER_DX;
|
|
24183
|
+
const totalH = (col.length - 1) * C3.TIER_DY;
|
|
24184
|
+
const startY = margin + ((maxRows - 1) * C3.TIER_DY - totalH) / 2;
|
|
24185
|
+
col.forEach((id, ri) => centres.set(id, { x, y: startY + ri * C3.TIER_DY }));
|
|
22947
24186
|
});
|
|
22948
|
-
const width = margin + 2 *
|
|
22949
|
-
const height = margin + (maxRows - 1) *
|
|
24187
|
+
const width = margin + 2 * C3.TIER_DX + C3.STATE_R + C3.LOOP_OUT + C3.PADDING;
|
|
24188
|
+
const height = margin + (maxRows - 1) * C3.TIER_DY + C3.STATE_R + C3.LOOP_OUT + C3.PADDING;
|
|
22950
24189
|
return { width, height };
|
|
22951
24190
|
}
|
|
22952
|
-
function curvedArcGeom(tr, a, b, bow,
|
|
24191
|
+
function curvedArcGeom(tr, a, b, bow, C3) {
|
|
22953
24192
|
const dx = b.x - a.x;
|
|
22954
24193
|
const dy = b.y - a.y;
|
|
22955
24194
|
const len = Math.hypot(dx, dy) || 1;
|
|
@@ -22962,8 +24201,8 @@ function curvedArcGeom(tr, a, b, bow, C2) {
|
|
|
22962
24201
|
x: (a.x + b.x) / 2 + nx * bow,
|
|
22963
24202
|
y: (a.y + b.y) / 2 + ny * bow
|
|
22964
24203
|
};
|
|
22965
|
-
const start = aimToBoundary(a, mid,
|
|
22966
|
-
const end = aimToBoundary(b, mid,
|
|
24204
|
+
const start = aimToBoundary(a, mid, C3.STATE_R);
|
|
24205
|
+
const end = aimToBoundary(b, mid, C3.STATE_R);
|
|
22967
24206
|
const c1 = { x: (start.x + mid.x) / 2, y: (start.y + mid.y) / 2 };
|
|
22968
24207
|
const c2 = { x: (end.x + mid.x) / 2, y: (end.y + mid.y) / 2 };
|
|
22969
24208
|
return {
|
|
@@ -22980,7 +24219,7 @@ function aimToBoundary(centre, target, r7) {
|
|
|
22980
24219
|
const len = Math.hypot(dx, dy) || 1;
|
|
22981
24220
|
return { x: centre.x + dx / len * r7, y: centre.y + dy / len * r7 };
|
|
22982
24221
|
}
|
|
22983
|
-
function selfLoopGeom(tr, c, ringCenter, layout,
|
|
24222
|
+
function selfLoopGeom(tr, c, ringCenter, layout, C3) {
|
|
22984
24223
|
let ox = c.x - ringCenter.x;
|
|
22985
24224
|
let oy = c.y - ringCenter.y;
|
|
22986
24225
|
if (layout === "layered" || ox === 0 && oy === 0) {
|
|
@@ -22992,26 +24231,26 @@ function selfLoopGeom(tr, c, ringCenter, layout, C2) {
|
|
|
22992
24231
|
const uy = oy / ol;
|
|
22993
24232
|
const tx = -uy;
|
|
22994
24233
|
const ty = ux;
|
|
22995
|
-
const spread =
|
|
24234
|
+
const spread = C3.STATE_R * 0.6;
|
|
22996
24235
|
const foot1 = {
|
|
22997
|
-
x: c.x + ux *
|
|
22998
|
-
y: c.y + uy *
|
|
24236
|
+
x: c.x + ux * C3.STATE_R * 0.85 - tx * spread,
|
|
24237
|
+
y: c.y + uy * C3.STATE_R * 0.85 - ty * spread
|
|
22999
24238
|
};
|
|
23000
24239
|
const foot2 = {
|
|
23001
|
-
x: c.x + ux *
|
|
23002
|
-
y: c.y + uy *
|
|
24240
|
+
x: c.x + ux * C3.STATE_R * 0.85 + tx * spread,
|
|
24241
|
+
y: c.y + uy * C3.STATE_R * 0.85 + ty * spread
|
|
23003
24242
|
};
|
|
23004
24243
|
const apex = {
|
|
23005
|
-
x: c.x + ux * (
|
|
23006
|
-
y: c.y + uy * (
|
|
24244
|
+
x: c.x + ux * (C3.STATE_R + C3.LOOP_OUT),
|
|
24245
|
+
y: c.y + uy * (C3.STATE_R + C3.LOOP_OUT)
|
|
23007
24246
|
};
|
|
23008
24247
|
const ctrl1 = {
|
|
23009
|
-
x: foot1.x + ux *
|
|
23010
|
-
y: foot1.y + uy *
|
|
24248
|
+
x: foot1.x + ux * C3.LOOP_OUT - tx * C3.LOOP_R,
|
|
24249
|
+
y: foot1.y + uy * C3.LOOP_OUT - ty * C3.LOOP_R
|
|
23011
24250
|
};
|
|
23012
24251
|
const ctrl2 = {
|
|
23013
|
-
x: foot2.x + ux *
|
|
23014
|
-
y: foot2.y + uy *
|
|
24252
|
+
x: foot2.x + ux * C3.LOOP_OUT + tx * C3.LOOP_R,
|
|
24253
|
+
y: foot2.y + uy * C3.LOOP_OUT + ty * C3.LOOP_R
|
|
23015
24254
|
};
|
|
23016
24255
|
return {
|
|
23017
24256
|
transition: tr,
|
|
@@ -23088,7 +24327,7 @@ function renderState2(sb) {
|
|
|
23088
24327
|
parts.push(
|
|
23089
24328
|
text(
|
|
23090
24329
|
{ class: "sx-markov-pi", x: sb.cx, y: sb.cy + sb.r + 14, "text-anchor": "middle" },
|
|
23091
|
-
`\u03C0=${
|
|
24330
|
+
`\u03C0=${fmt6(sb.pi)}`
|
|
23092
24331
|
)
|
|
23093
24332
|
);
|
|
23094
24333
|
}
|
|
@@ -23097,7 +24336,7 @@ function renderState2(sb) {
|
|
|
23097
24336
|
"data-id": sb.state.id
|
|
23098
24337
|
};
|
|
23099
24338
|
if (sb.classTag) attrs["data-class"] = sb.classTag;
|
|
23100
|
-
if (sb.pi !== void 0) attrs["data-pi"] =
|
|
24339
|
+
if (sb.pi !== void 0) attrs["data-pi"] = fmt6(sb.pi);
|
|
23101
24340
|
return group(attrs, parts);
|
|
23102
24341
|
}
|
|
23103
24342
|
function arcPath2(ag) {
|
|
@@ -23110,7 +24349,7 @@ function renderArc3(ag) {
|
|
|
23110
24349
|
path({ class: cls, d: arcPath2(ag), "marker-end": "url(#sx-markov-head)" }),
|
|
23111
24350
|
text(
|
|
23112
24351
|
{ class: "sx-markov-prob", x: ag.labelX, y: ag.labelY + 3, "text-anchor": "middle" },
|
|
23113
|
-
|
|
24352
|
+
fmt6(ag.transition.probability)
|
|
23114
24353
|
)
|
|
23115
24354
|
];
|
|
23116
24355
|
return group(
|
|
@@ -23118,7 +24357,7 @@ function renderArc3(ag) {
|
|
|
23118
24357
|
class: ag.self ? "sx-markov-arc-g sx-markov-arc-self-g" : "sx-markov-arc-g",
|
|
23119
24358
|
"data-from": ag.transition.from,
|
|
23120
24359
|
"data-to": ag.transition.to,
|
|
23121
|
-
"data-prob":
|
|
24360
|
+
"data-prob": fmt6(ag.transition.probability)
|
|
23122
24361
|
},
|
|
23123
24362
|
parts
|
|
23124
24363
|
);
|
|
@@ -23139,11 +24378,11 @@ function buildDesc(layout) {
|
|
|
23139
24378
|
}
|
|
23140
24379
|
if (a.stationary) {
|
|
23141
24380
|
if (a.stationary.unique) {
|
|
23142
|
-
const entries = layout.analysis.order.filter((id) => a.stationary.pi[id] !== void 0).map((id) => `${id}=${
|
|
24381
|
+
const entries = layout.analysis.order.filter((id) => a.stationary.pi[id] !== void 0).map((id) => `${id}=${fmt6(a.stationary.pi[id])}`);
|
|
23143
24382
|
out.push(`Stationary \u03C0: { ${entries.join(", ")} }.`);
|
|
23144
24383
|
} else if (a.stationary.perClass.length) {
|
|
23145
24384
|
const blocks = a.stationary.perClass.map((pc2) => {
|
|
23146
|
-
const e = pc2.states.map((id) => `${id}=${
|
|
24385
|
+
const e = pc2.states.map((id) => `${id}=${fmt6(pc2.pi[id])}`).join(", ");
|
|
23147
24386
|
return `{ ${e} }`;
|
|
23148
24387
|
});
|
|
23149
24388
|
out.push(`Stationary \u03C0 (per recurrent class, not globally unique): ${blocks.join("; ")}.`);
|
|
@@ -23151,10 +24390,10 @@ function buildDesc(layout) {
|
|
|
23151
24390
|
}
|
|
23152
24391
|
if (a.absorbing) {
|
|
23153
24392
|
const ab = a.absorbing;
|
|
23154
|
-
const tParts = ab.transient.map((id, i) => `${id}=${
|
|
24393
|
+
const tParts = ab.transient.map((id, i) => `${id}=${fmt6(ab.t[i])}`);
|
|
23155
24394
|
out.push(`Expected steps to absorption t: { ${tParts.join(", ")} }.`);
|
|
23156
24395
|
const bRows = ab.transient.map((from, i) => {
|
|
23157
|
-
const probs = ab.absorbing.map((to, j) => `${to}=${
|
|
24396
|
+
const probs = ab.absorbing.map((to, j) => `${to}=${fmt6(ab.B[i][j])}`).join(", ");
|
|
23158
24397
|
return `${from}\u2192(${probs})`;
|
|
23159
24398
|
});
|
|
23160
24399
|
out.push(`Absorption probabilities B: ${bRows.join("; ")}.`);
|
|
@@ -23210,7 +24449,7 @@ function renderMarkov(textOrAst, config) {
|
|
|
23210
24449
|
const layout = layoutMarkov(ast);
|
|
23211
24450
|
return renderMarkovLayout(layout, config);
|
|
23212
24451
|
}
|
|
23213
|
-
function
|
|
24452
|
+
function fmt6(x) {
|
|
23214
24453
|
if (!Number.isFinite(x)) return String(x);
|
|
23215
24454
|
const r7 = Math.round(x * 1e3) / 1e3;
|
|
23216
24455
|
return String(r7);
|
|
@@ -23256,7 +24495,7 @@ function parseGitGraph(text2) {
|
|
|
23256
24495
|
i = skipBlankAndDirectives(rawLines, i, ast);
|
|
23257
24496
|
let headerSeen = false;
|
|
23258
24497
|
while (i < rawLines.length) {
|
|
23259
|
-
const t =
|
|
24498
|
+
const t = stripComment10(rawLines[i] ?? "").trim();
|
|
23260
24499
|
if (t === "") {
|
|
23261
24500
|
i++;
|
|
23262
24501
|
continue;
|
|
@@ -23264,7 +24503,7 @@ function parseGitGraph(text2) {
|
|
|
23264
24503
|
const h = /^gitgraph\b\s*:?\s*(.*)$/i.exec(t);
|
|
23265
24504
|
if (!h) {
|
|
23266
24505
|
throw new GitGraphParseError(
|
|
23267
|
-
`expected a 'gitGraph' header, got: ${
|
|
24506
|
+
`expected a 'gitGraph' header, got: ${truncate9(t)}`,
|
|
23268
24507
|
i + 1
|
|
23269
24508
|
);
|
|
23270
24509
|
}
|
|
@@ -23283,7 +24522,7 @@ function parseGitGraph(text2) {
|
|
|
23283
24522
|
throw new GitGraphParseError("empty gitGraph (no header)");
|
|
23284
24523
|
}
|
|
23285
24524
|
for (; i < rawLines.length; i++) {
|
|
23286
|
-
const t =
|
|
24525
|
+
const t = stripComment10(rawLines[i] ?? "").trim();
|
|
23287
24526
|
if (t === "") continue;
|
|
23288
24527
|
if (/^%%\{/.test(t)) {
|
|
23289
24528
|
applyInit(t, ast);
|
|
@@ -23292,7 +24531,7 @@ function parseGitGraph(text2) {
|
|
|
23292
24531
|
const op = parseOperation2(t, i + 1);
|
|
23293
24532
|
if (op) ast.operations.push(op);
|
|
23294
24533
|
}
|
|
23295
|
-
|
|
24534
|
+
validate6(ast);
|
|
23296
24535
|
return ast;
|
|
23297
24536
|
}
|
|
23298
24537
|
function parseOperation2(line2, lineNo) {
|
|
@@ -23334,7 +24573,7 @@ function parseOptions(rest, lineNo) {
|
|
|
23334
24573
|
const keyMatch = /^([A-Za-z]+)\s*:/.exec(s.slice(i));
|
|
23335
24574
|
if (!keyMatch) {
|
|
23336
24575
|
throw new GitGraphParseError(
|
|
23337
|
-
`unexpected token near '${
|
|
24576
|
+
`unexpected token near '${truncate9(s.slice(i))}' (expected key: value)`,
|
|
23338
24577
|
lineNo
|
|
23339
24578
|
);
|
|
23340
24579
|
}
|
|
@@ -23449,7 +24688,7 @@ function parseCherryPick(rest, lineNo) {
|
|
|
23449
24688
|
}
|
|
23450
24689
|
return { kind: "cherry-pick", id: o.id, tag: o.tag, parent: o.parent, line: lineNo };
|
|
23451
24690
|
}
|
|
23452
|
-
function
|
|
24691
|
+
function validate6(ast) {
|
|
23453
24692
|
const firstOp = ast.operations[0];
|
|
23454
24693
|
if (firstOp && firstOp.kind !== "commit" && firstOp.kind !== "branch") ;
|
|
23455
24694
|
}
|
|
@@ -23470,7 +24709,7 @@ function parseFrontmatter2(lines, start, ast) {
|
|
|
23470
24709
|
function skipBlankAndDirectives(lines, start, ast) {
|
|
23471
24710
|
let i = start;
|
|
23472
24711
|
for (; i < lines.length; i++) {
|
|
23473
|
-
const t =
|
|
24712
|
+
const t = stripComment10(lines[i] ?? "").trim();
|
|
23474
24713
|
if (t === "") continue;
|
|
23475
24714
|
if (/^%%\{/.test(t)) {
|
|
23476
24715
|
applyInit(t, ast);
|
|
@@ -23503,7 +24742,7 @@ function applyConfigLines(lines, ast) {
|
|
|
23503
24742
|
if (tv && !/^(LR|TB|BT)$/i.test(tv)) ast.title = tv;
|
|
23504
24743
|
}
|
|
23505
24744
|
}
|
|
23506
|
-
function
|
|
24745
|
+
function stripComment10(line2) {
|
|
23507
24746
|
const idx = line2.indexOf("%%");
|
|
23508
24747
|
if (idx >= 0 && line2.slice(idx, idx + 3) !== "%%{") {
|
|
23509
24748
|
return line2.slice(0, idx);
|
|
@@ -23513,7 +24752,7 @@ function stripComment9(line2) {
|
|
|
23513
24752
|
function isQuote(ch) {
|
|
23514
24753
|
return ch === '"' || ch === "'" || ch === "\u201C" || ch === "\u2018" || ch === "\u300C" || ch === "\u300E" || ch === "\xAB";
|
|
23515
24754
|
}
|
|
23516
|
-
function
|
|
24755
|
+
function truncate9(s) {
|
|
23517
24756
|
return s.length > 40 ? `${s.slice(0, 40)}\u2026` : s;
|
|
23518
24757
|
}
|
|
23519
24758
|
|
|
@@ -23719,18 +24958,18 @@ function assignLanes(ast, state2) {
|
|
|
23719
24958
|
}
|
|
23720
24959
|
function layoutGitGraph(ast) {
|
|
23721
24960
|
const replay = replayGitGraph(ast);
|
|
23722
|
-
const
|
|
24961
|
+
const C3 = GITGRAPH_CONST;
|
|
23723
24962
|
const branchByName = new Map(replay.branches.map((b) => [b.name, b]));
|
|
23724
24963
|
const laneCount = replay.branches.length;
|
|
23725
24964
|
const commitCount = replay.commits.length;
|
|
23726
|
-
const headBand = ast.showBranches ?
|
|
23727
|
-
const timeAt = (s) => headBand +
|
|
23728
|
-
const crossAt = (l) =>
|
|
24965
|
+
const headBand = ast.showBranches ? C3.PILL_GUTTER : C3.LEAD_IN;
|
|
24966
|
+
const timeAt = (s) => headBand + C3.LEAD_IN + s * C3.TIME_STEP;
|
|
24967
|
+
const crossAt = (l) => C3.PAD + C3.TAG_BAND + C3.LANE_GAP / 2 + l * C3.LANE_GAP;
|
|
23729
24968
|
const timeSpan = commitCount > 0 ? timeAt(commitCount - 1) : timeAt(0);
|
|
23730
24969
|
const crossSpan = crossAt(Math.max(0, laneCount - 1));
|
|
23731
24970
|
const isVertical = ast.orientation === "TB" || ast.orientation === "BT";
|
|
23732
|
-
const timeMax = timeSpan +
|
|
23733
|
-
const crossMax = crossSpan +
|
|
24971
|
+
const timeMax = timeSpan + C3.TIME_STEP / 2 + (ast.showCommitLabel ? C3.LABEL_BAND : C3.PAD);
|
|
24972
|
+
const crossMax = crossSpan + C3.LANE_GAP / 2 + C3.PAD;
|
|
23734
24973
|
const width = isVertical ? crossMax : timeMax;
|
|
23735
24974
|
const height = isVertical ? timeMax : crossMax;
|
|
23736
24975
|
const place = (t, c) => {
|
|
@@ -23761,12 +25000,12 @@ function layoutGitGraph(ast) {
|
|
|
23761
25000
|
}
|
|
23762
25001
|
const firstOwn = own[0];
|
|
23763
25002
|
const isTrunk = !firstOwn || firstOwn.parents.length === 0;
|
|
23764
|
-
const startT = isTrunk ? timeAt(startSeq) -
|
|
25003
|
+
const startT = isTrunk ? timeAt(startSeq) - C3.TIME_STEP / 2 : timeAt(startSeq);
|
|
23765
25004
|
const endT = timeAt(endSeq);
|
|
23766
25005
|
const c = crossAt(info.lane);
|
|
23767
25006
|
const head = place(startT, c);
|
|
23768
25007
|
const tail = place(endT, c);
|
|
23769
|
-
const pillT = headBand -
|
|
25008
|
+
const pillT = headBand - C3.LEAD_IN / 2;
|
|
23770
25009
|
const pillPos = place(pillT, c);
|
|
23771
25010
|
return {
|
|
23772
25011
|
info,
|
|
@@ -23890,7 +25129,7 @@ function renderGitGraphLayout(layout, config) {
|
|
|
23890
25129
|
const styleBlock = buildStyle2(pal, ast.showBranches);
|
|
23891
25130
|
const children = [
|
|
23892
25131
|
title(a11y),
|
|
23893
|
-
desc(
|
|
25132
|
+
desc(summarise9(layout)),
|
|
23894
25133
|
styleBlock,
|
|
23895
25134
|
rect({ x: 0, y: 0, width, height, class: "sx-gg-bg" })
|
|
23896
25135
|
];
|
|
@@ -24140,7 +25379,7 @@ ${laneRules}
|
|
|
24140
25379
|
function num2(n) {
|
|
24141
25380
|
return Number.isInteger(n) ? String(n) : n.toFixed(2);
|
|
24142
25381
|
}
|
|
24143
|
-
function
|
|
25382
|
+
function summarise9(layout) {
|
|
24144
25383
|
const c = layout.replay.commits.length;
|
|
24145
25384
|
const b = layout.replay.branches.length;
|
|
24146
25385
|
const merges = layout.replay.commits.filter((n) => n.isMerge).length;
|
|
@@ -24432,7 +25671,7 @@ function parseEpc(text2) {
|
|
|
24432
25671
|
let i = 0;
|
|
24433
25672
|
let headerSeen = false;
|
|
24434
25673
|
while (i < rawLines.length) {
|
|
24435
|
-
const t =
|
|
25674
|
+
const t = stripComment11(rawLines[i] ?? "").trim();
|
|
24436
25675
|
if (t === "") {
|
|
24437
25676
|
i++;
|
|
24438
25677
|
continue;
|
|
@@ -24440,7 +25679,7 @@ function parseEpc(text2) {
|
|
|
24440
25679
|
const h = /^epc\b(.*)$/i.exec(t);
|
|
24441
25680
|
if (h) {
|
|
24442
25681
|
const after = h[1].trim();
|
|
24443
|
-
const q =
|
|
25682
|
+
const q = matchQuoted7(after);
|
|
24444
25683
|
if (q) ast.title = q.value;
|
|
24445
25684
|
headerSeen = true;
|
|
24446
25685
|
i++;
|
|
@@ -24451,11 +25690,11 @@ function parseEpc(text2) {
|
|
|
24451
25690
|
}
|
|
24452
25691
|
if (!headerSeen) return finish(ast);
|
|
24453
25692
|
for (; i < rawLines.length; i++) {
|
|
24454
|
-
const t =
|
|
25693
|
+
const t = stripComment11(rawLines[i] ?? "").trim();
|
|
24455
25694
|
if (t === "") continue;
|
|
24456
25695
|
const lineNo = i + 1;
|
|
24457
25696
|
if (/^layout\s*:/i.test(t)) {
|
|
24458
|
-
const v =
|
|
25697
|
+
const v = afterColon7(t).toLowerCase();
|
|
24459
25698
|
if (v === "tb" || v === "lr") ast.direction = v;
|
|
24460
25699
|
else ast.warnings.push(`Line ${lineNo}: unknown layout "${v}" \u2014 using "tb".`);
|
|
24461
25700
|
continue;
|
|
@@ -24469,7 +25708,7 @@ function parseEpc(text2) {
|
|
|
24469
25708
|
parseDeclLine(ast, kw[1].toLowerCase(), t.slice(kw[0].length).trim(), lineNo);
|
|
24470
25709
|
continue;
|
|
24471
25710
|
}
|
|
24472
|
-
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${
|
|
25711
|
+
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${truncate10(t, 80)}"`);
|
|
24473
25712
|
}
|
|
24474
25713
|
return finish(ast);
|
|
24475
25714
|
}
|
|
@@ -24503,13 +25742,13 @@ function parseEdgeLine(ast, line2, lineNo) {
|
|
|
24503
25742
|
const colon = topLevelColon(body);
|
|
24504
25743
|
if (colon >= 0) {
|
|
24505
25744
|
const labelPart = body.slice(colon + 1).trim();
|
|
24506
|
-
const q =
|
|
25745
|
+
const q = matchQuoted7(labelPart);
|
|
24507
25746
|
edgeLabel2 = q ? q.value : labelPart;
|
|
24508
25747
|
body = body.slice(0, colon).trim();
|
|
24509
25748
|
}
|
|
24510
25749
|
const parts = splitArrow(body).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
24511
25750
|
if (parts.length < 2) {
|
|
24512
|
-
throw new EpcParseError(`an edge needs at least two nodes around '->', got "${
|
|
25751
|
+
throw new EpcParseError(`an edge needs at least two nodes around '->', got "${truncate10(line2, 60)}"`, lineNo);
|
|
24513
25752
|
}
|
|
24514
25753
|
for (const p of parts) {
|
|
24515
25754
|
if (!isId2(p)) throw new EpcParseError(`invalid node reference "${p}" in edge`, lineNo);
|
|
@@ -24546,11 +25785,11 @@ function ensureRef(ast, id, _lineNo) {
|
|
|
24546
25785
|
}
|
|
24547
25786
|
function parseIdAndLabel3(s, lineNo) {
|
|
24548
25787
|
const m = /^([A-Za-z_]\w*)/.exec(s.trim());
|
|
24549
|
-
if (!m) throw new EpcParseError(`expected a node id, got "${
|
|
25788
|
+
if (!m) throw new EpcParseError(`expected a node id, got "${truncate10(s, 40)}"`, lineNo);
|
|
24550
25789
|
const id = m[1];
|
|
24551
25790
|
const rest = s.trim().slice(id.length).trim();
|
|
24552
25791
|
if (rest === "") return { id };
|
|
24553
|
-
const q =
|
|
25792
|
+
const q = matchQuoted7(rest);
|
|
24554
25793
|
if (q) return { id, label: q.value };
|
|
24555
25794
|
return { id, label: rest };
|
|
24556
25795
|
}
|
|
@@ -24573,7 +25812,7 @@ function splitArrow(s) {
|
|
|
24573
25812
|
}
|
|
24574
25813
|
if (ch === '"' || ch === "\u300C" || ch === "\u201C") {
|
|
24575
25814
|
inQ = true;
|
|
24576
|
-
qc =
|
|
25815
|
+
qc = closingQuote7(ch);
|
|
24577
25816
|
buf += ch;
|
|
24578
25817
|
continue;
|
|
24579
25818
|
}
|
|
@@ -24598,30 +25837,30 @@ function topLevelColon(s) {
|
|
|
24598
25837
|
}
|
|
24599
25838
|
if (ch === '"' || ch === "\u300C" || ch === "\u201C") {
|
|
24600
25839
|
inQ = true;
|
|
24601
|
-
qc =
|
|
25840
|
+
qc = closingQuote7(ch);
|
|
24602
25841
|
continue;
|
|
24603
25842
|
}
|
|
24604
25843
|
if (ch === ":") return i;
|
|
24605
25844
|
}
|
|
24606
25845
|
return -1;
|
|
24607
25846
|
}
|
|
24608
|
-
function
|
|
25847
|
+
function matchQuoted7(s) {
|
|
24609
25848
|
if (!s) return void 0;
|
|
24610
25849
|
const open = s[0];
|
|
24611
25850
|
if (open !== '"' && open !== "\u300C" && open !== "\u201C") return void 0;
|
|
24612
|
-
const close =
|
|
25851
|
+
const close = closingQuote7(open);
|
|
24613
25852
|
const end = s.indexOf(close, 1);
|
|
24614
25853
|
if (end < 0) return void 0;
|
|
24615
25854
|
return { value: s.slice(1, end), length: end + 1 };
|
|
24616
25855
|
}
|
|
24617
|
-
function
|
|
25856
|
+
function closingQuote7(open) {
|
|
24618
25857
|
return open === "\u300C" ? "\u300D" : open === "\u201C" ? "\u201D" : '"';
|
|
24619
25858
|
}
|
|
24620
|
-
function
|
|
25859
|
+
function afterColon7(s) {
|
|
24621
25860
|
const i = s.indexOf(":");
|
|
24622
25861
|
return i < 0 ? "" : s.slice(i + 1).trim();
|
|
24623
25862
|
}
|
|
24624
|
-
function
|
|
25863
|
+
function stripComment11(line2) {
|
|
24625
25864
|
let inQ = false, qc = "";
|
|
24626
25865
|
for (let i = 0; i < line2.length; i++) {
|
|
24627
25866
|
const ch = line2[i];
|
|
@@ -24631,7 +25870,7 @@ function stripComment10(line2) {
|
|
|
24631
25870
|
}
|
|
24632
25871
|
if (ch === '"' || ch === "\u300C" || ch === "\u201C") {
|
|
24633
25872
|
inQ = true;
|
|
24634
|
-
qc =
|
|
25873
|
+
qc = closingQuote7(ch);
|
|
24635
25874
|
continue;
|
|
24636
25875
|
}
|
|
24637
25876
|
if (ch === "#") return line2.slice(0, i);
|
|
@@ -24639,7 +25878,7 @@ function stripComment10(line2) {
|
|
|
24639
25878
|
}
|
|
24640
25879
|
return line2;
|
|
24641
25880
|
}
|
|
24642
|
-
function
|
|
25881
|
+
function truncate10(s, n) {
|
|
24643
25882
|
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
24644
25883
|
}
|
|
24645
25884
|
|
|
@@ -24981,7 +26220,7 @@ function renderEpcLayout(layout, config) {
|
|
|
24981
26220
|
);
|
|
24982
26221
|
const children = [
|
|
24983
26222
|
title(a11y),
|
|
24984
|
-
desc(
|
|
26223
|
+
desc(summarise10(layout)),
|
|
24985
26224
|
styleBlock,
|
|
24986
26225
|
defs([
|
|
24987
26226
|
el("marker", {
|
|
@@ -25125,7 +26364,7 @@ function renderEdge6(e) {
|
|
|
25125
26364
|
parts
|
|
25126
26365
|
);
|
|
25127
26366
|
}
|
|
25128
|
-
function
|
|
26367
|
+
function summarise10(layout) {
|
|
25129
26368
|
const { ast, analysis } = layout;
|
|
25130
26369
|
const counts = { event: 0, function: 0, connector: 0 };
|
|
25131
26370
|
for (const n of ast.nodes) counts[n.kind]++;
|
|
@@ -25220,12 +26459,12 @@ function parseIdef0(text2) {
|
|
|
25220
26459
|
let i = 0;
|
|
25221
26460
|
let headerSeen = false;
|
|
25222
26461
|
for (; i < rawLines.length; i++) {
|
|
25223
|
-
const t =
|
|
26462
|
+
const t = stripComment12(rawLines[i] ?? "").trim();
|
|
25224
26463
|
if (t === "") continue;
|
|
25225
26464
|
const h = /^idef0\b(.*)$/i.exec(t);
|
|
25226
26465
|
if (h) {
|
|
25227
26466
|
const after = h[1].trim();
|
|
25228
|
-
const q =
|
|
26467
|
+
const q = matchQuoted8(after);
|
|
25229
26468
|
if (q) ast.title = q.value;
|
|
25230
26469
|
headerSeen = true;
|
|
25231
26470
|
i++;
|
|
@@ -25236,7 +26475,7 @@ function parseIdef0(text2) {
|
|
|
25236
26475
|
throw new Idef0ParseError(`an idef0 diagram must start with the 'idef0' keyword`);
|
|
25237
26476
|
}
|
|
25238
26477
|
for (; i < rawLines.length; i++) {
|
|
25239
|
-
const t =
|
|
26478
|
+
const t = stripComment12(rawLines[i] ?? "").trim();
|
|
25240
26479
|
if (t === "") continue;
|
|
25241
26480
|
const lineNo = i + 1;
|
|
25242
26481
|
const nodeM = /^node\s+(.+)$/i.exec(t);
|
|
@@ -25247,7 +26486,7 @@ function parseIdef0(text2) {
|
|
|
25247
26486
|
const metaM = /^(purpose|viewpoint)\b\s*(.*)$/i.exec(t);
|
|
25248
26487
|
if (metaM) {
|
|
25249
26488
|
const key = metaM[1].toLowerCase();
|
|
25250
|
-
const q =
|
|
26489
|
+
const q = matchQuoted8(metaM[2].trim());
|
|
25251
26490
|
ast[key] = q ? q.value : metaM[2].trim();
|
|
25252
26491
|
continue;
|
|
25253
26492
|
}
|
|
@@ -25265,17 +26504,17 @@ function parseIdef0(text2) {
|
|
|
25265
26504
|
parseFlowArrow(ast, t, lineNo);
|
|
25266
26505
|
continue;
|
|
25267
26506
|
}
|
|
25268
|
-
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${
|
|
26507
|
+
ast.warnings.push(`Line ${lineNo}: unrecognised line: "${truncate11(t, 80)}"`);
|
|
25269
26508
|
}
|
|
25270
26509
|
return ast;
|
|
25271
26510
|
}
|
|
25272
26511
|
function parseFunction2(ast, rest, lineNo) {
|
|
25273
26512
|
const idM = /^([A-Za-z_]\w*)/.exec(rest);
|
|
25274
|
-
if (!idM) throw new Idef0ParseError(`function needs an id, got "${
|
|
26513
|
+
if (!idM) throw new Idef0ParseError(`function needs an id, got "${truncate11(rest, 40)}"`, lineNo);
|
|
25275
26514
|
const id = idM[1];
|
|
25276
26515
|
let tail = rest.slice(id.length).trim();
|
|
25277
26516
|
let name = id;
|
|
25278
|
-
const q =
|
|
26517
|
+
const q = matchQuoted8(tail);
|
|
25279
26518
|
if (q) {
|
|
25280
26519
|
name = q.value;
|
|
25281
26520
|
tail = tail.slice(q.length).trim();
|
|
@@ -25297,11 +26536,11 @@ function parseFunction2(ast, rest, lineNo) {
|
|
|
25297
26536
|
}
|
|
25298
26537
|
function parseRoleArrow(ast, role, rest, lineNo) {
|
|
25299
26538
|
const idM = /^([A-Za-z_]\w*)/.exec(rest);
|
|
25300
|
-
if (!idM) throw new Idef0ParseError(`${role} needs a box id, got "${
|
|
26539
|
+
if (!idM) throw new Idef0ParseError(`${role} needs a box id, got "${truncate11(rest, 40)}"`, lineNo);
|
|
25301
26540
|
const boxId = idM[1];
|
|
25302
26541
|
let tail = rest.slice(boxId.length).trim();
|
|
25303
26542
|
let label = "";
|
|
25304
|
-
const q =
|
|
26543
|
+
const q = matchQuoted8(tail);
|
|
25305
26544
|
if (q) {
|
|
25306
26545
|
label = q.value;
|
|
25307
26546
|
tail = tail.slice(q.length).trim();
|
|
@@ -25327,10 +26566,10 @@ function parseFlowArrow(ast, t, lineNo) {
|
|
|
25327
26566
|
const lhs = t.slice(0, arrowIdx).trim();
|
|
25328
26567
|
let rhs = t.slice(arrowIdx + 2).trim();
|
|
25329
26568
|
const srcM = /^([A-Za-z_]\w*)$/.exec(lhs);
|
|
25330
|
-
if (!srcM) throw new Idef0ParseError(`flow arrow source must be a box id, got "${
|
|
26569
|
+
if (!srcM) throw new Idef0ParseError(`flow arrow source must be a box id, got "${truncate11(lhs, 40)}"`, lineNo);
|
|
25331
26570
|
const srcId = srcM[1];
|
|
25332
26571
|
const tgtM = /^([A-Za-z_]\w*)(?:\.([a-z]+))?/i.exec(rhs);
|
|
25333
|
-
if (!tgtM) throw new Idef0ParseError(`flow arrow target must be a box id, got "${
|
|
26572
|
+
if (!tgtM) throw new Idef0ParseError(`flow arrow target must be a box id, got "${truncate11(rhs, 40)}"`, lineNo);
|
|
25334
26573
|
const tgtId = tgtM[1];
|
|
25335
26574
|
const roleWord = tgtM[2]?.toLowerCase();
|
|
25336
26575
|
let role = "input";
|
|
@@ -25351,7 +26590,7 @@ function parseFlowArrow(ast, t, lineNo) {
|
|
|
25351
26590
|
}
|
|
25352
26591
|
rhs = rhs.slice(tgtM[0].length).trim();
|
|
25353
26592
|
let label = "";
|
|
25354
|
-
const q =
|
|
26593
|
+
const q = matchQuoted8(rhs);
|
|
25355
26594
|
if (q) {
|
|
25356
26595
|
label = q.value;
|
|
25357
26596
|
rhs = rhs.slice(q.length).trim();
|
|
@@ -25374,19 +26613,19 @@ function parseFlowArrow(ast, t, lineNo) {
|
|
|
25374
26613
|
function consumeTunnel(s) {
|
|
25375
26614
|
return /\(\s*tunnel\s*\)/i.test(s);
|
|
25376
26615
|
}
|
|
25377
|
-
function
|
|
26616
|
+
function matchQuoted8(s) {
|
|
25378
26617
|
if (!s) return void 0;
|
|
25379
26618
|
const open = s[0];
|
|
25380
26619
|
if (open !== '"' && open !== "\u300C" && open !== "\u201C") return void 0;
|
|
25381
|
-
const close =
|
|
26620
|
+
const close = closingQuote8(open);
|
|
25382
26621
|
const end = s.indexOf(close, 1);
|
|
25383
26622
|
if (end < 0) return void 0;
|
|
25384
26623
|
return { value: s.slice(1, end), length: end + 1 };
|
|
25385
26624
|
}
|
|
25386
|
-
function
|
|
26625
|
+
function closingQuote8(open) {
|
|
25387
26626
|
return open === "\u300C" ? "\u300D" : open === "\u201C" ? "\u201D" : '"';
|
|
25388
26627
|
}
|
|
25389
|
-
function
|
|
26628
|
+
function stripComment12(line2) {
|
|
25390
26629
|
let inQ = false;
|
|
25391
26630
|
let qc = "";
|
|
25392
26631
|
for (let i = 0; i < line2.length; i++) {
|
|
@@ -25397,14 +26636,14 @@ function stripComment11(line2) {
|
|
|
25397
26636
|
}
|
|
25398
26637
|
if (ch === '"' || ch === "\u300C" || ch === "\u201C") {
|
|
25399
26638
|
inQ = true;
|
|
25400
|
-
qc =
|
|
26639
|
+
qc = closingQuote8(ch);
|
|
25401
26640
|
continue;
|
|
25402
26641
|
}
|
|
25403
26642
|
if (ch === "#") return line2.slice(0, i);
|
|
25404
26643
|
}
|
|
25405
26644
|
return line2;
|
|
25406
26645
|
}
|
|
25407
|
-
function
|
|
26646
|
+
function truncate11(s, n) {
|
|
25408
26647
|
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
25409
26648
|
}
|
|
25410
26649
|
|
|
@@ -25562,22 +26801,22 @@ var IDEF0_CONST = {
|
|
|
25562
26801
|
};
|
|
25563
26802
|
function layoutIdef0(astIn) {
|
|
25564
26803
|
const ast = analyseIdef0(astIn);
|
|
25565
|
-
const
|
|
25566
|
-
const ox =
|
|
25567
|
-
const oy =
|
|
26804
|
+
const C3 = IDEF0_CONST;
|
|
26805
|
+
const ox = C3.MARGIN;
|
|
26806
|
+
const oy = C3.MARGIN + C3.TITLE_H;
|
|
25568
26807
|
const boxes = ast.boxes.map((box2, idx) => ({
|
|
25569
26808
|
box: box2,
|
|
25570
|
-
x: ox + idx *
|
|
25571
|
-
y: oy + idx *
|
|
25572
|
-
width:
|
|
25573
|
-
height:
|
|
26809
|
+
x: ox + idx * C3.STEP_X,
|
|
26810
|
+
y: oy + idx * C3.STEP_Y,
|
|
26811
|
+
width: C3.BOX_W,
|
|
26812
|
+
height: C3.BOX_H
|
|
25574
26813
|
}));
|
|
25575
26814
|
const boxIndex = new Map(boxes.map((b, i) => [b.box.id, i]));
|
|
25576
26815
|
const lastBox = boxes[boxes.length - 1];
|
|
25577
|
-
const contentRight = lastBox.x +
|
|
25578
|
-
const contentBottom = lastBox.y +
|
|
25579
|
-
const width = contentRight +
|
|
25580
|
-
const height = contentBottom +
|
|
26816
|
+
const contentRight = lastBox.x + C3.BOX_W;
|
|
26817
|
+
const contentBottom = lastBox.y + C3.BOX_H;
|
|
26818
|
+
const width = contentRight + C3.MARGIN;
|
|
26819
|
+
const height = contentBottom + C3.MARGIN + C3.TITLEBLOCK_H;
|
|
25581
26820
|
const arrows = ast.arrows.map(
|
|
25582
26821
|
(arrow2) => routeArrow(arrow2, boxes, boxIndex)
|
|
25583
26822
|
);
|
|
@@ -25596,7 +26835,7 @@ function sidePoint(b, side) {
|
|
|
25596
26835
|
}
|
|
25597
26836
|
}
|
|
25598
26837
|
function routeArrow(arrow2, boxes, boxIndex) {
|
|
25599
|
-
const
|
|
26838
|
+
const C3 = IDEF0_CONST;
|
|
25600
26839
|
const targetSide = ICOM_SIDE[arrow2.role];
|
|
25601
26840
|
const fromBoundary = arrow2.from.kind === "boundary";
|
|
25602
26841
|
const toBoundary = arrow2.to.kind === "boundary";
|
|
@@ -25629,10 +26868,10 @@ function routeArrow(arrow2, boxes, boxIndex) {
|
|
|
25629
26868
|
margin: false
|
|
25630
26869
|
};
|
|
25631
26870
|
}
|
|
25632
|
-
const marginY =
|
|
26871
|
+
const marginY = C3.MARGIN / 2 + C3.TITLE_H;
|
|
25633
26872
|
let d;
|
|
25634
26873
|
if (targetSide === "left") {
|
|
25635
|
-
const approachX = end.x -
|
|
26874
|
+
const approachX = end.x - C3.FEEDBACK_LEADIN;
|
|
25636
26875
|
d = `M ${start.x} ${start.y} L ${start.x + 20} ${start.y} L ${start.x + 20} ${marginY} L ${approachX} ${marginY} L ${approachX} ${end.y} L ${end.x} ${end.y}`;
|
|
25637
26876
|
} else {
|
|
25638
26877
|
d = `M ${start.x} ${start.y} L ${start.x + 20} ${start.y} L ${start.x + 20} ${marginY} L ${end.x} ${marginY} L ${end.x} ${end.y}`;
|
|
@@ -25646,7 +26885,7 @@ function routeArrow(arrow2, boxes, boxIndex) {
|
|
|
25646
26885
|
};
|
|
25647
26886
|
}
|
|
25648
26887
|
function routeBoundary(arrow2, boxes, boxIndex, targetSide, fromBoundary) {
|
|
25649
|
-
const
|
|
26888
|
+
const C3 = IDEF0_CONST;
|
|
25650
26889
|
const boxEnd = fromBoundary ? arrow2.to : arrow2.from;
|
|
25651
26890
|
const b = boxes[boxIndex.get(boxEnd.boxId)];
|
|
25652
26891
|
const side = fromBoundary ? targetSide : "right";
|
|
@@ -25654,16 +26893,16 @@ function routeBoundary(arrow2, boxes, boxIndex, targetSide, fromBoundary) {
|
|
|
25654
26893
|
let stub;
|
|
25655
26894
|
switch (side) {
|
|
25656
26895
|
case "left":
|
|
25657
|
-
stub = { x: edge.x -
|
|
26896
|
+
stub = { x: edge.x - C3.STUB, y: edge.y };
|
|
25658
26897
|
break;
|
|
25659
26898
|
case "top":
|
|
25660
|
-
stub = { x: edge.x, y: edge.y -
|
|
26899
|
+
stub = { x: edge.x, y: edge.y - C3.STUB };
|
|
25661
26900
|
break;
|
|
25662
26901
|
case "right":
|
|
25663
|
-
stub = { x: edge.x +
|
|
26902
|
+
stub = { x: edge.x + C3.STUB, y: edge.y };
|
|
25664
26903
|
break;
|
|
25665
26904
|
case "bottom":
|
|
25666
|
-
stub = { x: edge.x, y: edge.y +
|
|
26905
|
+
stub = { x: edge.x, y: edge.y + C3.STUB };
|
|
25667
26906
|
break;
|
|
25668
26907
|
}
|
|
25669
26908
|
let path2;
|
|
@@ -25738,7 +26977,7 @@ function renderIdef0Layout(layout, config) {
|
|
|
25738
26977
|
);
|
|
25739
26978
|
const children = [
|
|
25740
26979
|
title(a11y),
|
|
25741
|
-
desc(
|
|
26980
|
+
desc(summarise11(layout)),
|
|
25742
26981
|
styleBlock,
|
|
25743
26982
|
rect({ x: 0, y: 0, width, height, class: "sx-idef0-bg" })
|
|
25744
26983
|
];
|
|
@@ -25896,7 +27135,7 @@ function renderTitleBlock(width, height, node, title2) {
|
|
|
25896
27135
|
text({ x: c2 + 6, y: y + 13, class: "sx-idef0-tb-key" }, "NUMBER")
|
|
25897
27136
|
]);
|
|
25898
27137
|
}
|
|
25899
|
-
function
|
|
27138
|
+
function summarise11(layout) {
|
|
25900
27139
|
const { ast } = layout;
|
|
25901
27140
|
const counts = {};
|
|
25902
27141
|
for (const a of ast.arrows) counts[a.role] = (counts[a.role] ?? 0) + 1;
|
|
@@ -25966,7 +27205,7 @@ function normalizeQuotes3(text2) {
|
|
|
25966
27205
|
for (const ch of text2) out += QUOTE_FOLD.get(ch) ?? ch;
|
|
25967
27206
|
return out;
|
|
25968
27207
|
}
|
|
25969
|
-
function
|
|
27208
|
+
function stripComment13(line2) {
|
|
25970
27209
|
let inQuote = false;
|
|
25971
27210
|
for (let i = 0; i < line2.length; i++) {
|
|
25972
27211
|
const ch = line2[i];
|
|
@@ -26010,7 +27249,7 @@ function parseThreatModel(text2) {
|
|
|
26010
27249
|
let i = 0;
|
|
26011
27250
|
let headerSeen = false;
|
|
26012
27251
|
for (; i < rawLines.length; i++) {
|
|
26013
|
-
const t =
|
|
27252
|
+
const t = stripComment13(rawLines[i] ?? "").trim();
|
|
26014
27253
|
if (t === "") continue;
|
|
26015
27254
|
const h = /^(threatmodel|stride)\b(.*)$/i.exec(t);
|
|
26016
27255
|
if (h) {
|
|
@@ -26029,7 +27268,7 @@ function parseThreatModel(text2) {
|
|
|
26029
27268
|
}
|
|
26030
27269
|
for (; i < rawLines.length; i++) {
|
|
26031
27270
|
const lineNo = i + 1;
|
|
26032
|
-
const t =
|
|
27271
|
+
const t = stripComment13(rawLines[i] ?? "").trim();
|
|
26033
27272
|
if (t === "") continue;
|
|
26034
27273
|
const titleM = /^title\s*:\s*(.+)$/i.exec(t);
|
|
26035
27274
|
if (titleM) {
|
|
@@ -26123,7 +27362,7 @@ function parseThreatModel(text2) {
|
|
|
26123
27362
|
);
|
|
26124
27363
|
}
|
|
26125
27364
|
resolveEndpoints(ast, byId);
|
|
26126
|
-
|
|
27365
|
+
validate7(ast, byId);
|
|
26127
27366
|
return ast;
|
|
26128
27367
|
}
|
|
26129
27368
|
function resolveEndpoints(ast, byId) {
|
|
@@ -26146,7 +27385,7 @@ function resolveEndpoints(ast, byId) {
|
|
|
26146
27385
|
b.members = b.members.map(canon);
|
|
26147
27386
|
}
|
|
26148
27387
|
}
|
|
26149
|
-
function
|
|
27388
|
+
function validate7(ast, byId) {
|
|
26150
27389
|
for (const f of ast.flows) {
|
|
26151
27390
|
const s = byId.get(f.source)?.node;
|
|
26152
27391
|
const tg = byId.get(f.target)?.node;
|
|
@@ -26557,7 +27796,7 @@ function renderThreatModelLayout(layout, config) {
|
|
|
26557
27796
|
]);
|
|
26558
27797
|
const children = [
|
|
26559
27798
|
title(a11y),
|
|
26560
|
-
desc(
|
|
27799
|
+
desc(summarise12(layout)),
|
|
26561
27800
|
styleBlock,
|
|
26562
27801
|
markerDefs,
|
|
26563
27802
|
rect({ x: 0, y: 0, width, height, class: "sx-tm-bg" })
|
|
@@ -26673,8 +27912,8 @@ ${n.label}` : n.label;
|
|
|
26673
27912
|
}
|
|
26674
27913
|
function strideBadge(n, fontFamily) {
|
|
26675
27914
|
const letters = n.stride.categories.join("");
|
|
26676
|
-
const
|
|
26677
|
-
const bw = letters.length *
|
|
27915
|
+
const charW2 = 7;
|
|
27916
|
+
const bw = letters.length * charW2 + 10;
|
|
26678
27917
|
const bh = 15;
|
|
26679
27918
|
const bx = n.cx - bw / 2;
|
|
26680
27919
|
const by = n.y - bh - 4;
|
|
@@ -26703,7 +27942,7 @@ function strideBadge(n, fontFamily) {
|
|
|
26703
27942
|
}
|
|
26704
27943
|
function renderFlow(f, fontFamily) {
|
|
26705
27944
|
const pts = f.points;
|
|
26706
|
-
const d = "M " + pts.map((p) => `${
|
|
27945
|
+
const d = "M " + pts.map((p) => `${round10(p.x)} ${round10(p.y)}`).join(" L ");
|
|
26707
27946
|
const crossing = f.crossesBoundary ? "true" : void 0;
|
|
26708
27947
|
const marker = f.crossesBoundary ? "url(#sx-tm-mk-x)" : "url(#sx-tm-mk)";
|
|
26709
27948
|
const labelHaloW = f.label.length * 5.4 + 8;
|
|
@@ -26771,10 +28010,10 @@ function arrowMarker2(id, cls, crossing) {
|
|
|
26771
28010
|
]
|
|
26772
28011
|
);
|
|
26773
28012
|
}
|
|
26774
|
-
function
|
|
28013
|
+
function round10(n) {
|
|
26775
28014
|
return Math.round(n * 100) / 100;
|
|
26776
28015
|
}
|
|
26777
|
-
function
|
|
28016
|
+
function summarise12(layout) {
|
|
26778
28017
|
const a = layout.analysis;
|
|
26779
28018
|
const counts = {
|
|
26780
28019
|
external: layout.nodes.filter((n) => n.kind === "external").length,
|
|
@@ -26986,7 +28225,7 @@ function parseWeldSpec(raw) {
|
|
|
26986
28225
|
function emptyJoint() {
|
|
26987
28226
|
return { around: false, field: false };
|
|
26988
28227
|
}
|
|
26989
|
-
function
|
|
28228
|
+
function stripComment14(line2) {
|
|
26990
28229
|
const hash = line2.indexOf("#");
|
|
26991
28230
|
if (hash >= 0 && (line2.slice(0, hash).match(/["'“‘]/g)?.length ?? 0) % 2 === 0) {
|
|
26992
28231
|
return line2.slice(0, hash);
|
|
@@ -27015,7 +28254,7 @@ function parseJointBody(body, joint) {
|
|
|
27015
28254
|
}
|
|
27016
28255
|
function parseWelding(text2) {
|
|
27017
28256
|
const ast = { type: "welding", standard: "aws", joints: [], warnings: [] };
|
|
27018
|
-
const src = text2.split(/\r?\n/).map(
|
|
28257
|
+
const src = text2.split(/\r?\n/).map(stripComment14).join("\n");
|
|
27019
28258
|
const headEnd = src.search(/\bjoint\b/i);
|
|
27020
28259
|
const headerScope = headEnd >= 0 ? src.slice(0, headEnd) : src;
|
|
27021
28260
|
const header = headerScope.match(/welding\b([^\n]*)/i);
|
|
@@ -27083,9 +28322,9 @@ function layoutWelding(ast) {
|
|
|
27083
28322
|
var W = 18;
|
|
27084
28323
|
var H = 15;
|
|
27085
28324
|
function pl(points, cls) {
|
|
27086
|
-
return polygon({ points: points.map((p) => `${
|
|
28325
|
+
return polygon({ points: points.map((p) => `${round11(p[0])},${round11(p[1])}`).join(" "), class: cls, fill: "none" });
|
|
27087
28326
|
}
|
|
27088
|
-
function
|
|
28327
|
+
function round11(n) {
|
|
27089
28328
|
return Math.round(n * 100) / 100;
|
|
27090
28329
|
}
|
|
27091
28330
|
function weldGlyph(type, cx, y, dir, cls) {
|
|
@@ -27101,7 +28340,7 @@ function weldGlyph(type, cx, y, dir, cls) {
|
|
|
27101
28340
|
line({ x1: cx + 3, y1: y, x2: cx + 3, y2: yb, class: cls })
|
|
27102
28341
|
];
|
|
27103
28342
|
case "vgroove":
|
|
27104
|
-
return [path({ d: `M ${
|
|
28343
|
+
return [path({ d: `M ${round11(x0)} ${round11(yb)} L ${round11(cx)} ${round11(y)} L ${round11(x1)} ${round11(yb)}`, class: cls, fill: "none" })];
|
|
27105
28344
|
case "bevel":
|
|
27106
28345
|
return [
|
|
27107
28346
|
line({ x1: cx - 4, y1: y, x2: cx - 4, y2: yb, class: cls }),
|
|
@@ -27110,7 +28349,7 @@ function weldGlyph(type, cx, y, dir, cls) {
|
|
|
27110
28349
|
case "ugroove":
|
|
27111
28350
|
return [
|
|
27112
28351
|
path({
|
|
27113
|
-
d: `M ${
|
|
28352
|
+
d: `M ${round11(x0)} ${round11(y)} L ${round11(x0)} ${round11(y + dir * H * 0.45)} Q ${round11(x0)} ${round11(yb)} ${round11(cx)} ${round11(yb)} Q ${round11(x1)} ${round11(yb)} ${round11(x1)} ${round11(y + dir * H * 0.45)} L ${round11(x1)} ${round11(y)}`,
|
|
27114
28353
|
class: cls,
|
|
27115
28354
|
fill: "none"
|
|
27116
28355
|
})
|
|
@@ -27118,20 +28357,20 @@ function weldGlyph(type, cx, y, dir, cls) {
|
|
|
27118
28357
|
case "jgroove":
|
|
27119
28358
|
return [
|
|
27120
28359
|
path({
|
|
27121
|
-
d: `M ${
|
|
28360
|
+
d: `M ${round11(cx - 4)} ${round11(y)} L ${round11(cx - 4)} ${round11(y + dir * H * 0.45)} Q ${round11(cx - 4)} ${round11(yb)} ${round11(x1)} ${round11(yb)}`,
|
|
27122
28361
|
class: cls,
|
|
27123
28362
|
fill: "none"
|
|
27124
28363
|
})
|
|
27125
28364
|
];
|
|
27126
28365
|
case "flarev":
|
|
27127
28366
|
return [
|
|
27128
|
-
path({ d: `M ${
|
|
27129
|
-
path({ d: `M ${
|
|
28367
|
+
path({ d: `M ${round11(x0)} ${round11(yb)} Q ${round11(cx - 2)} ${round11(yb)} ${round11(cx)} ${round11(y)}`, class: cls, fill: "none" }),
|
|
28368
|
+
path({ d: `M ${round11(x1)} ${round11(yb)} Q ${round11(cx + 2)} ${round11(yb)} ${round11(cx)} ${round11(y)}`, class: cls, fill: "none" })
|
|
27130
28369
|
];
|
|
27131
28370
|
case "flarebevel":
|
|
27132
28371
|
return [
|
|
27133
28372
|
line({ x1: cx - 4, y1: y, x2: cx - 4, y2: yb, class: cls }),
|
|
27134
|
-
path({ d: `M ${
|
|
28373
|
+
path({ d: `M ${round11(cx - 4)} ${round11(y)} Q ${round11(x1)} ${round11(y)} ${round11(x1)} ${round11(yb)}`, class: cls, fill: "none" })
|
|
27135
28374
|
];
|
|
27136
28375
|
case "plug":
|
|
27137
28376
|
case "slot": {
|
|
@@ -27149,12 +28388,12 @@ function weldGlyph(type, cx, y, dir, cls) {
|
|
|
27149
28388
|
case "backing": {
|
|
27150
28389
|
const rr = W * 0.45;
|
|
27151
28390
|
const sweep = dir > 0 ? 1 : 0;
|
|
27152
|
-
return [path({ d: `M ${
|
|
28391
|
+
return [path({ d: `M ${round11(cx - rr)} ${round11(y)} A ${round11(rr)} ${round11(rr)} 0 0 ${sweep} ${round11(cx + rr)} ${round11(y)}`, class: cls, fill: "none" })];
|
|
27153
28392
|
}
|
|
27154
28393
|
case "surfacing": {
|
|
27155
28394
|
const r7 = W / 4;
|
|
27156
28395
|
const yy = y + dir * 3;
|
|
27157
|
-
const bump = (off) => path({ d: `M ${
|
|
28396
|
+
const bump = (off) => path({ d: `M ${round11(cx - W / 2)} ${round11(off)} a ${round11(r7)} ${round11(r7)} 0 0 ${dir > 0 ? 1 : 0} ${round11(W / 2)} 0 a ${round11(r7)} ${round11(r7)} 0 0 ${dir > 0 ? 1 : 0} ${round11(W / 2)} 0`, class: cls, fill: "none" });
|
|
27158
28397
|
return [bump(y), bump(yy)];
|
|
27159
28398
|
}
|
|
27160
28399
|
case "edge":
|
|
@@ -27172,7 +28411,7 @@ function contourGlyph(contour, cx, y, dir, cls) {
|
|
|
27172
28411
|
}
|
|
27173
28412
|
const bulge = contour === "convex" ? dir : -dir;
|
|
27174
28413
|
return path({
|
|
27175
|
-
d: `M ${
|
|
28414
|
+
d: `M ${round11(cx - half)} ${round11(yy)} Q ${round11(cx)} ${round11(yy + bulge * 5)} ${round11(cx + half)} ${round11(yy)}`,
|
|
27176
28415
|
class: cls,
|
|
27177
28416
|
fill: "none"
|
|
27178
28417
|
});
|
|
@@ -31017,7 +32256,7 @@ var ErdParseError = class extends Error {
|
|
|
31017
32256
|
}
|
|
31018
32257
|
lineNumber;
|
|
31019
32258
|
};
|
|
31020
|
-
function
|
|
32259
|
+
function stripComment15(s) {
|
|
31021
32260
|
let out = "";
|
|
31022
32261
|
let inQuote = false;
|
|
31023
32262
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -31071,7 +32310,7 @@ function parseCardToken(raw, side) {
|
|
|
31071
32310
|
}
|
|
31072
32311
|
function lex(text2) {
|
|
31073
32312
|
return text2.split(/\r?\n/).map((raw, i) => ({
|
|
31074
|
-
text:
|
|
32313
|
+
text: stripComment15(raw).trim(),
|
|
31075
32314
|
lineNumber: i + 1
|
|
31076
32315
|
})).filter((l) => l.text.length > 0);
|
|
31077
32316
|
}
|
|
@@ -31449,8 +32688,8 @@ var ERD_CONST = {
|
|
|
31449
32688
|
EDGE_BEND_STAGGER: 10
|
|
31450
32689
|
};
|
|
31451
32690
|
function measureEntity(ent) {
|
|
31452
|
-
const
|
|
31453
|
-
const headerWidth =
|
|
32691
|
+
const C3 = ERD_CONST;
|
|
32692
|
+
const headerWidth = C3.ENTITY_PADDING_X * 2 + ent.name.length * C3.CHAR_W_HEADER;
|
|
31454
32693
|
let widest = headerWidth;
|
|
31455
32694
|
const rows = [];
|
|
31456
32695
|
for (let i = 0; i < ent.attributes.length; i++) {
|
|
@@ -31458,14 +32697,14 @@ function measureEntity(ent) {
|
|
|
31458
32697
|
const namePart = a.name;
|
|
31459
32698
|
const typePart = a.type ?? "";
|
|
31460
32699
|
const markerCount = (a.pk ? 1 : 0) + (a.fk ? 1 : 0) + (a.uk ? 1 : 0) + (a.notNull && !a.pk ? 1 : 0);
|
|
31461
|
-
const markerWidth = markerCount * (
|
|
31462
|
-
const w =
|
|
32700
|
+
const markerWidth = markerCount * (C3.CHAR_W_MARKER * 2.4 + 4);
|
|
32701
|
+
const w = C3.ENTITY_PADDING_X * 2 + namePart.length * C3.CHAR_W_ROW + (typePart ? C3.TYPE_GAP + typePart.length * C3.CHAR_W_ROW : 0) + markerWidth;
|
|
31463
32702
|
if (w > widest) widest = w;
|
|
31464
|
-
const yCenter =
|
|
32703
|
+
const yCenter = C3.HEADER_HEIGHT + i * C3.ROW_HEIGHT + C3.ROW_HEIGHT / 2;
|
|
31465
32704
|
rows.push({ attribute: a, yCenter });
|
|
31466
32705
|
}
|
|
31467
|
-
const width = Math.max(widest,
|
|
31468
|
-
const height =
|
|
32706
|
+
const width = Math.max(widest, C3.ENTITY_MIN_WIDTH);
|
|
32707
|
+
const height = C3.HEADER_HEIGHT + ent.attributes.length * C3.ROW_HEIGHT + C3.ROW_PAD_Y;
|
|
31469
32708
|
return { width, height, rows };
|
|
31470
32709
|
}
|
|
31471
32710
|
function buildColumnAssignment(ast) {
|
|
@@ -31562,15 +32801,15 @@ function reorderByBarycenter(layerToEnts, layers, refs) {
|
|
|
31562
32801
|
sweep("down");
|
|
31563
32802
|
}
|
|
31564
32803
|
function assignYCoordinates(orderedLayers, measured, neighbors) {
|
|
31565
|
-
const
|
|
32804
|
+
const C3 = ERD_CONST;
|
|
31566
32805
|
const placed = /* @__PURE__ */ new Map();
|
|
31567
32806
|
for (const ls of orderedLayers) {
|
|
31568
|
-
let prevBottom =
|
|
32807
|
+
let prevBottom = C3.PADDING - C3.ROW_GAP;
|
|
31569
32808
|
for (let i = 0; i < ls.ids.length; i++) {
|
|
31570
32809
|
const id = ls.ids[i];
|
|
31571
32810
|
const m = measured.get(id);
|
|
31572
32811
|
const ns = neighbors.get(id);
|
|
31573
|
-
let target =
|
|
32812
|
+
let target = C3.PADDING;
|
|
31574
32813
|
if (ns) {
|
|
31575
32814
|
const placedNeighborCenters = [];
|
|
31576
32815
|
for (const n of ns) {
|
|
@@ -31584,7 +32823,7 @@ function assignYCoordinates(orderedLayers, measured, neighbors) {
|
|
|
31584
32823
|
target = med - m.height / 2;
|
|
31585
32824
|
}
|
|
31586
32825
|
}
|
|
31587
|
-
const y = Math.max(target, prevBottom +
|
|
32826
|
+
const y = Math.max(target, prevBottom + C3.ROW_GAP);
|
|
31588
32827
|
placed.set(id, {
|
|
31589
32828
|
id,
|
|
31590
32829
|
y,
|
|
@@ -31597,7 +32836,7 @@ function assignYCoordinates(orderedLayers, measured, neighbors) {
|
|
|
31597
32836
|
}
|
|
31598
32837
|
for (let li = orderedLayers.length - 1; li >= 0; li--) {
|
|
31599
32838
|
const ls = orderedLayers[li];
|
|
31600
|
-
let prevBottom =
|
|
32839
|
+
let prevBottom = C3.PADDING - C3.ROW_GAP;
|
|
31601
32840
|
for (let i = 0; i < ls.ids.length; i++) {
|
|
31602
32841
|
const id = ls.ids[i];
|
|
31603
32842
|
const slot = placed.get(id);
|
|
@@ -31616,7 +32855,7 @@ function assignYCoordinates(orderedLayers, measured, neighbors) {
|
|
|
31616
32855
|
target = med - slot.height / 2;
|
|
31617
32856
|
}
|
|
31618
32857
|
}
|
|
31619
|
-
const lower = prevBottom +
|
|
32858
|
+
const lower = prevBottom + C3.ROW_GAP;
|
|
31620
32859
|
const newY = Math.max(lower, Math.min(slot.y, target));
|
|
31621
32860
|
slot.y = newY;
|
|
31622
32861
|
prevBottom = newY + slot.height;
|
|
@@ -31627,7 +32866,7 @@ function assignYCoordinates(orderedLayers, measured, neighbors) {
|
|
|
31627
32866
|
return out;
|
|
31628
32867
|
}
|
|
31629
32868
|
function layoutErd(ast) {
|
|
31630
|
-
const
|
|
32869
|
+
const C3 = ERD_CONST;
|
|
31631
32870
|
const isLR = ast.direction === "LR";
|
|
31632
32871
|
const measured = /* @__PURE__ */ new Map();
|
|
31633
32872
|
for (const e of ast.entities) {
|
|
@@ -31675,30 +32914,30 @@ function layoutErd(ast) {
|
|
|
31675
32914
|
const orderingCoord = assignYCoordinates(orderedLayers, measureForOrdering, neighbors);
|
|
31676
32915
|
const placed = [];
|
|
31677
32916
|
if (isLR) {
|
|
31678
|
-
let cursorX =
|
|
32917
|
+
let cursorX = C3.PADDING;
|
|
31679
32918
|
for (const ls of layerSizes) {
|
|
31680
32919
|
for (const id of ls.ids) {
|
|
31681
32920
|
const m = measured.get(id);
|
|
31682
32921
|
const x = cursorX + (ls.maxWidth - m.width) / 2;
|
|
31683
|
-
const y = orderingCoord.get(id) ??
|
|
32922
|
+
const y = orderingCoord.get(id) ?? C3.PADDING;
|
|
31684
32923
|
placed.push({
|
|
31685
32924
|
entity: m.ent,
|
|
31686
32925
|
x,
|
|
31687
32926
|
y,
|
|
31688
32927
|
width: m.width,
|
|
31689
32928
|
height: m.height,
|
|
31690
|
-
headerHeight:
|
|
32929
|
+
headerHeight: C3.HEADER_HEIGHT,
|
|
31691
32930
|
rows: m.rows
|
|
31692
32931
|
});
|
|
31693
32932
|
}
|
|
31694
|
-
cursorX += ls.maxWidth +
|
|
32933
|
+
cursorX += ls.maxWidth + C3.COL_GAP;
|
|
31695
32934
|
}
|
|
31696
32935
|
} else {
|
|
31697
|
-
let cursorY =
|
|
32936
|
+
let cursorY = C3.PADDING;
|
|
31698
32937
|
for (const ls of layerSizes) {
|
|
31699
32938
|
for (const id of ls.ids) {
|
|
31700
32939
|
const m = measured.get(id);
|
|
31701
|
-
const x = orderingCoord.get(id) ??
|
|
32940
|
+
const x = orderingCoord.get(id) ?? C3.PADDING;
|
|
31702
32941
|
const y = cursorY + (ls.maxHeight - m.height) / 2;
|
|
31703
32942
|
placed.push({
|
|
31704
32943
|
entity: m.ent,
|
|
@@ -31706,11 +32945,11 @@ function layoutErd(ast) {
|
|
|
31706
32945
|
y,
|
|
31707
32946
|
width: m.width,
|
|
31708
32947
|
height: m.height,
|
|
31709
|
-
headerHeight:
|
|
32948
|
+
headerHeight: C3.HEADER_HEIGHT,
|
|
31710
32949
|
rows: m.rows
|
|
31711
32950
|
});
|
|
31712
32951
|
}
|
|
31713
|
-
cursorY += ls.maxHeight +
|
|
32952
|
+
cursorY += ls.maxHeight + C3.ROW_GAP;
|
|
31714
32953
|
}
|
|
31715
32954
|
}
|
|
31716
32955
|
let maxX = 0;
|
|
@@ -31719,8 +32958,8 @@ function layoutErd(ast) {
|
|
|
31719
32958
|
if (e.x + e.width > maxX) maxX = e.x + e.width;
|
|
31720
32959
|
if (e.y + e.height > maxY) maxY = e.y + e.height;
|
|
31721
32960
|
}
|
|
31722
|
-
const width = maxX +
|
|
31723
|
-
const height = maxY +
|
|
32961
|
+
const width = maxX + C3.PADDING;
|
|
32962
|
+
const height = maxY + C3.PADDING;
|
|
31724
32963
|
const placedById = new Map(placed.map((p) => [p.entity.id, p]));
|
|
31725
32964
|
const edges = [];
|
|
31726
32965
|
const bendBucketUses = /* @__PURE__ */ new Map();
|
|
@@ -31757,7 +32996,7 @@ function rowYByColumn(e, col) {
|
|
|
31757
32996
|
return e.y + e.height / 2;
|
|
31758
32997
|
}
|
|
31759
32998
|
function routeOrthogonal(a, b, fromCol, toCol, bendBucketUses) {
|
|
31760
|
-
const
|
|
32999
|
+
const C3 = ERD_CONST;
|
|
31761
33000
|
const aCenterX = a.x + a.width / 2;
|
|
31762
33001
|
const bCenterX = b.x + b.width / 2;
|
|
31763
33002
|
const aCenterY = a.y + a.height / 2;
|
|
@@ -31781,14 +33020,14 @@ function routeOrthogonal(a, b, fromCol, toCol, bendBucketUses) {
|
|
|
31781
33020
|
const useIdx = bendBucketUses.get(bucketKey) ?? 0;
|
|
31782
33021
|
bendBucketUses.set(bucketKey, useIdx + 1);
|
|
31783
33022
|
const sign = useIdx % 2 === 0 ? 1 : -1;
|
|
31784
|
-
const stagger = Math.ceil(useIdx / 2) *
|
|
33023
|
+
const stagger = Math.ceil(useIdx / 2) * C3.EDGE_BEND_STAGGER * sign;
|
|
31785
33024
|
const midX = baseMidX + stagger;
|
|
31786
33025
|
const path2 = `M ${aAnchor.x} ${aAnchor.y} L ${midX} ${aAnchor.y} L ${midX} ${bAnchor.y} L ${bAnchor.x} ${bAnchor.y}`;
|
|
31787
33026
|
return {
|
|
31788
33027
|
path: path2,
|
|
31789
33028
|
fromAnchor: { x: aAnchor.x, y: aAnchor.y, side: aSide },
|
|
31790
33029
|
toAnchor: { x: bAnchor.x, y: bAnchor.y, side: bSide },
|
|
31791
|
-
labelAt: { x: midX, y: (aAnchor.y + bAnchor.y) / 2 -
|
|
33030
|
+
labelAt: { x: midX, y: (aAnchor.y + bAnchor.y) / 2 - C3.LABEL_OFFSET }
|
|
31792
33031
|
};
|
|
31793
33032
|
} else {
|
|
31794
33033
|
const baseMidY = (aAnchor.y + bAnchor.y) / 2;
|
|
@@ -31796,14 +33035,14 @@ function routeOrthogonal(a, b, fromCol, toCol, bendBucketUses) {
|
|
|
31796
33035
|
const useIdx = bendBucketUses.get(bucketKey) ?? 0;
|
|
31797
33036
|
bendBucketUses.set(bucketKey, useIdx + 1);
|
|
31798
33037
|
const sign = useIdx % 2 === 0 ? 1 : -1;
|
|
31799
|
-
const stagger = Math.ceil(useIdx / 2) *
|
|
33038
|
+
const stagger = Math.ceil(useIdx / 2) * C3.EDGE_BEND_STAGGER * sign;
|
|
31800
33039
|
const midY = baseMidY + stagger;
|
|
31801
33040
|
const path2 = `M ${aAnchor.x} ${aAnchor.y} L ${aAnchor.x} ${midY} L ${bAnchor.x} ${midY} L ${bAnchor.x} ${bAnchor.y}`;
|
|
31802
33041
|
return {
|
|
31803
33042
|
path: path2,
|
|
31804
33043
|
fromAnchor: { x: aAnchor.x, y: aAnchor.y, side: aSide },
|
|
31805
33044
|
toAnchor: { x: bAnchor.x, y: bAnchor.y, side: bSide },
|
|
31806
|
-
labelAt: { x: (aAnchor.x + bAnchor.x) / 2, y: midY -
|
|
33045
|
+
labelAt: { x: (aAnchor.x + bAnchor.x) / 2, y: midY - C3.LABEL_OFFSET }
|
|
31807
33046
|
};
|
|
31808
33047
|
}
|
|
31809
33048
|
}
|
|
@@ -31854,7 +33093,7 @@ function attrMarkers(a) {
|
|
|
31854
33093
|
return out;
|
|
31855
33094
|
}
|
|
31856
33095
|
function renderEntity(e) {
|
|
31857
|
-
const
|
|
33096
|
+
const C3 = ERD_CONST;
|
|
31858
33097
|
const body = [];
|
|
31859
33098
|
body.push(
|
|
31860
33099
|
rect({
|
|
@@ -31890,9 +33129,9 @@ function renderEntity(e) {
|
|
|
31890
33129
|
body.push(
|
|
31891
33130
|
line({
|
|
31892
33131
|
x1: e.x,
|
|
31893
|
-
y1: e.y + e.headerHeight + i *
|
|
33132
|
+
y1: e.y + e.headerHeight + i * C3.ROW_HEIGHT,
|
|
31894
33133
|
x2: e.x + e.width,
|
|
31895
|
-
y2: e.y + e.headerHeight + i *
|
|
33134
|
+
y2: e.y + e.headerHeight + i * C3.ROW_HEIGHT,
|
|
31896
33135
|
class: "lt-erd-row-divider"
|
|
31897
33136
|
})
|
|
31898
33137
|
);
|
|
@@ -31900,7 +33139,7 @@ function renderEntity(e) {
|
|
|
31900
33139
|
body.push(
|
|
31901
33140
|
text(
|
|
31902
33141
|
{
|
|
31903
|
-
x: e.x +
|
|
33142
|
+
x: e.x + C3.ENTITY_PADDING_X,
|
|
31904
33143
|
y: cy,
|
|
31905
33144
|
class: a.pk ? "lt-erd-attr-name-pk" : "lt-erd-attr-name"
|
|
31906
33145
|
},
|
|
@@ -31911,7 +33150,7 @@ function renderEntity(e) {
|
|
|
31911
33150
|
const markerW = 26;
|
|
31912
33151
|
const markerGap = 4;
|
|
31913
33152
|
const markersBlockW = markers6.length * markerW + (markers6.length - 1) * markerGap;
|
|
31914
|
-
const markersStartX = e.x + e.width -
|
|
33153
|
+
const markersStartX = e.x + e.width - C3.ENTITY_PADDING_X - markersBlockW;
|
|
31915
33154
|
if (a.type) {
|
|
31916
33155
|
body.push(
|
|
31917
33156
|
text(
|
|
@@ -31959,25 +33198,25 @@ function renderEntity(e) {
|
|
|
31959
33198
|
);
|
|
31960
33199
|
}
|
|
31961
33200
|
function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
31962
|
-
const
|
|
33201
|
+
const C3 = ERD_CONST;
|
|
31963
33202
|
const px = -dirY;
|
|
31964
33203
|
const py = dirX;
|
|
31965
|
-
const barDist =
|
|
33204
|
+
const barDist = C3.GLYPH_OFFSET;
|
|
31966
33205
|
const barX = ax + dirX * barDist;
|
|
31967
33206
|
const barY = ay + dirY * barDist;
|
|
31968
33207
|
const footTipX = ax;
|
|
31969
33208
|
const footTipY = ay;
|
|
31970
|
-
const footBaseX = ax + dirX *
|
|
31971
|
-
const footBaseY = ay + dirY *
|
|
33209
|
+
const footBaseX = ax + dirX * C3.GLYPH_FOOT_LEN;
|
|
33210
|
+
const footBaseY = ay + dirY * C3.GLYPH_FOOT_LEN;
|
|
31972
33211
|
const parts = [];
|
|
31973
33212
|
switch (card) {
|
|
31974
33213
|
case "one-mandatory": {
|
|
31975
33214
|
parts.push(
|
|
31976
33215
|
line({
|
|
31977
|
-
x1: barX + px *
|
|
31978
|
-
y1: barY + py *
|
|
31979
|
-
x2: barX - px *
|
|
31980
|
-
y2: barY - py *
|
|
33216
|
+
x1: barX + px * C3.GLYPH_BAR_HALF,
|
|
33217
|
+
y1: barY + py * C3.GLYPH_BAR_HALF,
|
|
33218
|
+
x2: barX - px * C3.GLYPH_BAR_HALF,
|
|
33219
|
+
y2: barY - py * C3.GLYPH_BAR_HALF,
|
|
31981
33220
|
class: "lt-erd-glyph"
|
|
31982
33221
|
})
|
|
31983
33222
|
);
|
|
@@ -31988,7 +33227,7 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
31988
33227
|
circle({
|
|
31989
33228
|
cx: barX,
|
|
31990
33229
|
cy: barY,
|
|
31991
|
-
r:
|
|
33230
|
+
r: C3.GLYPH_CIRCLE_R,
|
|
31992
33231
|
class: "lt-erd-glyph-circle"
|
|
31993
33232
|
})
|
|
31994
33233
|
);
|
|
@@ -31997,10 +33236,10 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
31997
33236
|
case "many-mandatory": {
|
|
31998
33237
|
parts.push(
|
|
31999
33238
|
line({
|
|
32000
|
-
x1: barX + px *
|
|
32001
|
-
y1: barY + py *
|
|
32002
|
-
x2: barX - px *
|
|
32003
|
-
y2: barY - py *
|
|
33239
|
+
x1: barX + px * C3.GLYPH_BAR_HALF,
|
|
33240
|
+
y1: barY + py * C3.GLYPH_BAR_HALF,
|
|
33241
|
+
x2: barX - px * C3.GLYPH_BAR_HALF,
|
|
33242
|
+
y2: barY - py * C3.GLYPH_BAR_HALF,
|
|
32004
33243
|
class: "lt-erd-glyph"
|
|
32005
33244
|
})
|
|
32006
33245
|
);
|
|
@@ -32011,8 +33250,8 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
32011
33250
|
line({
|
|
32012
33251
|
x1: footBaseX,
|
|
32013
33252
|
y1: footBaseY,
|
|
32014
|
-
x2: footTipX + px *
|
|
32015
|
-
y2: footTipY + py *
|
|
33253
|
+
x2: footTipX + px * C3.GLYPH_BAR_HALF,
|
|
33254
|
+
y2: footTipY + py * C3.GLYPH_BAR_HALF,
|
|
32016
33255
|
class: "lt-erd-glyph"
|
|
32017
33256
|
})
|
|
32018
33257
|
);
|
|
@@ -32020,8 +33259,8 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
32020
33259
|
line({
|
|
32021
33260
|
x1: footBaseX,
|
|
32022
33261
|
y1: footBaseY,
|
|
32023
|
-
x2: footTipX - px *
|
|
32024
|
-
y2: footTipY - py *
|
|
33262
|
+
x2: footTipX - px * C3.GLYPH_BAR_HALF,
|
|
33263
|
+
y2: footTipY - py * C3.GLYPH_BAR_HALF,
|
|
32025
33264
|
class: "lt-erd-glyph"
|
|
32026
33265
|
})
|
|
32027
33266
|
);
|
|
@@ -32032,7 +33271,7 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
32032
33271
|
circle({
|
|
32033
33272
|
cx: barX,
|
|
32034
33273
|
cy: barY,
|
|
32035
|
-
r:
|
|
33274
|
+
r: C3.GLYPH_CIRCLE_R,
|
|
32036
33275
|
class: "lt-erd-glyph-circle"
|
|
32037
33276
|
})
|
|
32038
33277
|
);
|
|
@@ -32043,8 +33282,8 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
32043
33282
|
line({
|
|
32044
33283
|
x1: footBaseX,
|
|
32045
33284
|
y1: footBaseY,
|
|
32046
|
-
x2: footTipX + px *
|
|
32047
|
-
y2: footTipY + py *
|
|
33285
|
+
x2: footTipX + px * C3.GLYPH_BAR_HALF,
|
|
33286
|
+
y2: footTipY + py * C3.GLYPH_BAR_HALF,
|
|
32048
33287
|
class: "lt-erd-glyph"
|
|
32049
33288
|
})
|
|
32050
33289
|
);
|
|
@@ -32052,8 +33291,8 @@ function renderGlyph2(ax, ay, dirX, dirY, card) {
|
|
|
32052
33291
|
line({
|
|
32053
33292
|
x1: footBaseX,
|
|
32054
33293
|
y1: footBaseY,
|
|
32055
|
-
x2: footTipX - px *
|
|
32056
|
-
y2: footTipY - py *
|
|
33294
|
+
x2: footTipX - px * C3.GLYPH_BAR_HALF,
|
|
33295
|
+
y2: footTipY - py * C3.GLYPH_BAR_HALF,
|
|
32057
33296
|
class: "lt-erd-glyph"
|
|
32058
33297
|
})
|
|
32059
33298
|
);
|
|
@@ -32198,7 +33437,7 @@ var BreadboardParseError = class extends Error {
|
|
|
32198
33437
|
}
|
|
32199
33438
|
lineNumber;
|
|
32200
33439
|
};
|
|
32201
|
-
function
|
|
33440
|
+
function stripComment16(s) {
|
|
32202
33441
|
let out = "";
|
|
32203
33442
|
let inQuote = false;
|
|
32204
33443
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -32219,7 +33458,7 @@ function unquote8(v) {
|
|
|
32219
33458
|
}
|
|
32220
33459
|
function lex2(text2) {
|
|
32221
33460
|
return text2.split(/\r?\n/).map((raw, i) => ({
|
|
32222
|
-
text:
|
|
33461
|
+
text: stripComment16(raw).trimEnd().replace(/^\s+/, ""),
|
|
32223
33462
|
lineNumber: i + 1
|
|
32224
33463
|
})).filter((l) => l.text.length > 0);
|
|
32225
33464
|
}
|
|
@@ -34160,7 +35399,7 @@ function layerPool(ast, pool, colByObj) {
|
|
|
34160
35399
|
}
|
|
34161
35400
|
}
|
|
34162
35401
|
}
|
|
34163
|
-
function
|
|
35402
|
+
function fmt7(n) {
|
|
34164
35403
|
return (Math.round(n * 100) / 100).toString();
|
|
34165
35404
|
}
|
|
34166
35405
|
function routeSequenceFlow(f, objCenter, objById) {
|
|
@@ -34181,11 +35420,11 @@ function routeSequenceFlow(f, objCenter, objById) {
|
|
|
34181
35420
|
let labelAnchor;
|
|
34182
35421
|
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
34183
35422
|
const midX = (from.x + to.x) / 2;
|
|
34184
|
-
path2 = `M ${
|
|
35423
|
+
path2 = `M ${fmt7(from.x)} ${fmt7(from.y)} L ${fmt7(midX)} ${fmt7(from.y)} L ${fmt7(midX)} ${fmt7(to.y)} L ${fmt7(to.x)} ${fmt7(to.y)}`;
|
|
34185
35424
|
labelAnchor = { x: midX, y: (from.y + to.y) / 2 - 6 };
|
|
34186
35425
|
} else {
|
|
34187
35426
|
const midY = (from.y + to.y) / 2;
|
|
34188
|
-
path2 = `M ${
|
|
35427
|
+
path2 = `M ${fmt7(from.x)} ${fmt7(from.y)} L ${fmt7(from.x)} ${fmt7(midY)} L ${fmt7(to.x)} ${fmt7(midY)} L ${fmt7(to.x)} ${fmt7(to.y)}`;
|
|
34189
35428
|
labelAnchor = { x: (from.x + to.x) / 2, y: midY - 6 };
|
|
34190
35429
|
}
|
|
34191
35430
|
return { flow: f, path: path2, labelAnchor };
|
|
@@ -34208,7 +35447,7 @@ function routeMessageFlow(f, objCenter, poolByLabel) {
|
|
|
34208
35447
|
const A = endpoint(f.from);
|
|
34209
35448
|
const B = endpoint(f.to);
|
|
34210
35449
|
const midY = (A.y + B.y) / 2;
|
|
34211
|
-
const path2 = `M ${
|
|
35450
|
+
const path2 = `M ${fmt7(A.x)} ${fmt7(A.y)} L ${fmt7(A.x)} ${fmt7(midY)} L ${fmt7(B.x)} ${fmt7(midY)} L ${fmt7(B.x)} ${fmt7(B.y)}`;
|
|
34212
35451
|
return { flow: f, path: path2, labelAnchor: { x: (A.x + B.x) / 2, y: midY - 6 } };
|
|
34213
35452
|
}
|
|
34214
35453
|
|
|
@@ -37113,14 +38352,39 @@ function chairAt(px, cx, cy, deg) {
|
|
|
37113
38352
|
const rot = Math.round(deg * 10) / 10;
|
|
37114
38353
|
return el("g", { transform: `translate(${px(cx)},${px(cy)}) rotate(${rot})` }, [body]);
|
|
37115
38354
|
}
|
|
37116
|
-
|
|
38355
|
+
var SEAT_FS = 0.17;
|
|
38356
|
+
var SEAT_FS_MIN = 0.085;
|
|
38357
|
+
function seatName(c, cx, cy, name, slotW) {
|
|
38358
|
+
if (!name) return "";
|
|
38359
|
+
const unitW = estimateTextWidth(name, 1);
|
|
38360
|
+
let fs = SEAT_FS;
|
|
38361
|
+
if (unitW > 0 && unitW * fs > slotW) fs = Math.max(SEAT_FS_MIN, slotW / unitW);
|
|
38362
|
+
return text(
|
|
38363
|
+
{
|
|
38364
|
+
class: "sx-fp-seat-name",
|
|
38365
|
+
x: c.px(cx),
|
|
38366
|
+
y: c.px(cy),
|
|
38367
|
+
"text-anchor": "middle",
|
|
38368
|
+
"dominant-baseline": "central",
|
|
38369
|
+
"font-size": c.px(fs)
|
|
38370
|
+
},
|
|
38371
|
+
name
|
|
38372
|
+
);
|
|
38373
|
+
}
|
|
38374
|
+
function edgeChairs(c, top, bottom, seats) {
|
|
37117
38375
|
const n = Math.max(1, Math.round(c.w / 0.65));
|
|
38376
|
+
const slotW = c.w / n * 0.96;
|
|
37118
38377
|
const out = [];
|
|
37119
|
-
|
|
37120
|
-
|
|
37121
|
-
|
|
37122
|
-
|
|
37123
|
-
|
|
38378
|
+
let s = 0;
|
|
38379
|
+
const row = (cy, deg) => {
|
|
38380
|
+
for (let i = 0; i < n; i++) {
|
|
38381
|
+
const cx = (i + 0.5) / n * c.w;
|
|
38382
|
+
out.push(chairAt(c.px, cx, cy, deg));
|
|
38383
|
+
if (seats) out.push(seatName(c, cx, cy, seats[s++], slotW));
|
|
38384
|
+
}
|
|
38385
|
+
};
|
|
38386
|
+
if (top) row(-CHAIR_GAP, 0);
|
|
38387
|
+
row(c.h + CHAIR_GAP, 180);
|
|
37124
38388
|
return out.join("");
|
|
37125
38389
|
}
|
|
37126
38390
|
function box(c, cls = "sx-fp-furn", rx = 0) {
|
|
@@ -37202,19 +38466,21 @@ function roundTable(seats, diaM) {
|
|
|
37202
38466
|
const r7 = half - ring;
|
|
37203
38467
|
const cx = c.w / 2;
|
|
37204
38468
|
const cy = c.h / 2;
|
|
38469
|
+
const slotW = 2 * (r7 + ring * 0.55) * Math.sin(Math.PI / seats);
|
|
37205
38470
|
const parts = [circle({ class: "sx-fp-furn", cx: c.px(cx), cy: c.px(cy), r: c.px(r7) })];
|
|
37206
38471
|
for (let i = 0; i < seats; i++) {
|
|
37207
38472
|
const a = i / seats * 2 * Math.PI - Math.PI / 2;
|
|
37208
38473
|
const px0 = cx + (r7 + ring * 0.55) * Math.cos(a);
|
|
37209
38474
|
const py0 = cy + (r7 + ring * 0.55) * Math.sin(a);
|
|
37210
38475
|
parts.push(chairAt(c.px, px0, py0, a * 180 / Math.PI + 90));
|
|
38476
|
+
if (c.seats) parts.push(seatName(c, px0, py0, c.seats[i], slotW));
|
|
37211
38477
|
}
|
|
37212
38478
|
return parts.join("");
|
|
37213
38479
|
}
|
|
37214
38480
|
};
|
|
37215
38481
|
}
|
|
37216
38482
|
function tableDraw(top, bottom) {
|
|
37217
|
-
return (c) => box(c) + edgeChairs(c, top);
|
|
38483
|
+
return (c) => box(c) + edgeChairs(c, top, bottom, c.seats);
|
|
37218
38484
|
}
|
|
37219
38485
|
var TREAD = 0.28;
|
|
37220
38486
|
function treadLines(c, vert, fixed0, fixed1, from, to, dashedFrom) {
|
|
@@ -37464,7 +38730,7 @@ var FLOORPLAN_SYMBOLS = {
|
|
|
37464
38730
|
return parts.join("");
|
|
37465
38731
|
}
|
|
37466
38732
|
},
|
|
37467
|
-
"dining-table": { w: 1.6, h: 0.9, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true) },
|
|
38733
|
+
"dining-table": { w: 1.6, h: 0.9, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true, true) },
|
|
37468
38734
|
sectional: {
|
|
37469
38735
|
w: 2.6,
|
|
37470
38736
|
h: 2,
|
|
@@ -37896,10 +39162,10 @@ var FLOORPLAN_SYMBOLS = {
|
|
|
37896
39162
|
"round-table-6": roundTable(6, 1.52),
|
|
37897
39163
|
"round-table-8": roundTable(8, 1.52),
|
|
37898
39164
|
"round-table-10": roundTable(10, 1.83),
|
|
37899
|
-
"conference-table": { w: 2.4, h: 1.2, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true) },
|
|
39165
|
+
"conference-table": { w: 2.4, h: 1.2, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true, true) },
|
|
37900
39166
|
// ── event / banquet ──
|
|
37901
|
-
"banquet-table": { w: 2.44, h: 0.76, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true) },
|
|
37902
|
-
"head-table": { w: 3.7, h: 0.76, envelope: [0, 0, CHAIR_OVERHANG, 0], draw: tableDraw(false) },
|
|
39167
|
+
"banquet-table": { w: 2.44, h: 0.76, envelope: [CHAIR_OVERHANG, 0, CHAIR_OVERHANG, 0], draw: tableDraw(true, true) },
|
|
39168
|
+
"head-table": { w: 3.7, h: 0.76, envelope: [0, 0, CHAIR_OVERHANG, 0], draw: tableDraw(false, true) },
|
|
37903
39169
|
stage: {
|
|
37904
39170
|
w: 4,
|
|
37905
39171
|
h: 2,
|
|
@@ -38149,6 +39415,118 @@ var FLOORPLAN_SYMBOLS = {
|
|
|
38149
39415
|
underlay: true,
|
|
38150
39416
|
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) })
|
|
38151
39417
|
},
|
|
39418
|
+
// ── restaurant / commercial kitchen ──
|
|
39419
|
+
// Restaurant booth: two facing benches (chair fill) with a table between.
|
|
39420
|
+
// Default seats 4 (two per bench); benches are not auto-named seats.
|
|
39421
|
+
booth: {
|
|
39422
|
+
w: 1.4,
|
|
39423
|
+
h: 1.6,
|
|
39424
|
+
envelope: [0, 0, 0, 0],
|
|
39425
|
+
draw: (c) => {
|
|
39426
|
+
const benchH = Math.min(0.45, c.h * 0.28);
|
|
39427
|
+
const inset = c.w * 0.12;
|
|
39428
|
+
return [
|
|
39429
|
+
rect({ class: "sx-fp-chair", x: 0, y: 0, width: c.px(c.w), height: c.px(benchH), rx: c.px(0.06) }),
|
|
39430
|
+
rect({ class: "sx-fp-chair", x: 0, y: c.px(c.h - benchH), width: c.px(c.w), height: c.px(benchH), rx: c.px(0.06) }),
|
|
39431
|
+
rect({
|
|
39432
|
+
class: "sx-fp-furn",
|
|
39433
|
+
x: c.px(inset),
|
|
39434
|
+
y: c.px(benchH + 0.06),
|
|
39435
|
+
width: c.px(c.w - 2 * inset),
|
|
39436
|
+
height: c.px(Math.max(0.1, c.h - 2 * benchH - 0.12)),
|
|
39437
|
+
rx: c.px(0.04)
|
|
39438
|
+
})
|
|
39439
|
+
].join("");
|
|
39440
|
+
}
|
|
39441
|
+
},
|
|
39442
|
+
// Stainless prep / work table: solid top with a dashed under-shelf outline.
|
|
39443
|
+
"prep-table": {
|
|
39444
|
+
w: 1.5,
|
|
39445
|
+
h: 0.75,
|
|
39446
|
+
draw: (c) => box(c) + rect({
|
|
39447
|
+
class: "sx-fp-furn-dash",
|
|
39448
|
+
x: c.px(0.08),
|
|
39449
|
+
y: c.px(0.08),
|
|
39450
|
+
width: c.px(Math.max(0.1, c.w - 0.16)),
|
|
39451
|
+
height: c.px(Math.max(0.1, c.h - 0.16))
|
|
39452
|
+
})
|
|
39453
|
+
},
|
|
39454
|
+
// Commercial range: 6 burners (2 rows × 3 cols) over an oven (front line).
|
|
39455
|
+
range: {
|
|
39456
|
+
w: 0.9,
|
|
39457
|
+
h: 0.85,
|
|
39458
|
+
draw: (c) => {
|
|
39459
|
+
const parts = [box(c)];
|
|
39460
|
+
const r7 = Math.min(c.w / 6, c.h / 8);
|
|
39461
|
+
for (let row = 0; row < 2; row++) {
|
|
39462
|
+
for (let col = 0; col < 3; col++) {
|
|
39463
|
+
const cx = (col + 0.5) / 3 * c.w;
|
|
39464
|
+
const cy = (row + 0.5) / 4 * c.h;
|
|
39465
|
+
parts.push(circle({ class: "sx-fp-furn-line", cx: c.px(cx), cy: c.px(cy), r: c.px(r7) }));
|
|
39466
|
+
}
|
|
39467
|
+
}
|
|
39468
|
+
parts.push(line({ class: "sx-fp-furn-line", x1: 0, y1: c.px(c.h * 0.62), x2: c.px(c.w), y2: c.px(c.h * 0.62) }));
|
|
39469
|
+
return parts.join("");
|
|
39470
|
+
}
|
|
39471
|
+
},
|
|
39472
|
+
// Walk-in cooler/freezer: insulated double-wall box with a door gap + label.
|
|
39473
|
+
"walk-in": {
|
|
39474
|
+
w: 2.4,
|
|
39475
|
+
h: 2,
|
|
39476
|
+
draw: (c) => {
|
|
39477
|
+
const t = Math.min(0.12, c.w * 0.06);
|
|
39478
|
+
const doorW = Math.min(0.9, c.w * 0.4);
|
|
39479
|
+
const parts = [
|
|
39480
|
+
box(c),
|
|
39481
|
+
rect({
|
|
39482
|
+
class: "sx-fp-furn-line",
|
|
39483
|
+
x: c.px(t),
|
|
39484
|
+
y: c.px(t),
|
|
39485
|
+
width: c.px(Math.max(0.1, c.w - 2 * t)),
|
|
39486
|
+
height: c.px(Math.max(0.1, c.h - 2 * t))
|
|
39487
|
+
}),
|
|
39488
|
+
// door gap on the bottom wall + a hinged leaf
|
|
39489
|
+
rect({ class: "sx-fp-furn-solid", x: c.px((c.w - doorW) / 2), y: c.px(c.h - t), width: c.px(doorW), height: c.px(t) }),
|
|
39490
|
+
line({
|
|
39491
|
+
class: "sx-fp-door-leaf",
|
|
39492
|
+
x1: c.px((c.w - doorW) / 2),
|
|
39493
|
+
y1: c.px(c.h),
|
|
39494
|
+
x2: c.px((c.w - doorW) / 2),
|
|
39495
|
+
y2: c.px(c.h + doorW * 0.6)
|
|
39496
|
+
}),
|
|
39497
|
+
glyphText(c, "WALK-IN")
|
|
39498
|
+
];
|
|
39499
|
+
return parts.join("");
|
|
39500
|
+
}
|
|
39501
|
+
},
|
|
39502
|
+
// Three-compartment commercial sink: three basins + a faucet dot per basin.
|
|
39503
|
+
"commercial-sink": {
|
|
39504
|
+
w: 1.8,
|
|
39505
|
+
h: 0.6,
|
|
39506
|
+
draw: (c) => {
|
|
39507
|
+
const parts = [box(c)];
|
|
39508
|
+
const gap = c.w * 0.04;
|
|
39509
|
+
const bw = (c.w - 4 * gap) / 3;
|
|
39510
|
+
for (let i = 0; i < 3; i++) {
|
|
39511
|
+
const bx = gap + i * (bw + gap);
|
|
39512
|
+
parts.push(rect({ class: "sx-fp-furn-line", x: c.px(bx), y: c.px(c.h * 0.18), width: c.px(bw), height: c.px(c.h * 0.64), rx: c.px(0.03) }));
|
|
39513
|
+
parts.push(circle({ class: "sx-fp-furn-dot", cx: c.px(bx + bw / 2), cy: c.px(c.h * 0.12), r: c.px(0.04) }));
|
|
39514
|
+
}
|
|
39515
|
+
return parts.join("");
|
|
39516
|
+
}
|
|
39517
|
+
},
|
|
39518
|
+
// Deep fryer: two fry vats with handles.
|
|
39519
|
+
fryer: {
|
|
39520
|
+
w: 0.4,
|
|
39521
|
+
h: 0.8,
|
|
39522
|
+
draw: (c) => {
|
|
39523
|
+
const parts = [box(c)];
|
|
39524
|
+
for (const cy of [c.h * 0.28, c.h * 0.72]) {
|
|
39525
|
+
parts.push(rect({ class: "sx-fp-furn-line", x: c.px(c.w * 0.18), y: c.px(cy - c.h * 0.16), width: c.px(c.w * 0.64), height: c.px(c.h * 0.32), rx: c.px(0.02) }));
|
|
39526
|
+
}
|
|
39527
|
+
return parts.join("");
|
|
39528
|
+
}
|
|
39529
|
+
},
|
|
38152
39530
|
// ── site / outdoor ──
|
|
38153
39531
|
// Tree in plan: a canopy disc with a foliage ring and a trunk dot.
|
|
38154
39532
|
tree: {
|
|
@@ -38396,7 +39774,14 @@ function parseFurniture(tok, ast, ln) {
|
|
|
38396
39774
|
f.y = c.y;
|
|
38397
39775
|
} else if (t.word === "size") f.size = parseDims(tok.shift(), "size", ln);
|
|
38398
39776
|
else if (t.word === "rotate") f.rotate = parseNum2(tok.shift(), "rotate", ln);
|
|
38399
|
-
else
|
|
39777
|
+
else if (t.word === "seats") {
|
|
39778
|
+
const names = [];
|
|
39779
|
+
while (isStr(tok[0])) names.push(tok.shift().str);
|
|
39780
|
+
if (names.length === 0) {
|
|
39781
|
+
throw new FloorplanParseError(`"seats" expects one or more quoted names`, ln);
|
|
39782
|
+
}
|
|
39783
|
+
f.seats = names;
|
|
39784
|
+
} else throw new FloorplanParseError(`furniture: unexpected token "${t.word}"`, ln);
|
|
38400
39785
|
}
|
|
38401
39786
|
ast.furniture.push(f);
|
|
38402
39787
|
}
|
|
@@ -38826,11 +40211,11 @@ function layoutFloorplan(ast) {
|
|
|
38826
40211
|
}
|
|
38827
40212
|
const items = [];
|
|
38828
40213
|
const seqByType = /* @__PURE__ */ new Map();
|
|
38829
|
-
const place = (type, roomIdx, localX, localY, w, h, rotate, label) => {
|
|
40214
|
+
const place = (type, roomIdx, localX, localY, w, h, rotate, label, seats) => {
|
|
38830
40215
|
const room = rooms[roomIdx];
|
|
38831
40216
|
const seq = (seqByType.get(type) ?? 0) + 1;
|
|
38832
40217
|
seqByType.set(type, seq);
|
|
38833
|
-
items.push({ type, x: room.x + localX, y: room.y + localY, w, h, rotate, label, roomId: room.id, seq });
|
|
40218
|
+
items.push({ type, x: room.x + localX, y: room.y + localY, w, h, rotate, label, seats, roomId: room.id, seq });
|
|
38834
40219
|
};
|
|
38835
40220
|
const roomIdxOf = (stmt, roomId, line2) => {
|
|
38836
40221
|
if (!roomId) {
|
|
@@ -38850,7 +40235,7 @@ function layoutFloorplan(ast) {
|
|
|
38850
40235
|
if (idx === void 0) continue;
|
|
38851
40236
|
const w = f.size ? f.size.w * u : def.w;
|
|
38852
40237
|
const h = f.size ? f.size.h * u : def.h;
|
|
38853
|
-
place(f.type, idx, f.x * u, f.y * u, w, h, f.rotate, f.label);
|
|
40238
|
+
place(f.type, idx, f.x * u, f.y * u, w, h, f.rotate, f.label, f.seats);
|
|
38854
40239
|
}
|
|
38855
40240
|
for (const a of ast.arrays) {
|
|
38856
40241
|
const def = FLOORPLAN_SYMBOLS[a.type];
|
|
@@ -39171,6 +40556,7 @@ function buildCss13(t) {
|
|
|
39171
40556
|
.sx-fp-hatch { fill: none; stroke: ${t.hatchStroke}; stroke-width: 1; }
|
|
39172
40557
|
.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; }
|
|
39173
40558
|
.sx-fp-furn-label { font: 11px sans-serif; fill: ${t.furnLabel}; paint-order: stroke; stroke: ${t.floorFill}; stroke-width: 3px; stroke-linejoin: round; }
|
|
40559
|
+
.sx-fp-seat-name { font-family: sans-serif; fill: ${t.furnLabel}; paint-order: stroke; stroke: ${t.floorFill}; stroke-width: 2px; stroke-linejoin: round; }
|
|
39174
40560
|
.sx-fp-door-leaf { fill: none; stroke: ${t.doorLeaf}; stroke-width: 1.6; }
|
|
39175
40561
|
.sx-fp-door-arc { fill: none; stroke: ${t.doorArc}; stroke-width: 1; }
|
|
39176
40562
|
.sx-fp-window { fill: none; stroke: ${t.windowStroke}; stroke-width: 1.3; }
|
|
@@ -39456,7 +40842,7 @@ function renderFloorplanLayout(lay, config) {
|
|
|
39456
40842
|
const cx = r24(X(it.x) + wpx / 2);
|
|
39457
40843
|
const cy = r24(Y(it.y) + hpx / 2);
|
|
39458
40844
|
const rot = Math.round(it.rotate * 10) / 10;
|
|
39459
|
-
const children = [def.draw({ w: it.w, h: it.h, px, label: it.label })];
|
|
40845
|
+
const children = [def.draw({ w: it.w, h: it.h, px, label: it.label, seats: it.seats })];
|
|
39460
40846
|
if (warnSet.has(idx)) {
|
|
39461
40847
|
children.push(rect({ class: "sx-fp-warn-item", x: -1, y: -1, width: r24(wpx + 2), height: r24(hpx + 2) }));
|
|
39462
40848
|
}
|
|
@@ -41158,6 +42544,7 @@ var plugins = [
|
|
|
41158
42544
|
eventtree,
|
|
41159
42545
|
fmea,
|
|
41160
42546
|
rbd,
|
|
42547
|
+
comparison,
|
|
41161
42548
|
causalloop,
|
|
41162
42549
|
markov,
|
|
41163
42550
|
gitgraph,
|
|
@@ -41314,6 +42701,6 @@ function renderWithPlugin(prepared, plugin, config) {
|
|
|
41314
42701
|
return plugin.render(prepared, renderConfig);
|
|
41315
42702
|
}
|
|
41316
42703
|
|
|
41317
|
-
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, rbd, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
|
|
41318
|
-
//# sourceMappingURL=chunk-
|
|
41319
|
-
//# sourceMappingURL=chunk-
|
|
42704
|
+
export { FLOORPLAN_SYMBOLS, GEOMETRY, bowtie2 as bowtie, causalloop, comparison, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, rbd, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
|
|
42705
|
+
//# sourceMappingURL=chunk-T5QOVX2I.js.map
|
|
42706
|
+
//# sourceMappingURL=chunk-T5QOVX2I.js.map
|