compasso 0.4.0 → 0.4.1
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 +29 -0
- package/dist/{chunk-RWPGGWO5.js → chunk-FYBABYC7.js} +34 -10
- package/dist/chunk-FYBABYC7.js.map +1 -0
- package/dist/{chunk-F47C6ZEB.js → chunk-IPE7JZO5.js} +3 -3
- package/dist/{chunk-F47C6ZEB.js.map → chunk-IPE7JZO5.js.map} +1 -1
- package/dist/{chunk-Q6DVTCXD.js → chunk-LR7BXUWM.js} +18 -6
- package/dist/chunk-LR7BXUWM.js.map +1 -0
- package/dist/{chunk-LRHHUJFZ.js → chunk-M4WA6ME7.js} +3 -3
- package/dist/{chunk-LRHHUJFZ.js.map → chunk-M4WA6ME7.js.map} +1 -1
- package/dist/{chunk-JP4N42AY.js → chunk-PGUMLTIM.js} +3 -3
- package/dist/{chunk-JP4N42AY.js.map → chunk-PGUMLTIM.js.map} +1 -1
- package/dist/{chunk-O3BT2O42.js → chunk-SD4NTRBM.js} +29 -3
- package/dist/chunk-SD4NTRBM.js.map +1 -0
- package/dist/{chunk-UJVU7B44.js → chunk-TAE2UB7D.js} +30 -14
- package/dist/chunk-TAE2UB7D.js.map +1 -0
- package/dist/{chunk-ZBDABVIO.js → chunk-WJYYBGZW.js} +3 -3
- package/dist/{chunk-ZBDABVIO.js.map → chunk-WJYYBGZW.js.map} +1 -1
- package/dist/core/index.cjs +30 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +18 -2
- package/dist/core/index.d.ts +18 -2
- package/dist/core/index.js +1 -1
- package/dist/ecomap/index.cjs +34 -11
- package/dist/ecomap/index.cjs.map +1 -1
- package/dist/ecomap/index.d.cts +12 -0
- package/dist/ecomap/index.d.ts +12 -0
- package/dist/ecomap/index.js +2 -2
- package/dist/fault-tree/index.js +2 -2
- package/dist/fishbone/index.js +2 -2
- package/dist/genogram/index.cjs +57 -7
- package/dist/genogram/index.cjs.map +1 -1
- package/dist/genogram/index.d.cts +20 -4
- package/dist/genogram/index.d.ts +20 -4
- package/dist/genogram/index.js +2 -2
- package/dist/index.cjs +325 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +8 -8
- package/dist/{kinship-DqEklrDN.d.ts → kinship-BF90HyyS.d.ts} +1 -1
- package/dist/{kinship-Dy_ijjJV.d.cts → kinship-BOUss5cT.d.cts} +1 -1
- package/dist/{labels-RtFw9tX1.d.cts → labels-B0aOMbHy.d.cts} +12 -0
- package/dist/{labels-RtFw9tX1.d.ts → labels-B0aOMbHy.d.ts} +12 -0
- package/dist/{labels-DNqRkWuI.d.ts → labels-CuLbFyrz.d.ts} +1 -1
- package/dist/{labels-CBQ_3Ec9.d.cts → labels-DhQe7I8m.d.cts} +1 -1
- package/dist/locales/pt-br.d.cts +4 -4
- package/dist/locales/pt-br.d.ts +4 -4
- package/dist/org-chart/index.cjs +100 -58
- package/dist/org-chart/index.cjs.map +1 -1
- package/dist/org-chart/index.d.cts +19 -2
- package/dist/org-chart/index.d.ts +19 -2
- package/dist/org-chart/index.js +2 -2
- package/dist/pedigree/index.d.cts +4 -4
- package/dist/pedigree/index.d.ts +4 -4
- package/dist/pedigree/index.js +2 -2
- package/dist/phylo/index.js +2 -2
- package/dist/{types-BnMG7TCd.d.cts → types-jE2fdM1t.d.cts} +8 -0
- package/dist/{types-BnMG7TCd.d.ts → types-jE2fdM1t.d.ts} +8 -0
- package/package.json +1 -1
- package/dist/chunk-O3BT2O42.js.map +0 -1
- package/dist/chunk-Q6DVTCXD.js.map +0 -1
- package/dist/chunk-RWPGGWO5.js.map +0 -1
- package/dist/chunk-UJVU7B44.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -142,6 +142,32 @@ function qualityLineStyle(quality, lexicon = QUALITY_LEXICON_EN) {
|
|
|
142
142
|
return matched.length === 1 ? matched[0] : "plain";
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// src/core/annotation.ts
|
|
146
|
+
var round = (n) => Math.round(n * 100) / 100;
|
|
147
|
+
var ANNOTATION_INK = "#52525b";
|
|
148
|
+
var DOT_R = 3;
|
|
149
|
+
var TICK_HALF = 3;
|
|
150
|
+
function annotationDot(cx, cy) {
|
|
151
|
+
return `<circle cx="${round(cx)}" cy="${round(cy)}" r="${DOT_R}" fill="${ANNOTATION_INK}" fill-opacity="0.6"/>`;
|
|
152
|
+
}
|
|
153
|
+
function annotationTick(points) {
|
|
154
|
+
if (points.length < 2) return "";
|
|
155
|
+
const i = Math.floor((points.length - 1) / 2);
|
|
156
|
+
const a = points[i];
|
|
157
|
+
const b = points[i + 1] ?? points[i];
|
|
158
|
+
const mx = (a.x + b.x) / 2;
|
|
159
|
+
const my = (a.y + b.y) / 2;
|
|
160
|
+
const horizontal = Math.abs(a.y - b.y) <= Math.abs(a.x - b.x);
|
|
161
|
+
const x1 = horizontal ? mx : mx - TICK_HALF;
|
|
162
|
+
const x2 = horizontal ? mx : mx + TICK_HALF;
|
|
163
|
+
const y1 = horizontal ? my - TICK_HALF : my;
|
|
164
|
+
const y2 = horizontal ? my + TICK_HALF : my;
|
|
165
|
+
return `<line x1="${round(x1)}" y1="${round(y1)}" x2="${round(x2)}" y2="${round(y2)}" stroke="${ANNOTATION_INK}" stroke-width="1.5"/>`;
|
|
166
|
+
}
|
|
167
|
+
function annotationSwatch(x, yCenter) {
|
|
168
|
+
return annotationDot(x + LEGEND_SWATCH_W / 2, yCenter);
|
|
169
|
+
}
|
|
170
|
+
|
|
145
171
|
// src/genogram/types.ts
|
|
146
172
|
var UNION_STATUSES = [
|
|
147
173
|
"married",
|
|
@@ -317,7 +343,8 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
317
343
|
parentId: l.parentId,
|
|
318
344
|
childId: l.childId,
|
|
319
345
|
quality: l.quality,
|
|
320
|
-
edgeId: PARENT_REL_ID_BASE + l.id
|
|
346
|
+
edgeId: PARENT_REL_ID_BASE + l.id,
|
|
347
|
+
annotated: l.annotated ?? false
|
|
321
348
|
}));
|
|
322
349
|
const declaredPairs = new Set(validLinks.map((l) => pairKey(l.parentId, l.childId)));
|
|
323
350
|
const promotedByPair = /* @__PURE__ */ new Map();
|
|
@@ -336,7 +363,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
336
363
|
[parentId, childId] = fromIsChild ? [r.toPersonId, r.fromPersonId] : [r.fromPersonId, r.toPersonId];
|
|
337
364
|
}
|
|
338
365
|
if (parentId === childId) continue;
|
|
339
|
-
promotedByPair.set(key, { parentId, childId, quality: r.quality, edgeId: PROMOTED_REL_ID_BASE + r.id });
|
|
366
|
+
promotedByPair.set(key, { parentId, childId, quality: r.quality, edgeId: PROMOTED_REL_ID_BASE + r.id, annotated: r.annotated ?? false });
|
|
340
367
|
}
|
|
341
368
|
const allLinks = [...realLinks, ...promotedByPair.values()].sort((a, b) => a.edgeId - b.edgeId);
|
|
342
369
|
const coupleByPair = /* @__PURE__ */ new Map();
|
|
@@ -669,6 +696,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
669
696
|
toPersonId: u.personBId,
|
|
670
697
|
titles: [unionTitle(u)],
|
|
671
698
|
lineStyle: "plain",
|
|
699
|
+
annotated: u.annotated ?? false,
|
|
672
700
|
build: () => {
|
|
673
701
|
const cy = geo.cy(rowOfPerson.get(leftId));
|
|
674
702
|
if (dipLevel === 0) {
|
|
@@ -703,6 +731,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
703
731
|
toPersonId: u.personBId,
|
|
704
732
|
titles: [unionTitle(u)],
|
|
705
733
|
lineStyle: "plain",
|
|
734
|
+
annotated: u.annotated ?? false,
|
|
706
735
|
build
|
|
707
736
|
});
|
|
708
737
|
}
|
|
@@ -759,6 +788,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
759
788
|
};
|
|
760
789
|
gutterReqs.push(spineReq);
|
|
761
790
|
const aId = (c) => linkOf.get(`${u.personAId}>${c}`).edgeId;
|
|
791
|
+
const aLinkAnnotated = (c) => linkOf.get(`${u.personAId}>${c}`).annotated ?? false;
|
|
762
792
|
const childDrop = (c, parentId) => {
|
|
763
793
|
const link = linkOf.get(`${parentId}>${c}`);
|
|
764
794
|
const edgeId = link.edgeId;
|
|
@@ -771,6 +801,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
771
801
|
toPersonId: c,
|
|
772
802
|
titles: [linkTitle(link)],
|
|
773
803
|
lineStyle: "plain",
|
|
804
|
+
annotated: link.annotated,
|
|
774
805
|
build: () => {
|
|
775
806
|
const y = geo.corridorLaneY(sibReq);
|
|
776
807
|
const x = geo.cx(colOrThrow(c)) + arrivalOffset(arr.slot, arrivalCount.get(c) ?? 1);
|
|
@@ -791,6 +822,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
791
822
|
toPersonId: null,
|
|
792
823
|
titles: [linkTitle(linkOf.get(`${u.personAId}>${c}`))],
|
|
793
824
|
lineStyle: "plain",
|
|
825
|
+
annotated: aLinkAnnotated(c),
|
|
794
826
|
build: () => {
|
|
795
827
|
const sy = geo.corridorLaneY(sibReq);
|
|
796
828
|
const sx = geo.gutterCenterX(interGutter);
|
|
@@ -816,6 +848,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
816
848
|
// declared quality word is dropped from the drawn element (FIX C-2 / SPEC inv #4).
|
|
817
849
|
titles: [aLinkTitle(groupKids[0]), ...groupKids.slice(2).map(aLinkTitle)],
|
|
818
850
|
lineStyle: "plain",
|
|
851
|
+
annotated: [groupKids[0], ...groupKids.slice(2)].some(aLinkAnnotated),
|
|
819
852
|
build: () => {
|
|
820
853
|
const y = geo.corridorLaneY(sibReq);
|
|
821
854
|
const xs = sibSpan();
|
|
@@ -834,6 +867,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
834
867
|
titles: [aLinkTitle(groupKids[1])],
|
|
835
868
|
// the A-side link this spine carries, verbatim
|
|
836
869
|
lineStyle: "plain",
|
|
870
|
+
annotated: aLinkAnnotated(groupKids[1]),
|
|
837
871
|
build: () => {
|
|
838
872
|
const sy = geo.corridorLaneY(sibReq);
|
|
839
873
|
const x = geo.gutterCenterX(interGutter);
|
|
@@ -861,6 +895,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
861
895
|
titles: [linkTitle(l)],
|
|
862
896
|
lineStyle: "plain",
|
|
863
897
|
dotted: true,
|
|
898
|
+
annotated: l.annotated,
|
|
864
899
|
build
|
|
865
900
|
});
|
|
866
901
|
}
|
|
@@ -873,10 +908,11 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
873
908
|
const title = r.quality !== null ? `${r.type} \xB7 ${r.quality}` : r.type;
|
|
874
909
|
const g = bondGroups.get(key);
|
|
875
910
|
if (g === void 0) {
|
|
876
|
-
bondGroups.set(key, { relIds: [r.id], titles: [title], style, aId: r.fromPersonId, bId: r.toPersonId });
|
|
911
|
+
bondGroups.set(key, { relIds: [r.id], titles: [title], style, aId: r.fromPersonId, bId: r.toPersonId, annotated: r.annotated ?? false });
|
|
877
912
|
} else {
|
|
878
913
|
g.relIds.push(r.id);
|
|
879
914
|
g.titles.push(title);
|
|
915
|
+
if (r.annotated) g.annotated = true;
|
|
880
916
|
}
|
|
881
917
|
}
|
|
882
918
|
const bondList = [...bondGroups.values()].sort((a, b) => Math.max(...a.relIds) - Math.max(...b.relIds));
|
|
@@ -895,6 +931,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
895
931
|
toPersonId: g.bId,
|
|
896
932
|
titles: g.titles,
|
|
897
933
|
lineStyle: g.style,
|
|
934
|
+
annotated: g.annotated,
|
|
898
935
|
build
|
|
899
936
|
});
|
|
900
937
|
}
|
|
@@ -991,6 +1028,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
991
1028
|
label: p.label,
|
|
992
1029
|
shape: shapeForSex(p.sex),
|
|
993
1030
|
deceased: p.deceased,
|
|
1031
|
+
annotated: p.annotated ?? false,
|
|
994
1032
|
cx: colCenterX[c],
|
|
995
1033
|
cy: rowCenterY[r],
|
|
996
1034
|
size: NODE_SIZE,
|
|
@@ -1007,6 +1045,7 @@ function computeGenogramLayout(people, unions, parentLinks, relationships, opts
|
|
|
1007
1045
|
points: pl.build(),
|
|
1008
1046
|
titles: pl.titles,
|
|
1009
1047
|
lineStyle: pl.lineStyle,
|
|
1048
|
+
annotated: pl.annotated,
|
|
1010
1049
|
...pl.unionStyle !== void 0 ? { unionStyle: pl.unionStyle } : {},
|
|
1011
1050
|
...pl.dotted ? { dotted: true } : {}
|
|
1012
1051
|
}));
|
|
@@ -1111,6 +1150,7 @@ function elementSvg(el, override) {
|
|
|
1111
1150
|
}
|
|
1112
1151
|
return `<path d="${pathData2(pts)}" fill="none" stroke="${EDGE_INK}" stroke-width="${width}" stroke-opacity="${opacity}"${dashAttr}/>`;
|
|
1113
1152
|
};
|
|
1153
|
+
const tick = el.annotated ? annotationTick(el.points) : "";
|
|
1114
1154
|
if (el.kind === "union-bar" || el.kind === "union-elbow") {
|
|
1115
1155
|
const style = resolveUnionStyle(el, override);
|
|
1116
1156
|
const body = [drawLine(style.dash ?? null, STRUCT_WIDTH, STRUCT_OPACITY)];
|
|
@@ -1119,15 +1159,15 @@ function elementSvg(el, override) {
|
|
|
1119
1159
|
const [a, b] = pts.length === 2 ? [pts[0], pts[1]] : longestHSegment(pts);
|
|
1120
1160
|
body.push(slashMarks(a, b, slashes, STRUCT_WIDTH));
|
|
1121
1161
|
}
|
|
1122
|
-
return `<g data-edge-id="${el.edgeId}">${title}${body.join("")}</g>`;
|
|
1162
|
+
return `<g data-edge-id="${el.edgeId}">${title}${body.join("")}${tick}</g>`;
|
|
1123
1163
|
}
|
|
1124
1164
|
if (el.kind === "descent" || el.kind === "sibling-bar") {
|
|
1125
1165
|
const dash = el.dotted ? DOTTED_DASH : null;
|
|
1126
1166
|
const opacity = el.dotted ? DOTTED_OPACITY : STRUCT_OPACITY;
|
|
1127
|
-
return `<g data-edge-id="${el.edgeId}">${title}${drawLine(dash, STRUCT_WIDTH, opacity)}</g>`;
|
|
1167
|
+
return `<g data-edge-id="${el.edgeId}">${title}${drawLine(dash, STRUCT_WIDTH, opacity)}${tick}</g>`;
|
|
1128
1168
|
}
|
|
1129
1169
|
const ink = EDGE_STROKE[el.lineStyle];
|
|
1130
|
-
return `<g data-edge-id="${el.edgeId}">${title}${drawLine(ink.dash, ink.width, ink.opacity)}</g>`;
|
|
1170
|
+
return `<g data-edge-id="${el.edgeId}">${title}${drawLine(ink.dash, ink.width, ink.opacity)}${tick}</g>`;
|
|
1131
1171
|
}
|
|
1132
1172
|
function genogramLayoutSvg(layout, opts = {}) {
|
|
1133
1173
|
const override = opts.unionStyleByRelId ?? /* @__PURE__ */ new Map();
|
|
@@ -1145,6 +1185,9 @@ function genogramLayoutSvg(layout, opts = {}) {
|
|
|
1145
1185
|
`<line x1="${node.cx - half}" y1="${node.cy - half}" x2="${node.cx + half}" y2="${node.cy + half}" stroke="${GLYPH_STROKE}" stroke-width="2"/>`
|
|
1146
1186
|
);
|
|
1147
1187
|
}
|
|
1188
|
+
if (node.annotated) {
|
|
1189
|
+
pieces.push(annotationDot(node.cx + 0.7 * half, node.cy - 0.7 * half));
|
|
1190
|
+
}
|
|
1148
1191
|
const tspans = node.labelLines.map(
|
|
1149
1192
|
(line, i) => `<tspan x="${node.cx}" y="${node.labelTop + 10 + i * LABEL_LINE_H}">${xmlEscape(line)}</tspan>`
|
|
1150
1193
|
).join("");
|
|
@@ -1187,6 +1230,12 @@ function genogramLayoutSvg(layout, opts = {}) {
|
|
|
1187
1230
|
label: labels.isolated
|
|
1188
1231
|
});
|
|
1189
1232
|
}
|
|
1233
|
+
if (opts.annotationLabel !== void 0 && (layout.nodes.some((n) => n.annotated) || layout.elements.some((e) => e.annotated))) {
|
|
1234
|
+
entries.push({
|
|
1235
|
+
swatch: (x, y) => annotationSwatch(x, y),
|
|
1236
|
+
label: opts.annotationLabel
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1190
1239
|
const block = legendBlock(entries, layout.height);
|
|
1191
1240
|
if (block.svg !== "") {
|
|
1192
1241
|
parts.push(block.svg);
|
|
@@ -1238,7 +1287,8 @@ function genogramSvg(input, opts = {}) {
|
|
|
1238
1287
|
const svg = genogramLayoutSvg(layout, {
|
|
1239
1288
|
unionStyleByRelId,
|
|
1240
1289
|
...opts.legend === false ? { legend: false } : {},
|
|
1241
|
-
...opts.svgLabels !== void 0 ? { labels: opts.svgLabels } : {}
|
|
1290
|
+
...opts.svgLabels !== void 0 ? { labels: opts.svgLabels } : {},
|
|
1291
|
+
...opts.annotationLabel !== void 0 ? { annotationLabel: opts.annotationLabel } : {}
|
|
1242
1292
|
});
|
|
1243
1293
|
return { svg, layout };
|
|
1244
1294
|
}
|
|
@@ -1268,7 +1318,7 @@ var SINGLE_RING_MAX = 8;
|
|
|
1268
1318
|
var NODE_STROKE = "#52525b";
|
|
1269
1319
|
var LABEL_FILL = "#3f3f46";
|
|
1270
1320
|
var EDGE_INK2 = "#71717a";
|
|
1271
|
-
var
|
|
1321
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
1272
1322
|
function arrowHead(tipX, tipY, ux, uy, opacity) {
|
|
1273
1323
|
const LEN = 9;
|
|
1274
1324
|
const HALF_W = 4.5;
|
|
@@ -1277,9 +1327,9 @@ function arrowHead(tipX, tipY, ux, uy, opacity) {
|
|
|
1277
1327
|
const px = -uy;
|
|
1278
1328
|
const py = ux;
|
|
1279
1329
|
const points = [
|
|
1280
|
-
`${
|
|
1281
|
-
`${
|
|
1282
|
-
`${
|
|
1330
|
+
`${round2(tipX)},${round2(tipY)}`,
|
|
1331
|
+
`${round2(bx + px * HALF_W)},${round2(by + py * HALF_W)}`,
|
|
1332
|
+
`${round2(bx - px * HALF_W)},${round2(by - py * HALF_W)}`
|
|
1283
1333
|
].join(" ");
|
|
1284
1334
|
return `<polygon points="${points}" fill="${EDGE_INK2}" fill-opacity="${opacity}"/>`;
|
|
1285
1335
|
}
|
|
@@ -1369,7 +1419,7 @@ function ecomapSvg(input, opts = {}) {
|
|
|
1369
1419
|
const dashAttr = ink.dash === null ? "" : ` stroke-dasharray="${ink.dash[0]} ${ink.dash[1]}"`;
|
|
1370
1420
|
const title = s.tie.title ?? (s.tie.quality !== null ? `${s.tie.label} \xB7 ${s.tie.quality}` : s.tie.label);
|
|
1371
1421
|
const body = [
|
|
1372
|
-
`<line x1="${
|
|
1422
|
+
`<line x1="${round2(x1)}" y1="${round2(y1)}" x2="${round2(x2)}" y2="${round2(y2)}" stroke="${EDGE_INK2}" stroke-width="${ink.width}" stroke-opacity="${ink.opacity}"${dashAttr}/>`
|
|
1373
1423
|
];
|
|
1374
1424
|
if (s.tie.direction === "in" || s.tie.direction === "both") {
|
|
1375
1425
|
body.push(arrowHead(x2, y2, ux, uy, ink.opacity));
|
|
@@ -1381,19 +1431,25 @@ function ecomapSvg(input, opts = {}) {
|
|
|
1381
1431
|
}
|
|
1382
1432
|
{
|
|
1383
1433
|
const tspans = centerLines.map(
|
|
1384
|
-
(line, i) => `<tspan x="${
|
|
1434
|
+
(line, i) => `<tspan x="${round2(cx)}" y="${round2(cy - (centerLines.length - 1) * LINE_H / 2 + i * LINE_H + fontSize * 0.32)}">${xmlEscape(line)}</tspan>`
|
|
1385
1435
|
).join("");
|
|
1386
1436
|
parts.push(
|
|
1387
|
-
`<g data-individual-id="center"><title>${xmlEscape(input.centerLabel)}</title><circle cx="${
|
|
1437
|
+
`<g data-individual-id="center"><title>${xmlEscape(input.centerLabel)}</title><circle cx="${round2(cx)}" cy="${round2(cy)}" r="${round2(centerR)}" fill="transparent" stroke="${NODE_STROKE}" stroke-width="2"/><text text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${fontSize}" fill="${LABEL_FILL}">${tspans}</text></g>`
|
|
1388
1438
|
);
|
|
1389
1439
|
}
|
|
1390
1440
|
for (const s of sats) {
|
|
1391
1441
|
const tspans = s.lines.map(
|
|
1392
|
-
(line, i) => `<tspan x="${
|
|
1442
|
+
(line, i) => `<tspan x="${round2(s.x)}" y="${round2(s.y - (s.lines.length - 1) * LINE_H / 2 + i * LINE_H + fontSize * 0.32)}">${xmlEscape(line)}</tspan>`
|
|
1393
1443
|
).join("");
|
|
1394
|
-
|
|
1395
|
-
`<
|
|
1396
|
-
|
|
1444
|
+
const pieces = [
|
|
1445
|
+
`<title>${xmlEscape(s.tie.label)}</title>`,
|
|
1446
|
+
`<ellipse cx="${round2(s.x)}" cy="${round2(s.y)}" rx="${round2(s.rx)}" ry="${round2(s.ry)}" fill="transparent" stroke="${NODE_STROKE}" stroke-width="1.5"/>`,
|
|
1447
|
+
`<text text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${fontSize}" fill="${LABEL_FILL}">${tspans}</text>`
|
|
1448
|
+
];
|
|
1449
|
+
if (s.tie.annotated) {
|
|
1450
|
+
pieces.push(annotationDot(s.x + 0.7 * s.rx, s.y - 0.7 * s.ry));
|
|
1451
|
+
}
|
|
1452
|
+
parts.push(`<g data-individual-id="e${s.tie.id}">${pieces.join("")}</g>`);
|
|
1397
1453
|
}
|
|
1398
1454
|
if (opts.legend !== false && sats.length > 0) {
|
|
1399
1455
|
const entries = [];
|
|
@@ -1419,6 +1475,12 @@ function ecomapSvg(input, opts = {}) {
|
|
|
1419
1475
|
label: labels.direction
|
|
1420
1476
|
});
|
|
1421
1477
|
}
|
|
1478
|
+
if (opts.annotationLabel !== void 0 && sats.some((s) => s.tie.annotated)) {
|
|
1479
|
+
entries.push({
|
|
1480
|
+
swatch: (x, y) => annotationSwatch(x, y),
|
|
1481
|
+
label: opts.annotationLabel
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1422
1484
|
if (entries.length > 0) {
|
|
1423
1485
|
const block = legendBlock(entries, height);
|
|
1424
1486
|
parts.push(block.svg);
|
|
@@ -1681,7 +1743,7 @@ var FT_DROP_ID_BASE = 2e6;
|
|
|
1681
1743
|
var FT_BUS_ID_BASE = 3e6;
|
|
1682
1744
|
var FT_RISER_ID_BASE = 4e6;
|
|
1683
1745
|
var FT_CONDITION_ID_BASE = 5e6;
|
|
1684
|
-
var
|
|
1746
|
+
var round3 = (n) => Math.round(n * 100) / 100;
|
|
1685
1747
|
function wrapLeafLabel(displayLabel) {
|
|
1686
1748
|
const perLine = Math.min(20, Math.max(12, Math.ceil(displayLabel.length / 2) + 2));
|
|
1687
1749
|
return wrapLabel(displayLabel, perLine);
|
|
@@ -1868,14 +1930,14 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1868
1930
|
eventId: inst.event.id,
|
|
1869
1931
|
kind: inst.event.kind,
|
|
1870
1932
|
instance: instanceOf(inst),
|
|
1871
|
-
cx:
|
|
1872
|
-
top:
|
|
1873
|
-
nodeW:
|
|
1874
|
-
nodeH:
|
|
1875
|
-
glyphW:
|
|
1876
|
-
glyphH:
|
|
1933
|
+
cx: round3(inst.cx),
|
|
1934
|
+
top: round3(top),
|
|
1935
|
+
nodeW: round3(inst.m.nodeW),
|
|
1936
|
+
nodeH: round3(inst.m.nodeH),
|
|
1937
|
+
glyphW: round3(inst.m.glyphW),
|
|
1938
|
+
glyphH: round3(inst.m.glyphH),
|
|
1877
1939
|
labelLines: inst.m.labelLines,
|
|
1878
|
-
labelTop: labelTop === null ? null :
|
|
1940
|
+
labelTop: labelTop === null ? null : round3(labelTop),
|
|
1879
1941
|
code: inst.m.code,
|
|
1880
1942
|
depth: inst.depth,
|
|
1881
1943
|
title: inst.title
|
|
@@ -1893,8 +1955,8 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1893
1955
|
gates.push({
|
|
1894
1956
|
gateId: g.id,
|
|
1895
1957
|
type: g.type,
|
|
1896
|
-
cx:
|
|
1897
|
-
top:
|
|
1958
|
+
cx: round3(inst.cx),
|
|
1959
|
+
top: round3(gateTop),
|
|
1898
1960
|
glyphW,
|
|
1899
1961
|
glyphH,
|
|
1900
1962
|
title,
|
|
@@ -1911,8 +1973,8 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1911
1973
|
edgeId: FT_CONDITION_ID_BASE + g.id,
|
|
1912
1974
|
kind: "condition",
|
|
1913
1975
|
points: [
|
|
1914
|
-
{ x:
|
|
1915
|
-
{ x:
|
|
1976
|
+
{ x: round3(inst.cx + INHIBIT_W / 2), y: round3(gateTop + INHIBIT_CY) },
|
|
1977
|
+
{ x: round3(ovalLeft), y: round3(gateTop + INHIBIT_CY) }
|
|
1916
1978
|
],
|
|
1917
1979
|
instance: null,
|
|
1918
1980
|
title: titleLabels.condition
|
|
@@ -1922,8 +1984,8 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1922
1984
|
edgeId: FT_STEM_ID_BASE + g.id,
|
|
1923
1985
|
kind: "stem",
|
|
1924
1986
|
points: [
|
|
1925
|
-
{ x:
|
|
1926
|
-
{ x:
|
|
1987
|
+
{ x: round3(inst.cx), y: round3(rowTop[d] + inst.m.nodeH) },
|
|
1988
|
+
{ x: round3(inst.cx), y: round3(gateTop) }
|
|
1927
1989
|
],
|
|
1928
1990
|
instance: null,
|
|
1929
1991
|
title
|
|
@@ -1933,8 +1995,8 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1933
1995
|
edgeId: FT_DROP_ID_BASE + g.id,
|
|
1934
1996
|
kind: "drop",
|
|
1935
1997
|
points: [
|
|
1936
|
-
{ x:
|
|
1937
|
-
{ x:
|
|
1998
|
+
{ x: round3(inst.cx), y: round3(gateTop + glyphH) },
|
|
1999
|
+
{ x: round3(inst.cx), y: round3(by) }
|
|
1938
2000
|
],
|
|
1939
2001
|
instance: null,
|
|
1940
2002
|
title
|
|
@@ -1945,8 +2007,8 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1945
2007
|
edgeId: FT_BUS_ID_BASE + g.id,
|
|
1946
2008
|
kind: "bus",
|
|
1947
2009
|
points: [
|
|
1948
|
-
{ x:
|
|
1949
|
-
{ x:
|
|
2010
|
+
{ x: round3(Math.min(...xs)), y: round3(by) },
|
|
2011
|
+
{ x: round3(Math.max(...xs)), y: round3(by) }
|
|
1950
2012
|
],
|
|
1951
2013
|
instance: null,
|
|
1952
2014
|
title
|
|
@@ -1957,8 +2019,8 @@ function computeFaultTreeLayout(input, opts = {}) {
|
|
|
1957
2019
|
edgeId: FT_RISER_ID_BASE + c.event.id,
|
|
1958
2020
|
kind: "riser",
|
|
1959
2021
|
points: [
|
|
1960
|
-
{ x:
|
|
1961
|
-
{ x:
|
|
2022
|
+
{ x: round3(c.cx), y: round3(by) },
|
|
2023
|
+
{ x: round3(c.cx), y: round3(rowTop[d + 1]) }
|
|
1962
2024
|
],
|
|
1963
2025
|
instance: instanceOf(c),
|
|
1964
2026
|
title
|
|
@@ -1975,42 +2037,42 @@ var GLYPH_STROKE2 = "#52525b";
|
|
|
1975
2037
|
var LABEL_FILL2 = "#3f3f46";
|
|
1976
2038
|
var EDGE_INK3 = "#71717a";
|
|
1977
2039
|
var GLYPH_ATTRS = `fill="transparent" stroke="${GLYPH_STROKE2}" stroke-width="2"`;
|
|
1978
|
-
var
|
|
2040
|
+
var round4 = (n) => Math.round(n * 100) / 100;
|
|
1979
2041
|
function eventGlyph(n) {
|
|
1980
2042
|
const cx = n.cx;
|
|
1981
2043
|
const top = n.top;
|
|
1982
2044
|
if (n.kind === "intermediate") {
|
|
1983
|
-
return `<rect x="${
|
|
2045
|
+
return `<rect x="${round4(cx - n.nodeW / 2)}" y="${top}" width="${n.nodeW}" height="${n.nodeH}" rx="2" ${GLYPH_ATTRS}/>`;
|
|
1984
2046
|
}
|
|
1985
2047
|
if (n.kind === "basic") {
|
|
1986
|
-
return `<circle cx="${cx}" cy="${
|
|
2048
|
+
return `<circle cx="${cx}" cy="${round4(top + 22)}" r="22" ${GLYPH_ATTRS}/>`;
|
|
1987
2049
|
}
|
|
1988
2050
|
if (n.kind === "undeveloped") {
|
|
1989
|
-
const pts2 = `${cx},${top} ${
|
|
2051
|
+
const pts2 = `${cx},${top} ${round4(cx + 24)},${round4(top + 24)} ${cx},${round4(top + 48)} ${round4(cx - 24)},${round4(top + 24)}`;
|
|
1990
2052
|
return `<polygon points="${pts2}" ${GLYPH_ATTRS}/>`;
|
|
1991
2053
|
}
|
|
1992
2054
|
if (n.kind === "house") {
|
|
1993
|
-
const yB =
|
|
1994
|
-
const eave =
|
|
1995
|
-
const pts2 = `${
|
|
2055
|
+
const yB = round4(top + 40);
|
|
2056
|
+
const eave = round4(top + 16);
|
|
2057
|
+
const pts2 = `${round4(cx - 22)},${yB} ${round4(cx - 22)},${eave} ${cx},${top} ${round4(cx + 22)},${eave} ${round4(cx + 22)},${yB}`;
|
|
1996
2058
|
return `<polygon points="${pts2}" ${GLYPH_ATTRS}/>`;
|
|
1997
2059
|
}
|
|
1998
2060
|
if (n.kind === "conditioning") {
|
|
1999
|
-
return `<ellipse cx="${cx}" cy="${
|
|
2061
|
+
return `<ellipse cx="${cx}" cy="${round4(top + 16)}" rx="${round4(n.glyphW / 2)}" ry="16" ${GLYPH_ATTRS}/>`;
|
|
2000
2062
|
}
|
|
2001
|
-
const pts = `${cx},${top} ${
|
|
2063
|
+
const pts = `${cx},${top} ${round4(cx + 22)},${round4(top + 35)} ${round4(cx - 22)},${round4(top + 35)}`;
|
|
2002
2064
|
return `<polygon points="${pts}" ${GLYPH_ATTRS}/>`;
|
|
2003
2065
|
}
|
|
2004
2066
|
function nodeSvg(n) {
|
|
2005
2067
|
const pieces = [`<title>${xmlEscape(n.title)}</title>`, eventGlyph(n)];
|
|
2006
2068
|
if (n.code !== null && n.kind !== "intermediate") {
|
|
2007
2069
|
pieces.push(
|
|
2008
|
-
`<text x="${n.cx}" y="${
|
|
2070
|
+
`<text x="${n.cx}" y="${round4(n.top + n.glyphH / 2 + CODE_FONT * 0.32)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${CODE_FONT}" fill="${LABEL_FILL2}">${xmlEscape(n.code)}</text>`
|
|
2009
2071
|
);
|
|
2010
2072
|
}
|
|
2011
2073
|
if (n.labelLines.length > 0) {
|
|
2012
|
-
const firstBaseline = n.labelTop === null ?
|
|
2013
|
-
const tspans = n.labelLines.map((line, i) => `<tspan x="${n.cx}" y="${
|
|
2074
|
+
const firstBaseline = n.labelTop === null ? round4(n.top + 19) : round4(n.labelTop + 10);
|
|
2075
|
+
const tspans = n.labelLines.map((line, i) => `<tspan x="${n.cx}" y="${round4(firstBaseline + i * FT_LABEL_LINE_H)}">${xmlEscape(line)}</tspan>`).join("");
|
|
2014
2076
|
pieces.push(
|
|
2015
2077
|
`<text text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${FT_LABEL_FONT}" fill="${LABEL_FILL2}">${tspans}</text>`
|
|
2016
2078
|
);
|
|
@@ -2019,28 +2081,28 @@ function nodeSvg(n) {
|
|
|
2019
2081
|
return `<g data-node-id="e${n.eventId}"${instance}>${pieces.join("")}</g>`;
|
|
2020
2082
|
}
|
|
2021
2083
|
function orBodyPath(cx, top, yB) {
|
|
2022
|
-
return `M ${
|
|
2084
|
+
return `M ${round4(cx - 22)} ${yB} Q ${cx} ${round4(yB - 14)} ${round4(cx + 22)} ${yB} Q ${round4(cx + 22)} ${round4(top + 14)} ${cx} ${top} Q ${round4(cx - 22)} ${round4(top + 14)} ${round4(cx - 22)} ${yB} Z`;
|
|
2023
2085
|
}
|
|
2024
2086
|
function gateGlyph(g) {
|
|
2025
2087
|
const cx = g.cx;
|
|
2026
2088
|
const top = g.top;
|
|
2027
|
-
const yB =
|
|
2089
|
+
const yB = round4(top + 36);
|
|
2028
2090
|
if (g.type === "and") {
|
|
2029
|
-
const d = `M ${
|
|
2091
|
+
const d = `M ${round4(cx - 22)} ${yB} L ${round4(cx - 22)} ${round4(yB - 14)} A 22 22 0 0 1 ${round4(cx + 22)} ${round4(yB - 14)} L ${round4(cx + 22)} ${yB} Z`;
|
|
2030
2092
|
return `<path d="${d}" ${GLYPH_ATTRS}/>`;
|
|
2031
2093
|
}
|
|
2032
2094
|
if (g.type === "or") {
|
|
2033
2095
|
return `<path d="${orBodyPath(cx, top, yB)}" ${GLYPH_ATTRS}/>`;
|
|
2034
2096
|
}
|
|
2035
2097
|
if (g.type === "xor") {
|
|
2036
|
-
const arc = `M ${
|
|
2098
|
+
const arc = `M ${round4(cx - 22)} ${round4(yB + 5)} Q ${cx} ${round4(yB - 9)} ${round4(cx + 22)} ${round4(yB + 5)}`;
|
|
2037
2099
|
return `<path d="${orBodyPath(cx, top, yB)}" ${GLYPH_ATTRS}/><path d="${arc}" ${GLYPH_ATTRS}/>`;
|
|
2038
2100
|
}
|
|
2039
2101
|
if (g.type === "inhibit") {
|
|
2040
|
-
const pts = `${cx},${top} ${
|
|
2102
|
+
const pts = `${cx},${top} ${round4(cx + 18)},${round4(top + 12)} ${round4(cx + 18)},${round4(top + 34)} ${cx},${round4(top + 46)} ${round4(cx - 18)},${round4(top + 34)} ${round4(cx - 18)},${round4(top + 12)}`;
|
|
2041
2103
|
return `<polygon points="${pts}" ${GLYPH_ATTRS}/>`;
|
|
2042
2104
|
}
|
|
2043
|
-
return `<path d="${orBodyPath(cx, top, yB)}" ${GLYPH_ATTRS}/><text x="${cx}" y="${
|
|
2105
|
+
return `<path d="${orBodyPath(cx, top, yB)}" ${GLYPH_ATTRS}/><text x="${cx}" y="${round4(yB - 10)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="9" fill="${LABEL_FILL2}">${xmlEscape(g.voteText ?? "")}</text>`;
|
|
2044
2106
|
}
|
|
2045
2107
|
function gateSvg(g) {
|
|
2046
2108
|
return `<g data-node-id="g${g.gateId}"><title>${xmlEscape(g.title)}</title>${gateGlyph(g)}</g>`;
|
|
@@ -2053,34 +2115,34 @@ function elementSvg2(el) {
|
|
|
2053
2115
|
}
|
|
2054
2116
|
var MINI_ATTRS = `fill="transparent" stroke="${GLYPH_STROKE2}" stroke-width="1.5"`;
|
|
2055
2117
|
function miniEventSwatch(kind, x, y) {
|
|
2056
|
-
const cx =
|
|
2118
|
+
const cx = round4(x + LEGEND_SWATCH_W / 2);
|
|
2057
2119
|
if (kind === "intermediate") {
|
|
2058
|
-
return `<rect x="${
|
|
2120
|
+
return `<rect x="${round4(cx - 7)}" y="${round4(y - 4.5)}" width="14" height="9" rx="1" ${MINI_ATTRS}/>`;
|
|
2059
2121
|
}
|
|
2060
2122
|
if (kind === "basic") return `<circle cx="${cx}" cy="${y}" r="6" ${MINI_ATTRS}/>`;
|
|
2061
2123
|
if (kind === "undeveloped") {
|
|
2062
|
-
return `<polygon points="${cx},${
|
|
2124
|
+
return `<polygon points="${cx},${round4(y - 7)} ${round4(cx + 7)},${y} ${cx},${round4(y + 7)} ${round4(cx - 7)},${y}" ${MINI_ATTRS}/>`;
|
|
2063
2125
|
}
|
|
2064
2126
|
if (kind === "house") {
|
|
2065
|
-
return `<polygon points="${
|
|
2127
|
+
return `<polygon points="${round4(cx - 6)},${round4(y + 5.5)} ${round4(cx - 6)},${round4(y - 1)} ${cx},${round4(y - 5.5)} ${round4(cx + 6)},${round4(y - 1)} ${round4(cx + 6)},${round4(y + 5.5)}" ${MINI_ATTRS}/>`;
|
|
2066
2128
|
}
|
|
2067
2129
|
if (kind === "conditioning") return `<ellipse cx="${cx}" cy="${y}" rx="9" ry="5.5" ${MINI_ATTRS}/>`;
|
|
2068
|
-
return `<polygon points="${cx},${
|
|
2130
|
+
return `<polygon points="${cx},${round4(y - 5)} ${round4(cx + 6)},${round4(y + 5)} ${round4(cx - 6)},${round4(y + 5)}" ${MINI_ATTRS}/>`;
|
|
2069
2131
|
}
|
|
2070
2132
|
function miniOrPath(cx, y) {
|
|
2071
|
-
return `M ${
|
|
2133
|
+
return `M ${round4(cx - 7)} ${round4(y + 5.5)} Q ${cx} ${round4(y + 1)} ${round4(cx + 7)} ${round4(y + 5.5)} Q ${round4(cx + 7)} ${round4(y - 1.5)} ${cx} ${round4(y - 5.5)} Q ${round4(cx - 7)} ${round4(y - 1.5)} ${round4(cx - 7)} ${round4(y + 5.5)} Z`;
|
|
2072
2134
|
}
|
|
2073
2135
|
function miniGateSwatch(type, x, y) {
|
|
2074
|
-
const cx =
|
|
2136
|
+
const cx = round4(x + LEGEND_SWATCH_W / 2);
|
|
2075
2137
|
if (type === "and") {
|
|
2076
|
-
const d = `M ${
|
|
2138
|
+
const d = `M ${round4(cx - 7)} ${round4(y + 5.5)} L ${round4(cx - 7)} ${round4(y + 1.5)} A 7 7 0 0 1 ${round4(cx + 7)} ${round4(y + 1.5)} L ${round4(cx + 7)} ${round4(y + 5.5)} Z`;
|
|
2077
2139
|
return `<path d="${d}" ${MINI_ATTRS}/>`;
|
|
2078
2140
|
}
|
|
2079
2141
|
if (type === "xor") {
|
|
2080
|
-
return `<path d="${miniOrPath(cx, y)}" ${MINI_ATTRS}/><path d="M ${
|
|
2142
|
+
return `<path d="${miniOrPath(cx, y)}" ${MINI_ATTRS}/><path d="M ${round4(cx - 7)} ${round4(y + 7.5)} Q ${cx} ${round4(y + 3)} ${round4(cx + 7)} ${round4(y + 7.5)}" ${MINI_ATTRS}/>`;
|
|
2081
2143
|
}
|
|
2082
2144
|
if (type === "inhibit") {
|
|
2083
|
-
return `<polygon points="${cx},${
|
|
2145
|
+
return `<polygon points="${cx},${round4(y - 5.5)} ${round4(cx + 4.5)},${round4(y - 2.5)} ${round4(cx + 4.5)},${round4(y + 2.5)} ${cx},${round4(y + 5.5)} ${round4(cx - 4.5)},${round4(y + 2.5)} ${round4(cx - 4.5)},${round4(y - 2.5)}" ${MINI_ATTRS}/>`;
|
|
2084
2146
|
}
|
|
2085
2147
|
return `<path d="${miniOrPath(cx, y)}" ${MINI_ATTRS}/>`;
|
|
2086
2148
|
}
|
|
@@ -2209,7 +2271,7 @@ var TWIG_W = 1.5;
|
|
|
2209
2271
|
var TWIG_OP = 0.75;
|
|
2210
2272
|
var SUB_W = 1.2;
|
|
2211
2273
|
var SUB_OP = 0.7;
|
|
2212
|
-
var
|
|
2274
|
+
var round5 = (n) => Math.round(n * 100) / 100;
|
|
2213
2275
|
function arrowHead2(tipX, tipY, ux, uy, opacity) {
|
|
2214
2276
|
const LEN = 9;
|
|
2215
2277
|
const HALF_W = 4.5;
|
|
@@ -2218,14 +2280,14 @@ function arrowHead2(tipX, tipY, ux, uy, opacity) {
|
|
|
2218
2280
|
const px = -uy;
|
|
2219
2281
|
const py = ux;
|
|
2220
2282
|
const points = [
|
|
2221
|
-
`${
|
|
2222
|
-
`${
|
|
2223
|
-
`${
|
|
2283
|
+
`${round5(tipX)},${round5(tipY)}`,
|
|
2284
|
+
`${round5(bx + px * HALF_W)},${round5(by + py * HALF_W)}`,
|
|
2285
|
+
`${round5(bx - px * HALF_W)},${round5(by - py * HALF_W)}`
|
|
2224
2286
|
].join(" ");
|
|
2225
2287
|
return `<polygon points="${points}" fill="${EDGE_INK4}" fill-opacity="${opacity}"/>`;
|
|
2226
2288
|
}
|
|
2227
2289
|
function lineEl(x1, y1, x2, y2, w, op) {
|
|
2228
|
-
return `<line x1="${
|
|
2290
|
+
return `<line x1="${round5(x1)}" y1="${round5(y1)}" x2="${round5(x2)}" y2="${round5(y2)}" stroke="${EDGE_INK4}" stroke-width="${w}" stroke-opacity="${op}"/>`;
|
|
2229
2291
|
}
|
|
2230
2292
|
function fishboneSvg(input, opts = {}) {
|
|
2231
2293
|
validateIds(input);
|
|
@@ -2306,7 +2368,7 @@ function fishboneSvg(input, opts = {}) {
|
|
|
2306
2368
|
{ length: n },
|
|
2307
2369
|
(_, k) => up ? spineY - (e + twigGap + (n - 1 - k) * lineH) : spineY + e + twigGap + ascent + k * lineH
|
|
2308
2370
|
);
|
|
2309
|
-
const textBlock = (anchor, x, ys, lines) => `<text text-anchor="${anchor}" font-family="${FONT_FAMILY}" font-size="${fontSize}" fill="${LABEL_FILL3}">` + lines.map((line, i) => `<tspan x="${
|
|
2371
|
+
const textBlock = (anchor, x, ys, lines) => `<text text-anchor="${anchor}" font-family="${FONT_FAMILY}" font-size="${fontSize}" fill="${LABEL_FILL3}">` + lines.map((line, i) => `<tspan x="${round5(x)}" y="${round5(ys[i])}">${xmlEscape(line)}</tspan>`).join("") + `</text>`;
|
|
2310
2372
|
const parts = [];
|
|
2311
2373
|
{
|
|
2312
2374
|
const body = [lineEl(tailX + dx, spineY, headLeft + dx, spineY, SPINE_W, SPINE_OP)];
|
|
@@ -2321,7 +2383,7 @@ function fishboneSvg(input, opts = {}) {
|
|
|
2321
2383
|
if (arrows) body.push(arrowHead2(ax, spineY, COS60, -sgn * SIN60, BONE_OP));
|
|
2322
2384
|
const boxTop = bone.up ? spineY - bone.B - CAT_GAP - bone.boxH : spineY + bone.B + CAT_GAP;
|
|
2323
2385
|
body.push(
|
|
2324
|
-
`<rect x="${
|
|
2386
|
+
`<rect x="${round5(tipX - bone.boxW / 2)}" y="${round5(boxTop)}" width="${round5(bone.boxW)}" height="${round5(bone.boxH)}" rx="2" fill="transparent" stroke="${BOX_STROKE}" stroke-width="1.5"/>`
|
|
2325
2387
|
);
|
|
2326
2388
|
body.push(textBlock("middle", tipX, centeredYs(boxTop + bone.boxH / 2, bone.catLines.length), bone.catLines));
|
|
2327
2389
|
parts.push(
|
|
@@ -2351,7 +2413,7 @@ function fishboneSvg(input, opts = {}) {
|
|
|
2351
2413
|
{
|
|
2352
2414
|
const x = headLeft + dx;
|
|
2353
2415
|
parts.push(
|
|
2354
|
-
`<g data-node-id="head"><title>${xmlEscape(input.effectLabel)}</title><rect x="${
|
|
2416
|
+
`<g data-node-id="head"><title>${xmlEscape(input.effectLabel)}</title><rect x="${round5(x)}" y="${round5(spineY - headH / 2)}" width="${round5(headW)}" height="${round5(headH)}" rx="2" fill="transparent" stroke="${BOX_STROKE}" stroke-width="2"/>` + textBlock("middle", x + headW / 2, centeredYs(spineY, effLines.length), effLines) + `</g>`
|
|
2355
2417
|
);
|
|
2356
2418
|
}
|
|
2357
2419
|
const anySubs = input.categories.some((c) => c.causes.some((k) => k.subCauses.length > 0));
|
|
@@ -2619,7 +2681,7 @@ var PED_DESCENT_ID_BASE = 2e6;
|
|
|
2619
2681
|
var PED_SIBBAR_ID_BASE = 3e6;
|
|
2620
2682
|
var PED_RISER_ID_BASE = 4e6;
|
|
2621
2683
|
var PED_TWINBAR_ID_BASE = 5e6;
|
|
2622
|
-
var
|
|
2684
|
+
var round6 = (n) => Math.round(n * 100) / 100;
|
|
2623
2685
|
function shapeForSex2(sex) {
|
|
2624
2686
|
if (sex === "male") return "square";
|
|
2625
2687
|
if (sex === "female") return "circle";
|
|
@@ -2885,8 +2947,8 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
2885
2947
|
nodes.push({
|
|
2886
2948
|
individualId: ind.id,
|
|
2887
2949
|
shape: shapeForSex2(ind.sex),
|
|
2888
|
-
cx:
|
|
2889
|
-
cy:
|
|
2950
|
+
cx: round6(placed.cx),
|
|
2951
|
+
cy: round6(cy),
|
|
2890
2952
|
size: PED_GLYPH,
|
|
2891
2953
|
deceased: ind.deceased,
|
|
2892
2954
|
carrier: ind.carrier,
|
|
@@ -2896,7 +2958,7 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
2896
2958
|
labelLines: lines,
|
|
2897
2959
|
// Labels sit BELOW the row's serial-union dip band (rowDipBand) so a hub's bridge dips
|
|
2898
2960
|
// never cross a label box.
|
|
2899
|
-
labelTop:
|
|
2961
|
+
labelTop: round6(cy + PED_GLYPH / 2 + PED_LABEL_GAP + rowDipBand[row]),
|
|
2900
2962
|
addressLabel: address,
|
|
2901
2963
|
title: individualTitle(ind, address, conditionLabelById, titleLabels)
|
|
2902
2964
|
});
|
|
@@ -2939,23 +3001,23 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
2939
3001
|
edgeId: PED_MATING_ID_BASE + m.id,
|
|
2940
3002
|
kind: "mating-elbow",
|
|
2941
3003
|
points: [
|
|
2942
|
-
{ x:
|
|
2943
|
-
{ x:
|
|
2944
|
-
{ x:
|
|
2945
|
-
{ x:
|
|
3004
|
+
{ x: round6(hubStubX), y: round6(glyphBottom) },
|
|
3005
|
+
{ x: round6(hubStubX), y: round6(dipY) },
|
|
3006
|
+
{ x: round6(spouseStubX), y: round6(dipY) },
|
|
3007
|
+
{ x: round6(spouseStubX), y: round6(glyphBottom) }
|
|
2946
3008
|
],
|
|
2947
3009
|
consanguineous: m.consanguineous,
|
|
2948
3010
|
title
|
|
2949
3011
|
});
|
|
2950
|
-
matingMidpoint.set(m.id, { x:
|
|
3012
|
+
matingMidpoint.set(m.id, { x: round6((hubStubX + spouseStubX) / 2), y: round6(dipY) });
|
|
2951
3013
|
} else if (ay === by) {
|
|
2952
3014
|
const y = ay;
|
|
2953
3015
|
elements.push({
|
|
2954
3016
|
edgeId: PED_MATING_ID_BASE + m.id,
|
|
2955
3017
|
kind: "mating",
|
|
2956
3018
|
points: [
|
|
2957
|
-
{ x:
|
|
2958
|
-
{ x:
|
|
3019
|
+
{ x: round6(lx), y: round6(y) },
|
|
3020
|
+
{ x: round6(rx), y: round6(y) }
|
|
2959
3021
|
],
|
|
2960
3022
|
consanguineous: m.consanguineous,
|
|
2961
3023
|
title
|
|
@@ -2965,7 +3027,7 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
2965
3027
|
const channelRight = cxOf(rightId) - nodeHalfWidth(individualById.get(rightId));
|
|
2966
3028
|
const barCenter = matingBarCenter.get(m.id);
|
|
2967
3029
|
const originX = barCenter !== void 0 && barCenter >= channelLeft && barCenter <= channelRight ? barCenter : midX;
|
|
2968
|
-
matingMidpoint.set(m.id, { x:
|
|
3030
|
+
matingMidpoint.set(m.id, { x: round6(originX), y: round6(y) });
|
|
2969
3031
|
} else {
|
|
2970
3032
|
const upId = ay <= by ? aId : bId;
|
|
2971
3033
|
const downId = ay <= by ? bId : aId;
|
|
@@ -2984,32 +3046,32 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
2984
3046
|
edgeId: PED_MATING_ID_BASE + m.id,
|
|
2985
3047
|
kind: "mating-elbow",
|
|
2986
3048
|
points: [
|
|
2987
|
-
{ x:
|
|
2988
|
-
{ x:
|
|
2989
|
-
{ x:
|
|
2990
|
-
{ x:
|
|
2991
|
-
{ x:
|
|
2992
|
-
{ x:
|
|
3049
|
+
{ x: round6(upX + (downRight ? -GLYPH_HALF : GLYPH_HALF)), y: round6(upY) },
|
|
3050
|
+
{ x: round6(upExitX), y: round6(upY) },
|
|
3051
|
+
{ x: round6(upExitX), y: round6(laneY) },
|
|
3052
|
+
{ x: round6(downFarX), y: round6(laneY) },
|
|
3053
|
+
{ x: round6(downFarX), y: round6(downY) },
|
|
3054
|
+
{ x: round6(downSideX), y: round6(downY) }
|
|
2993
3055
|
],
|
|
2994
3056
|
consanguineous: m.consanguineous,
|
|
2995
3057
|
title
|
|
2996
3058
|
});
|
|
2997
|
-
matingMidpoint.set(m.id, { x:
|
|
3059
|
+
matingMidpoint.set(m.id, { x: round6(downFarX), y: round6(downY) });
|
|
2998
3060
|
} else {
|
|
2999
3061
|
const elbowX = (upX + downX) / 2;
|
|
3000
3062
|
elements.push({
|
|
3001
3063
|
edgeId: PED_MATING_ID_BASE + m.id,
|
|
3002
3064
|
kind: "mating-elbow",
|
|
3003
3065
|
points: [
|
|
3004
|
-
{ x:
|
|
3005
|
-
{ x:
|
|
3006
|
-
{ x:
|
|
3007
|
-
{ x:
|
|
3066
|
+
{ x: round6(upX + (downX >= upX ? GLYPH_HALF : -GLYPH_HALF)), y: round6(upY) },
|
|
3067
|
+
{ x: round6(elbowX), y: round6(upY) },
|
|
3068
|
+
{ x: round6(elbowX), y: round6(downY) },
|
|
3069
|
+
{ x: round6(downX + (upX >= downX ? GLYPH_HALF : -GLYPH_HALF)), y: round6(downY) }
|
|
3008
3070
|
],
|
|
3009
3071
|
consanguineous: m.consanguineous,
|
|
3010
3072
|
title
|
|
3011
3073
|
});
|
|
3012
|
-
matingMidpoint.set(m.id, { x:
|
|
3074
|
+
matingMidpoint.set(m.id, { x: round6(elbowX), y: round6(downY) });
|
|
3013
3075
|
}
|
|
3014
3076
|
}
|
|
3015
3077
|
}
|
|
@@ -3031,13 +3093,13 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3031
3093
|
const barCenterX = (barLeft + barRight) / 2;
|
|
3032
3094
|
const bendY = barY - 2 - lane % 3 * 3;
|
|
3033
3095
|
const descentPoints = Math.abs(mid.x - barCenterX) < 0.01 ? [
|
|
3034
|
-
{ x:
|
|
3035
|
-
{ x:
|
|
3096
|
+
{ x: round6(mid.x), y: round6(mid.y) },
|
|
3097
|
+
{ x: round6(mid.x), y: round6(barY) }
|
|
3036
3098
|
] : [
|
|
3037
|
-
{ x:
|
|
3038
|
-
{ x:
|
|
3039
|
-
{ x:
|
|
3040
|
-
{ x:
|
|
3099
|
+
{ x: round6(mid.x), y: round6(mid.y) },
|
|
3100
|
+
{ x: round6(mid.x), y: round6(bendY) },
|
|
3101
|
+
{ x: round6(barCenterX), y: round6(bendY) },
|
|
3102
|
+
{ x: round6(barCenterX), y: round6(barY) }
|
|
3041
3103
|
];
|
|
3042
3104
|
elements.push({
|
|
3043
3105
|
edgeId: PED_DESCENT_ID_BASE + s.id,
|
|
@@ -3052,8 +3114,8 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3052
3114
|
edgeId: PED_SIBBAR_ID_BASE + s.id,
|
|
3053
3115
|
kind: "sibship-bar",
|
|
3054
3116
|
points: [
|
|
3055
|
-
{ x:
|
|
3056
|
-
{ x:
|
|
3117
|
+
{ x: round6(barLeft), y: round6(barY) },
|
|
3118
|
+
{ x: round6(barRight), y: round6(barY) }
|
|
3057
3119
|
],
|
|
3058
3120
|
consanguineous: false,
|
|
3059
3121
|
title: titleLabels.sibship
|
|
@@ -3074,8 +3136,8 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3074
3136
|
edgeId: PED_RISER_ID_BASE + childId,
|
|
3075
3137
|
kind: "riser",
|
|
3076
3138
|
points: [
|
|
3077
|
-
{ x:
|
|
3078
|
-
{ x:
|
|
3139
|
+
{ x: round6(cx), y: round6(barY) },
|
|
3140
|
+
{ x: round6(cx), y: round6(childTop) }
|
|
3079
3141
|
],
|
|
3080
3142
|
consanguineous: false,
|
|
3081
3143
|
title: titleLabels.sibship
|
|
@@ -3089,15 +3151,15 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3089
3151
|
if (!emittedTwinOrdinals.has(tg.ordinal)) {
|
|
3090
3152
|
emittedTwinOrdinals.add(tg.ordinal);
|
|
3091
3153
|
if (tg.zygosity === "unknown") {
|
|
3092
|
-
unknownTwinJunctions.push({ x:
|
|
3154
|
+
unknownTwinJunctions.push({ x: round6(junctionX), y: round6(junctionY) });
|
|
3093
3155
|
}
|
|
3094
3156
|
elements.push({
|
|
3095
3157
|
edgeId: PED_RISER_ID_BASE + childId,
|
|
3096
3158
|
// anchored on the first member for a stable id
|
|
3097
3159
|
kind: "riser",
|
|
3098
3160
|
points: [
|
|
3099
|
-
{ x:
|
|
3100
|
-
{ x:
|
|
3161
|
+
{ x: round6(junctionX), y: round6(barY) },
|
|
3162
|
+
{ x: round6(junctionX), y: round6(junctionY) }
|
|
3101
3163
|
],
|
|
3102
3164
|
consanguineous: false,
|
|
3103
3165
|
title: titleLabels.twins[tg.zygosity]
|
|
@@ -3108,8 +3170,8 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3108
3170
|
edgeId: PED_TWINBAR_ID_BASE + s.id * 100 + tg.ordinal,
|
|
3109
3171
|
kind: "twin-bar",
|
|
3110
3172
|
points: [
|
|
3111
|
-
{ x:
|
|
3112
|
-
{ x:
|
|
3173
|
+
{ x: round6(Math.min(...memberXs)), y: round6(tieY) },
|
|
3174
|
+
{ x: round6(Math.max(...memberXs)), y: round6(tieY) }
|
|
3113
3175
|
],
|
|
3114
3176
|
consanguineous: false,
|
|
3115
3177
|
title: titleLabels.twins.mz
|
|
@@ -3117,12 +3179,12 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3117
3179
|
}
|
|
3118
3180
|
}
|
|
3119
3181
|
const horizontalThenDown = Math.abs(cx - junctionX) < 0.01 ? [
|
|
3120
|
-
{ x:
|
|
3121
|
-
{ x:
|
|
3182
|
+
{ x: round6(cx), y: round6(junctionY) },
|
|
3183
|
+
{ x: round6(cx), y: round6(childTop) }
|
|
3122
3184
|
] : [
|
|
3123
|
-
{ x:
|
|
3124
|
-
{ x:
|
|
3125
|
-
{ x:
|
|
3185
|
+
{ x: round6(junctionX), y: round6(junctionY) },
|
|
3186
|
+
{ x: round6(cx), y: round6(junctionY) },
|
|
3187
|
+
{ x: round6(cx), y: round6(childTop) }
|
|
3126
3188
|
];
|
|
3127
3189
|
elements.push({
|
|
3128
3190
|
edgeId: PED_RISER_ID_BASE + childId,
|
|
@@ -3150,7 +3212,7 @@ function computePedigreeLayout(input, opts = {}) {
|
|
|
3150
3212
|
}
|
|
3151
3213
|
const generations = [];
|
|
3152
3214
|
for (let row = 0; row < rowCount; row++) {
|
|
3153
|
-
generations.push({ roman: romanNumeral(row + 1), y:
|
|
3215
|
+
generations.push({ roman: romanNumeral(row + 1), y: round6(glyphCyOfRow(row)) });
|
|
3154
3216
|
}
|
|
3155
3217
|
const ZYGOSITY_ORDER = ["mz", "dz", "unknown"];
|
|
3156
3218
|
const twinZygositiesUsed = ZYGOSITY_ORDER.filter((z) => twinZygositiesUsedSet.has(z));
|
|
@@ -3174,15 +3236,15 @@ var EDGE_INK5 = "#71717a";
|
|
|
3174
3236
|
var GLYPH_ATTRS2 = `fill="transparent" stroke="${GLYPH_STROKE3}" stroke-width="2"`;
|
|
3175
3237
|
var CONSANG_GAP = 3;
|
|
3176
3238
|
var ZYGOSITIES = ["mz", "dz", "unknown"];
|
|
3177
|
-
var
|
|
3239
|
+
var round7 = (n) => Math.round(n * 100) / 100;
|
|
3178
3240
|
function glyphOutline(shape, cx, cy, half) {
|
|
3179
3241
|
if (shape === "square") {
|
|
3180
|
-
return `<rect x="${
|
|
3242
|
+
return `<rect x="${round7(cx - half)}" y="${round7(cy - half)}" width="${half * 2}" height="${half * 2}" ${GLYPH_ATTRS2}/>`;
|
|
3181
3243
|
}
|
|
3182
3244
|
if (shape === "circle") {
|
|
3183
3245
|
return `<circle cx="${cx}" cy="${cy}" r="${half}" ${GLYPH_ATTRS2}/>`;
|
|
3184
3246
|
}
|
|
3185
|
-
return `<polygon points="${cx},${
|
|
3247
|
+
return `<polygon points="${cx},${round7(cy - half)} ${round7(cx + half)},${cy} ${cx},${round7(cy + half)} ${round7(cx - half)},${cy}" ${GLYPH_ATTRS2}/>`;
|
|
3186
3248
|
}
|
|
3187
3249
|
function glyphVerticalExtentAt(shape, cy, half, dx) {
|
|
3188
3250
|
const ax = Math.min(Math.abs(dx), half);
|
|
@@ -3208,8 +3270,8 @@ function fillPartitions(n, half, inkByCondition) {
|
|
|
3208
3270
|
for (let s = 0; s <= SAMPLES; s++) {
|
|
3209
3271
|
const x = left + sliceW * s / SAMPLES;
|
|
3210
3272
|
const [yt, yb] = glyphVerticalExtentAt(n.shape, cy, half, x - cx);
|
|
3211
|
-
top.push(`${
|
|
3212
|
-
bottom.push(`${
|
|
3273
|
+
top.push(`${round7(x)},${round7(yt)}`);
|
|
3274
|
+
bottom.push(`${round7(x)},${round7(yb)}`);
|
|
3213
3275
|
}
|
|
3214
3276
|
const pts = [...top, ...bottom.reverse()].join(" ");
|
|
3215
3277
|
const ink = inkByCondition.get(id) ?? GLYPH_STROKE3;
|
|
@@ -3223,24 +3285,24 @@ function carrierDot(n) {
|
|
|
3223
3285
|
function deceasedSlash(n, half) {
|
|
3224
3286
|
if (!n.deceased) return "";
|
|
3225
3287
|
const ext = half + 4;
|
|
3226
|
-
return `<line x1="${
|
|
3288
|
+
return `<line x1="${round7(n.cx - ext)}" y1="${round7(n.cy - ext)}" x2="${round7(n.cx + ext)}" y2="${round7(n.cy + ext)}" stroke="${GLYPH_STROKE3}" stroke-width="2"/>`;
|
|
3227
3289
|
}
|
|
3228
3290
|
function probandArrow(n, half) {
|
|
3229
3291
|
if (n.role === null) return "";
|
|
3230
|
-
const tipX =
|
|
3231
|
-
const tipY =
|
|
3232
|
-
const tailX =
|
|
3233
|
-
const tailY =
|
|
3292
|
+
const tipX = round7(n.cx - half);
|
|
3293
|
+
const tipY = round7(n.cy + half);
|
|
3294
|
+
const tailX = round7(tipX - 16);
|
|
3295
|
+
const tailY = round7(tipY + 16);
|
|
3234
3296
|
const filled = n.role === "proband";
|
|
3235
3297
|
const fill = filled ? GLYPH_STROKE3 : "transparent";
|
|
3236
3298
|
const shaft = `<line x1="${tailX}" y1="${tailY}" x2="${tipX}" y2="${tipY}" stroke="${GLYPH_STROKE3}" stroke-width="2"/>`;
|
|
3237
|
-
const head = `<polygon points="${tipX},${tipY} ${
|
|
3299
|
+
const head = `<polygon points="${tipX},${tipY} ${round7(tipX - 8)},${round7(tipY + 2)} ${round7(tipX - 2)},${round7(tipY + 8)}" fill="${fill}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/>`;
|
|
3238
3300
|
return shaft + head;
|
|
3239
3301
|
}
|
|
3240
3302
|
function stillbirthMark(n, half) {
|
|
3241
3303
|
if (!n.stillbirth) return "";
|
|
3242
|
-
const y =
|
|
3243
|
-
return `<text x="${
|
|
3304
|
+
const y = round7(n.cy + half + PED_ADDRESS_FONT);
|
|
3305
|
+
return `<text x="${round7(n.cx - half - 2)}" y="${y}" text-anchor="end" font-family="${FONT_FAMILY}" font-size="${PED_ADDRESS_FONT}" font-weight="bold" fill="${LABEL_FILL4}">SB</text>`;
|
|
3244
3306
|
}
|
|
3245
3307
|
function nodeSvg2(n, inkByCondition) {
|
|
3246
3308
|
const half = n.size / 2;
|
|
@@ -3254,13 +3316,13 @@ function nodeSvg2(n, inkByCondition) {
|
|
|
3254
3316
|
probandArrow(n, half),
|
|
3255
3317
|
stillbirthMark(n, half)
|
|
3256
3318
|
];
|
|
3257
|
-
const addressY =
|
|
3319
|
+
const addressY = round7(n.labelTop + PED_ADDRESS_FONT);
|
|
3258
3320
|
pieces.push(
|
|
3259
3321
|
`<text x="${n.cx}" y="${addressY}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${PED_ADDRESS_FONT}" fill="${LABEL_FILL4}">${xmlEscape(n.addressLabel)}</text>`
|
|
3260
3322
|
);
|
|
3261
3323
|
if (n.labelLines.length > 0) {
|
|
3262
|
-
const firstBaseline =
|
|
3263
|
-
const tspans = n.labelLines.map((line, i) => `<tspan x="${n.cx}" y="${
|
|
3324
|
+
const firstBaseline = round7(n.labelTop + PED_LABEL_LINE_H + 10);
|
|
3325
|
+
const tspans = n.labelLines.map((line, i) => `<tspan x="${n.cx}" y="${round7(firstBaseline + i * PED_LABEL_LINE_H)}">${xmlEscape(line)}</tspan>`).join("");
|
|
3264
3326
|
pieces.push(
|
|
3265
3327
|
`<text text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${PED_LABEL_FONT}" fill="${LABEL_FILL4}">${tspans}</text>`
|
|
3266
3328
|
);
|
|
@@ -3275,7 +3337,7 @@ function elementSvg3(el) {
|
|
|
3275
3337
|
const title = `<title>${xmlEscape(el.title)}</title>`;
|
|
3276
3338
|
const stroke = `stroke="${EDGE_INK5}" stroke-width="1.5" stroke-opacity="0.75"`;
|
|
3277
3339
|
const draw = (offsetY) => {
|
|
3278
|
-
const shifted = pts.map((p) => ({ x: p.x, y:
|
|
3340
|
+
const shifted = pts.map((p) => ({ x: p.x, y: round7(p.y + offsetY) }));
|
|
3279
3341
|
if (shifted.length === 2) {
|
|
3280
3342
|
return `<line x1="${shifted[0].x}" y1="${shifted[0].y}" x2="${shifted[1].x}" y2="${shifted[1].y}" ${stroke}/>`;
|
|
3281
3343
|
}
|
|
@@ -3286,51 +3348,51 @@ function elementSvg3(el) {
|
|
|
3286
3348
|
}
|
|
3287
3349
|
function unknownTwinMarks(layout) {
|
|
3288
3350
|
return layout.unknownTwinJunctions.map(
|
|
3289
|
-
(p) => `<text x="${
|
|
3351
|
+
(p) => `<text x="${round7(p.x + 6)}" y="${round7(p.y + 4)}" font-family="${FONT_FAMILY}" font-size="12" font-weight="bold" fill="${LABEL_FILL4}">?</text>`
|
|
3290
3352
|
).join("");
|
|
3291
3353
|
}
|
|
3292
3354
|
var MINI_ATTRS2 = `fill="transparent" stroke="${GLYPH_STROKE3}" stroke-width="1.5"`;
|
|
3293
3355
|
function miniShapeSwatch(shape, x, y) {
|
|
3294
|
-
const cx =
|
|
3295
|
-
if (shape === "square") return `<rect x="${
|
|
3356
|
+
const cx = round7(x + LEGEND_SWATCH_W / 2);
|
|
3357
|
+
if (shape === "square") return `<rect x="${round7(cx - 6)}" y="${round7(y - 6)}" width="12" height="12" ${MINI_ATTRS2}/>`;
|
|
3296
3358
|
if (shape === "circle") return `<circle cx="${cx}" cy="${y}" r="6" ${MINI_ATTRS2}/>`;
|
|
3297
|
-
return `<polygon points="${cx},${
|
|
3359
|
+
return `<polygon points="${cx},${round7(y - 7)} ${round7(cx + 7)},${y} ${cx},${round7(y + 7)} ${round7(cx - 7)},${y}" ${MINI_ATTRS2}/>`;
|
|
3298
3360
|
}
|
|
3299
3361
|
function miniSwatchCircle(filled, ink, x, y) {
|
|
3300
|
-
const cx =
|
|
3362
|
+
const cx = round7(x + LEGEND_SWATCH_W / 2);
|
|
3301
3363
|
return `<circle cx="${cx}" cy="${y}" r="6" fill="${filled ? ink : "transparent"}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/>`;
|
|
3302
3364
|
}
|
|
3303
3365
|
function miniCarrierSwatch(x, y) {
|
|
3304
|
-
const cx =
|
|
3366
|
+
const cx = round7(x + LEGEND_SWATCH_W / 2);
|
|
3305
3367
|
return `<circle cx="${cx}" cy="${y}" r="6" ${MINI_ATTRS2}/><circle cx="${cx}" cy="${y}" r="2" fill="${GLYPH_STROKE3}" stroke="none"/>`;
|
|
3306
3368
|
}
|
|
3307
3369
|
function miniDeceasedSwatch(x, y) {
|
|
3308
|
-
const cx =
|
|
3309
|
-
return `<circle cx="${cx}" cy="${y}" r="6" ${MINI_ATTRS2}/><line x1="${
|
|
3370
|
+
const cx = round7(x + LEGEND_SWATCH_W / 2);
|
|
3371
|
+
return `<circle cx="${cx}" cy="${y}" r="6" ${MINI_ATTRS2}/><line x1="${round7(cx - 7)}" y1="${round7(y - 7)}" x2="${round7(cx + 7)}" y2="${round7(y + 7)}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/>`;
|
|
3310
3372
|
}
|
|
3311
3373
|
function miniArrowSwatch(filled, x, y) {
|
|
3312
|
-
const cx =
|
|
3313
|
-
const tipX =
|
|
3314
|
-
const tipY =
|
|
3315
|
-
return `<line x1="${
|
|
3374
|
+
const cx = round7(x + LEGEND_SWATCH_W / 2);
|
|
3375
|
+
const tipX = round7(cx - 2);
|
|
3376
|
+
const tipY = round7(y + 2);
|
|
3377
|
+
return `<line x1="${round7(tipX - 8)}" y1="${round7(tipY + 8)}" x2="${tipX}" y2="${tipY}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/><polygon points="${tipX},${tipY} ${round7(tipX - 5)},${round7(tipY + 1)} ${round7(tipX - 1)},${round7(tipY + 5)}" fill="${filled ? GLYPH_STROKE3 : "transparent"}" stroke="${GLYPH_STROKE3}" stroke-width="1"/>`;
|
|
3316
3378
|
}
|
|
3317
3379
|
function miniConsanguineousSwatch(x, y) {
|
|
3318
|
-
const x1 =
|
|
3319
|
-
const x2 =
|
|
3320
|
-
return `<line x1="${x1}" y1="${
|
|
3380
|
+
const x1 = round7(x + 2);
|
|
3381
|
+
const x2 = round7(x + LEGEND_SWATCH_W - 2);
|
|
3382
|
+
return `<line x1="${x1}" y1="${round7(y - 1.5)}" x2="${x2}" y2="${round7(y - 1.5)}" stroke="${EDGE_INK5}" stroke-width="1.5"/><line x1="${x1}" y1="${round7(y + 1.5)}" x2="${x2}" y2="${round7(y + 1.5)}" stroke="${EDGE_INK5}" stroke-width="1.5"/>`;
|
|
3321
3383
|
}
|
|
3322
3384
|
function miniTwinSwatch(zygosity, x, y) {
|
|
3323
|
-
const cx =
|
|
3324
|
-
const apexY =
|
|
3325
|
-
const baseY =
|
|
3326
|
-
const left =
|
|
3327
|
-
const right =
|
|
3385
|
+
const cx = round7(x + LEGEND_SWATCH_W / 2);
|
|
3386
|
+
const apexY = round7(y - 6);
|
|
3387
|
+
const baseY = round7(y + 6);
|
|
3388
|
+
const left = round7(cx - 6);
|
|
3389
|
+
const right = round7(cx + 6);
|
|
3328
3390
|
const stub = `<line x1="${cx}" y1="${apexY}" x2="${cx}" y2="${y}" stroke="${EDGE_INK5}" stroke-width="1.5"/><line x1="${left}" y1="${y}" x2="${right}" y2="${y}" stroke="${EDGE_INK5}" stroke-width="1.5"/><line x1="${left}" y1="${y}" x2="${left}" y2="${baseY}" stroke="${EDGE_INK5}" stroke-width="1.5"/><line x1="${right}" y1="${y}" x2="${right}" y2="${baseY}" stroke="${EDGE_INK5}" stroke-width="1.5"/>`;
|
|
3329
3391
|
if (zygosity === "mz") {
|
|
3330
|
-
return stub + `<line x1="${left}" y1="${
|
|
3392
|
+
return stub + `<line x1="${left}" y1="${round7(y + 3)}" x2="${right}" y2="${round7(y + 3)}" stroke="${EDGE_INK5}" stroke-width="1.5"/>`;
|
|
3331
3393
|
}
|
|
3332
3394
|
if (zygosity === "unknown") {
|
|
3333
|
-
return stub + `<text x="${
|
|
3395
|
+
return stub + `<text x="${round7(cx + 8)}" y="${round7(y + 3)}" font-family="${FONT_FAMILY}" font-size="9" fill="${LABEL_FILL4}">?</text>`;
|
|
3334
3396
|
}
|
|
3335
3397
|
return stub;
|
|
3336
3398
|
}
|
|
@@ -3343,7 +3405,7 @@ function pedigreeLayoutSvg(layout, opts = {}) {
|
|
|
3343
3405
|
for (const n of layout.nodes) parts.push(nodeSvg2(n, inkByCondition));
|
|
3344
3406
|
for (const g of layout.generations) {
|
|
3345
3407
|
parts.push(
|
|
3346
|
-
`<text x="${
|
|
3408
|
+
`<text x="${round7(16)}" y="${round7(g.y + PED_LABEL_FONT * 0.32)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${PED_LABEL_FONT}" font-weight="bold" fill="${LABEL_FILL4}">${xmlEscape(g.roman)}</text>`
|
|
3347
3409
|
);
|
|
3348
3410
|
}
|
|
3349
3411
|
let width = layout.width;
|
|
@@ -3593,7 +3655,7 @@ var PHYLO_BRANCH_ID_BASE = 2e6;
|
|
|
3593
3655
|
var PHYLO_EXTENSION_ID_BASE = 3e6;
|
|
3594
3656
|
var PHYLO_ROOTSTUB_ID_BASE = 4e6;
|
|
3595
3657
|
var PHYLO_SCALEBAR_ID = 5e6;
|
|
3596
|
-
var
|
|
3658
|
+
var round8 = (n) => Math.round(n * 100) / 100;
|
|
3597
3659
|
function niceScaleStep(maxLen) {
|
|
3598
3660
|
if (!(maxLen > 0)) return 0;
|
|
3599
3661
|
const target = maxLen / 4;
|
|
@@ -3719,8 +3781,8 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3719
3781
|
edgeId: PHYLO_ROOTSTUB_ID_BASE + root.node.id,
|
|
3720
3782
|
kind: "root-stub",
|
|
3721
3783
|
points: [
|
|
3722
|
-
{ x:
|
|
3723
|
-
{ x:
|
|
3784
|
+
{ x: round8(Math.max(PADDING6 / 2, rootDrawX - ROOT_STUB)), y: round8(root.cy) },
|
|
3785
|
+
{ x: round8(rootDrawX), y: round8(root.cy) }
|
|
3724
3786
|
],
|
|
3725
3787
|
dotted: false,
|
|
3726
3788
|
length: null,
|
|
@@ -3735,11 +3797,11 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3735
3797
|
nodes.push({
|
|
3736
3798
|
nodeId: w.node.id,
|
|
3737
3799
|
isTip: tip,
|
|
3738
|
-
cx:
|
|
3739
|
-
cy:
|
|
3800
|
+
cx: round8(drawX),
|
|
3801
|
+
cy: round8(w.cy),
|
|
3740
3802
|
support,
|
|
3741
3803
|
labelLines: labelLine,
|
|
3742
|
-
labelLeft:
|
|
3804
|
+
labelLeft: round8(drawX + PHYLO_TIP_R + PHYLO_LABEL_GAP),
|
|
3743
3805
|
depth: w.depth,
|
|
3744
3806
|
title: nodeTitle(w.node, tip, isRoot, w.inLength, titleLabels)
|
|
3745
3807
|
});
|
|
@@ -3748,8 +3810,8 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3748
3810
|
edgeId: PHYLO_EXTENSION_ID_BASE + w.node.id,
|
|
3749
3811
|
kind: "extension",
|
|
3750
3812
|
points: [
|
|
3751
|
-
{ x:
|
|
3752
|
-
{ x:
|
|
3813
|
+
{ x: round8(w.cx), y: round8(w.cy) },
|
|
3814
|
+
{ x: round8(alignX), y: round8(w.cy) }
|
|
3753
3815
|
],
|
|
3754
3816
|
dotted: true,
|
|
3755
3817
|
length: null,
|
|
@@ -3765,8 +3827,8 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3765
3827
|
edgeId: PHYLO_CLADEBAR_ID_BASE + w.node.id,
|
|
3766
3828
|
kind: "clade-bar",
|
|
3767
3829
|
points: [
|
|
3768
|
-
{ x:
|
|
3769
|
-
{ x:
|
|
3830
|
+
{ x: round8(w.cx), y: round8(barTop) },
|
|
3831
|
+
{ x: round8(w.cx), y: round8(barBottom) }
|
|
3770
3832
|
],
|
|
3771
3833
|
dotted: false,
|
|
3772
3834
|
length: null,
|
|
@@ -3778,8 +3840,8 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3778
3840
|
edgeId: PHYLO_BRANCH_ID_BASE + c.node.id,
|
|
3779
3841
|
kind: "branch",
|
|
3780
3842
|
points: [
|
|
3781
|
-
{ x:
|
|
3782
|
-
{ x:
|
|
3843
|
+
{ x: round8(w.cx), y: round8(c.cy) },
|
|
3844
|
+
{ x: round8(c.cx), y: round8(c.cy) }
|
|
3783
3845
|
],
|
|
3784
3846
|
dotted: false,
|
|
3785
3847
|
length: c.inLength,
|
|
@@ -3796,17 +3858,17 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3796
3858
|
const barY = treeBottom + SCALEBAR_ZONE / 2;
|
|
3797
3859
|
scaleBar = {
|
|
3798
3860
|
length: stepLen,
|
|
3799
|
-
x:
|
|
3800
|
-
y:
|
|
3801
|
-
pxLength:
|
|
3861
|
+
x: round8(barX),
|
|
3862
|
+
y: round8(barY),
|
|
3863
|
+
pxLength: round8(scaleBarPx),
|
|
3802
3864
|
valueLabel: trimNumber(stepLen)
|
|
3803
3865
|
};
|
|
3804
3866
|
elements.push({
|
|
3805
3867
|
edgeId: PHYLO_SCALEBAR_ID,
|
|
3806
3868
|
kind: "scale-bar",
|
|
3807
3869
|
points: [
|
|
3808
|
-
{ x:
|
|
3809
|
-
{ x:
|
|
3870
|
+
{ x: round8(barX), y: round8(barY) },
|
|
3871
|
+
{ x: round8(barX + scaleBarPx), y: round8(barY) }
|
|
3810
3872
|
],
|
|
3811
3873
|
dotted: false,
|
|
3812
3874
|
length: stepLen,
|
|
@@ -3820,18 +3882,18 @@ function computePhyloLayout(input, opts = {}) {
|
|
|
3820
3882
|
var GLYPH_STROKE4 = "#52525b";
|
|
3821
3883
|
var LABEL_FILL5 = "#3f3f46";
|
|
3822
3884
|
var EDGE_INK6 = "#71717a";
|
|
3823
|
-
var
|
|
3885
|
+
var round9 = (n) => Math.round(n * 100) / 100;
|
|
3824
3886
|
function nodeSvg3(n, showSupport) {
|
|
3825
3887
|
const pieces = [`<title>${xmlEscape(n.title)}</title>`];
|
|
3826
3888
|
pieces.push(`<circle cx="${n.cx}" cy="${n.cy}" r="2.5" fill="${GLYPH_STROKE4}"/>`);
|
|
3827
3889
|
if (n.labelLines.length > 0) {
|
|
3828
3890
|
pieces.push(
|
|
3829
|
-
`<text x="${n.labelLeft}" y="${
|
|
3891
|
+
`<text x="${n.labelLeft}" y="${round9(n.cy + PHYLO_LABEL_FONT * 0.32)}" font-family="${FONT_FAMILY}" font-size="${PHYLO_LABEL_FONT}" fill="${LABEL_FILL5}">${xmlEscape(n.labelLines[0])}</text>`
|
|
3830
3892
|
);
|
|
3831
3893
|
}
|
|
3832
3894
|
if (showSupport && !n.isTip && n.support !== null) {
|
|
3833
3895
|
pieces.push(
|
|
3834
|
-
`<text x="${
|
|
3896
|
+
`<text x="${round9(n.cx - 4)}" y="${round9(n.cy - 3)}" text-anchor="end" font-family="${FONT_FAMILY}" font-size="${PHYLO_SUPPORT_FONT}" fill="${LABEL_FILL5}">${xmlEscape(String(n.support))}</text>`
|
|
3835
3897
|
);
|
|
3836
3898
|
}
|
|
3837
3899
|
return `<g data-node-id="n${n.nodeId}">${pieces.join("")}</g>`;
|
|
@@ -3846,18 +3908,18 @@ function elementSvg4(el) {
|
|
|
3846
3908
|
function scaleBarLabelSvg(layout) {
|
|
3847
3909
|
const bar = layout.scaleBar;
|
|
3848
3910
|
if (bar === null) return "";
|
|
3849
|
-
const tick = (x) => `<line x1="${x}" y1="${
|
|
3850
|
-
return tick(bar.x) + tick(
|
|
3911
|
+
const tick = (x) => `<line x1="${x}" y1="${round9(bar.y - 3)}" x2="${x}" y2="${round9(bar.y + 3)}" stroke="${EDGE_INK6}" stroke-width="1.5" stroke-opacity="0.75"/>`;
|
|
3912
|
+
return tick(bar.x) + tick(round9(bar.x + bar.pxLength)) + `<text x="${round9(bar.x + bar.pxLength / 2)}" y="${round9(bar.y - 6)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${PHYLO_SUPPORT_FONT}" fill="${LABEL_FILL5}">${xmlEscape(bar.valueLabel)}</text>`;
|
|
3851
3913
|
}
|
|
3852
3914
|
function supportSwatch(x, y) {
|
|
3853
|
-
const cx =
|
|
3854
|
-
return `<circle cx="${cx}" cy="${y}" r="2.5" fill="${GLYPH_STROKE4}"/><text x="${
|
|
3915
|
+
const cx = round9(x + LEGEND_SWATCH_W / 2);
|
|
3916
|
+
return `<circle cx="${cx}" cy="${y}" r="2.5" fill="${GLYPH_STROKE4}"/><text x="${round9(cx + 5)}" y="${round9(y + PHYLO_SUPPORT_FONT * 0.32)}" font-family="${FONT_FAMILY}" font-size="${PHYLO_SUPPORT_FONT}" fill="${LABEL_FILL5}">95</text>`;
|
|
3855
3917
|
}
|
|
3856
3918
|
function scaleSwatch(x, y) {
|
|
3857
|
-
return `<line x1="${
|
|
3919
|
+
return `<line x1="${round9(x)}" y1="${y}" x2="${round9(x + LEGEND_SWATCH_W)}" y2="${y}" stroke="${EDGE_INK6}" stroke-width="1.5" stroke-opacity="0.75"/>`;
|
|
3858
3920
|
}
|
|
3859
3921
|
function alignedTipSwatch(x, y) {
|
|
3860
|
-
return `<line x1="${
|
|
3922
|
+
return `<line x1="${round9(x)}" y1="${y}" x2="${round9(x + LEGEND_SWATCH_W)}" y2="${y}" stroke="${EDGE_INK6}" stroke-width="1.5" stroke-opacity="0.5" stroke-dasharray="2,3"/>`;
|
|
3861
3923
|
}
|
|
3862
3924
|
function phyloLayoutSvg(layout, opts = {}) {
|
|
3863
3925
|
const labels = opts.labels ?? PHYLO_SVG_LABELS_EN;
|
|
@@ -4105,9 +4167,9 @@ var ORG_BUS_ID_BASE = 2e6;
|
|
|
4105
4167
|
var ORG_DROP_ID_BASE = 3e6;
|
|
4106
4168
|
var ORG_ASSIST_ID_BASE = 4e6;
|
|
4107
4169
|
var ORG_DOTTED_ID_BASE = 5e6;
|
|
4108
|
-
var
|
|
4109
|
-
var drawnLeftEdge = (cx, boxW) =>
|
|
4110
|
-
var drawnRightEdge = (cx, boxW) => drawnLeftEdge(cx, boxW) +
|
|
4170
|
+
var round10 = (n) => Math.round(n * 100) / 100;
|
|
4171
|
+
var drawnLeftEdge = (cx, boxW) => round10(round10(cx) - round10(boxW) / 2);
|
|
4172
|
+
var drawnRightEdge = (cx, boxW) => drawnLeftEdge(cx, boxW) + round10(boxW);
|
|
4111
4173
|
function packSubtree(node, gaps) {
|
|
4112
4174
|
const { ownHalfL, ownHalfR, children } = node;
|
|
4113
4175
|
if (children.length === 0) {
|
|
@@ -4275,10 +4337,10 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4275
4337
|
const pushNode = (inst) => {
|
|
4276
4338
|
nodes.push({
|
|
4277
4339
|
positionId: inst.position.id,
|
|
4278
|
-
cx:
|
|
4279
|
-
top:
|
|
4280
|
-
boxW:
|
|
4281
|
-
boxH:
|
|
4340
|
+
cx: round10(inst.cx),
|
|
4341
|
+
top: round10(rowTop[inst.depth]),
|
|
4342
|
+
boxW: round10(inst.m.boxW),
|
|
4343
|
+
boxH: round10(inst.m.boxH),
|
|
4282
4344
|
style: inst.style,
|
|
4283
4345
|
nameLines: inst.m.nameLines,
|
|
4284
4346
|
titleLines: inst.m.titleLines,
|
|
@@ -4286,7 +4348,8 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4286
4348
|
vacantMarker: inst.m.vacantMarker,
|
|
4287
4349
|
isAssistant: inst.assistReport !== null,
|
|
4288
4350
|
depth: inst.depth,
|
|
4289
|
-
title: inst.title
|
|
4351
|
+
title: inst.title,
|
|
4352
|
+
annotated: inst.position.annotated ?? false
|
|
4290
4353
|
});
|
|
4291
4354
|
};
|
|
4292
4355
|
const emit = (inst) => {
|
|
@@ -4300,10 +4363,10 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4300
4363
|
const assistMidY = assistTop + a.m.boxH / 2;
|
|
4301
4364
|
nodes.push({
|
|
4302
4365
|
positionId: a.position.id,
|
|
4303
|
-
cx:
|
|
4304
|
-
top:
|
|
4305
|
-
boxW:
|
|
4306
|
-
boxH:
|
|
4366
|
+
cx: round10(a.cx),
|
|
4367
|
+
top: round10(assistTop),
|
|
4368
|
+
boxW: round10(a.m.boxW),
|
|
4369
|
+
boxH: round10(a.m.boxH),
|
|
4307
4370
|
style: a.style,
|
|
4308
4371
|
nameLines: a.m.nameLines,
|
|
4309
4372
|
titleLines: a.m.titleLines,
|
|
@@ -4311,17 +4374,19 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4311
4374
|
vacantMarker: a.m.vacantMarker,
|
|
4312
4375
|
isAssistant: true,
|
|
4313
4376
|
depth: a.depth,
|
|
4314
|
-
title: a.title
|
|
4377
|
+
title: a.title,
|
|
4378
|
+
annotated: a.position.annotated ?? false
|
|
4315
4379
|
});
|
|
4316
4380
|
elements.push({
|
|
4317
4381
|
edgeId: ORG_ASSIST_ID_BASE + report.reportId,
|
|
4318
4382
|
kind: "assist",
|
|
4319
4383
|
points: [
|
|
4320
|
-
{ x:
|
|
4321
|
-
{ x: drawnRightEdge(a.cx, a.m.boxW), y:
|
|
4384
|
+
{ x: round10(inst.cx), y: round10(assistMidY) },
|
|
4385
|
+
{ x: drawnRightEdge(a.cx, a.m.boxW), y: round10(assistMidY) }
|
|
4322
4386
|
],
|
|
4323
4387
|
dashed: false,
|
|
4324
|
-
title: reportTitle("assistant", report.label)
|
|
4388
|
+
title: reportTitle("assistant", report.label),
|
|
4389
|
+
annotated: report.annotated ?? false
|
|
4325
4390
|
});
|
|
4326
4391
|
bandY += a.m.boxH + ORG_ASSIST_BAND_DROP;
|
|
4327
4392
|
}
|
|
@@ -4333,11 +4398,12 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4333
4398
|
edgeId: ORG_STEM_ID_BASE + inst.position.id,
|
|
4334
4399
|
kind: "stem",
|
|
4335
4400
|
points: [
|
|
4336
|
-
{ x:
|
|
4337
|
-
{ x:
|
|
4401
|
+
{ x: round10(inst.cx), y: round10(boxBottom) },
|
|
4402
|
+
{ x: round10(inst.cx), y: round10(by) }
|
|
4338
4403
|
],
|
|
4339
4404
|
dashed: false,
|
|
4340
|
-
title: reportTitle("line", null)
|
|
4405
|
+
title: reportTitle("line", null),
|
|
4406
|
+
annotated: false
|
|
4341
4407
|
});
|
|
4342
4408
|
const childCxs = inst.lineChildren.map((c) => c.cx);
|
|
4343
4409
|
if (inst.lineChildren.length > 1) {
|
|
@@ -4345,23 +4411,28 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4345
4411
|
edgeId: ORG_BUS_ID_BASE + inst.position.id,
|
|
4346
4412
|
kind: "bus",
|
|
4347
4413
|
points: [
|
|
4348
|
-
{ x:
|
|
4349
|
-
{ x:
|
|
4414
|
+
{ x: round10(Math.min(...childCxs)), y: round10(by) },
|
|
4415
|
+
{ x: round10(Math.max(...childCxs)), y: round10(by) }
|
|
4350
4416
|
],
|
|
4351
4417
|
dashed: false,
|
|
4352
|
-
title: reportTitle("line", null)
|
|
4418
|
+
title: reportTitle("line", null),
|
|
4419
|
+
annotated: false
|
|
4353
4420
|
});
|
|
4354
4421
|
}
|
|
4422
|
+
const lineReports = lineChildIds.get(inst.position.id) ?? [];
|
|
4423
|
+
const lineReportByChildId = new Map(lineReports.map((r) => [r.reportId, r]));
|
|
4355
4424
|
for (const c of inst.lineChildren) {
|
|
4425
|
+
const lineReport = lineReportByChildId.get(c.position.id);
|
|
4356
4426
|
elements.push({
|
|
4357
4427
|
edgeId: ORG_DROP_ID_BASE + c.position.id,
|
|
4358
4428
|
kind: "drop",
|
|
4359
4429
|
points: [
|
|
4360
|
-
{ x:
|
|
4361
|
-
{ x:
|
|
4430
|
+
{ x: round10(c.cx), y: round10(by) },
|
|
4431
|
+
{ x: round10(c.cx), y: round10(rowTop[c.depth]) }
|
|
4362
4432
|
],
|
|
4363
4433
|
dashed: false,
|
|
4364
|
-
title: reportTitle("line", null)
|
|
4434
|
+
title: reportTitle("line", null),
|
|
4435
|
+
annotated: lineReport?.annotated ?? false
|
|
4365
4436
|
});
|
|
4366
4437
|
}
|
|
4367
4438
|
for (const c of inst.lineChildren) emit(c);
|
|
@@ -4431,13 +4502,13 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4431
4502
|
return {
|
|
4432
4503
|
r,
|
|
4433
4504
|
i,
|
|
4434
|
-
repMidY:
|
|
4435
|
-
mgrMidY:
|
|
4436
|
-
repBoxEdge:
|
|
4437
|
-
mgrBoxEdge:
|
|
4438
|
-
repChY:
|
|
4439
|
-
mgrChY:
|
|
4440
|
-
laneX:
|
|
4505
|
+
repMidY: round10(nextExitY(r.reportId, rep.midY, rep.boxH)),
|
|
4506
|
+
mgrMidY: round10(nextExitY(r.managerId, mgr.midY, mgr.boxH)),
|
|
4507
|
+
repBoxEdge: round10(rep.boxEdge),
|
|
4508
|
+
mgrBoxEdge: round10(mgr.boxEdge),
|
|
4509
|
+
repChY: round10(channelTop + 2 * i * ORG_MATRIX_LANE_PITCH),
|
|
4510
|
+
mgrChY: round10(channelTop + (2 * i + 1) * ORG_MATRIX_LANE_PITCH),
|
|
4511
|
+
laneX: round10(canvasRight + ORG_MATRIX_GUTTER_GAP + laneOf.get(r.id) * ORG_MATRIX_LANE_PITCH),
|
|
4441
4512
|
repEscX: 0,
|
|
4442
4513
|
mgrEscX: 0
|
|
4443
4514
|
};
|
|
@@ -4475,14 +4546,14 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4475
4546
|
else if (v.side === -1 && bRight < v.escCol - VERT_EPS) band = Math.min(band, v.escCol - bRight);
|
|
4476
4547
|
}
|
|
4477
4548
|
let lane = 0;
|
|
4478
|
-
let candidate =
|
|
4549
|
+
let candidate = round10(v.escCol);
|
|
4479
4550
|
while (placed.some((p) => Math.abs(p.x - candidate) < VERT_EPS && yOverlap(p.lo, p.hi, v.lo, v.hi))) {
|
|
4480
4551
|
lane += 1;
|
|
4481
|
-
candidate =
|
|
4552
|
+
candidate = round10(v.escCol + v.side * lane * ORG_MATRIX_ESCAPE_STEP);
|
|
4482
4553
|
}
|
|
4483
4554
|
if (lane > 0 && lane * ORG_MATRIX_ESCAPE_STEP >= band - VERT_EPS) {
|
|
4484
4555
|
throw new Error(
|
|
4485
|
-
`org-chart: escape-column-overflow \u2014 escape vertical nudged ${lane * ORG_MATRIX_ESCAPE_STEP}px from its base column exceeds the ${
|
|
4556
|
+
`org-chart: escape-column-overflow \u2014 escape vertical nudged ${lane * ORG_MATRIX_ESCAPE_STEP}px from its base column exceeds the ${round10(band)}px box-free band`
|
|
4486
4557
|
);
|
|
4487
4558
|
}
|
|
4488
4559
|
placed.push({ x: candidate, lo: v.lo, hi: v.hi });
|
|
@@ -4511,7 +4582,8 @@ function computeOrgChartLayout(input, opts = {}) {
|
|
|
4511
4582
|
// → into the manager box's edge
|
|
4512
4583
|
]),
|
|
4513
4584
|
dashed: true,
|
|
4514
|
-
title: reportTitle("dotted", g.r.label)
|
|
4585
|
+
title: reportTitle("dotted", g.r.label),
|
|
4586
|
+
annotated: g.r.annotated ?? false
|
|
4515
4587
|
});
|
|
4516
4588
|
}
|
|
4517
4589
|
}
|
|
@@ -4554,25 +4626,25 @@ var EDGE_INK7 = "#71717a";
|
|
|
4554
4626
|
var GLYPH_ATTRS3 = `fill="transparent" stroke="${GLYPH_STROKE5}" stroke-width="2"`;
|
|
4555
4627
|
var VACANT_DASH = `stroke-dasharray="6,4"`;
|
|
4556
4628
|
var DOTTED = EDGE_STROKE.distant;
|
|
4557
|
-
var
|
|
4629
|
+
var round11 = (n) => Math.round(n * 100) / 100;
|
|
4558
4630
|
var ORG_BOX_PAD_Y2 = 9;
|
|
4559
4631
|
function boxSvg(n) {
|
|
4560
|
-
const left =
|
|
4632
|
+
const left = round11(n.cx - n.boxW / 2);
|
|
4561
4633
|
const pieces = [`<title>${xmlEscape(n.title)}</title>`];
|
|
4562
4634
|
if (n.style === "dashed") {
|
|
4563
4635
|
pieces.push(
|
|
4564
4636
|
`<rect x="${left}" y="${n.top}" width="${n.boxW}" height="${n.boxH}" rx="2" ${GLYPH_ATTRS3} ${VACANT_DASH}/>`
|
|
4565
4637
|
);
|
|
4566
|
-
const ix =
|
|
4567
|
-
const iy =
|
|
4568
|
-
const iw =
|
|
4569
|
-
const ih =
|
|
4638
|
+
const ix = round11(left + 3);
|
|
4639
|
+
const iy = round11(n.top + 3);
|
|
4640
|
+
const iw = round11(n.boxW - 6);
|
|
4641
|
+
const ih = round11(n.boxH - 6);
|
|
4570
4642
|
pieces.push(`<rect x="${ix}" y="${iy}" width="${iw}" height="${ih}" rx="2" ${GLYPH_ATTRS3} ${VACANT_DASH}/>`);
|
|
4571
4643
|
} else {
|
|
4572
4644
|
pieces.push(`<rect x="${left}" y="${n.top}" width="${n.boxW}" height="${n.boxH}" rx="2" ${GLYPH_ATTRS3}/>`);
|
|
4573
4645
|
}
|
|
4574
4646
|
let lineIndex = 0;
|
|
4575
|
-
const firstBaseline = (i) =>
|
|
4647
|
+
const firstBaseline = (i) => round11(n.top + ORG_BOX_PAD_Y2 + ORG_LABEL_LINE_H / 2 + i * ORG_LABEL_LINE_H + ORG_LABEL_FONT * 0.32);
|
|
4576
4648
|
const textBlock = (lines, font) => {
|
|
4577
4649
|
if (lines.length === 0) return "";
|
|
4578
4650
|
const tspans = lines.map((line, i) => `<tspan x="${n.cx}" y="${firstBaseline(lineIndex + i)}">${xmlEscape(line)}</tspan>`).join("");
|
|
@@ -4583,43 +4655,45 @@ function boxSvg(n) {
|
|
|
4583
4655
|
pieces.push(textBlock(n.titleLines, ORG_TITLE_FONT));
|
|
4584
4656
|
pieces.push(textBlock(n.subtitleLines, ORG_TITLE_FONT));
|
|
4585
4657
|
if (n.vacantMarker !== null) pieces.push(textBlock([n.vacantMarker], ORG_TITLE_FONT));
|
|
4658
|
+
if (n.annotated) pieces.push(annotationDot(round11(n.cx + n.boxW / 2) - 4, n.top + 4));
|
|
4586
4659
|
return `<g data-node-id="p${n.positionId}">${pieces.filter((p) => p !== "").join("")}</g>`;
|
|
4587
4660
|
}
|
|
4588
4661
|
function elementSvg5(el) {
|
|
4589
4662
|
const head = `<g data-edge-id="${el.edgeId}"><title>${xmlEscape(el.title)}</title>`;
|
|
4663
|
+
const tick = el.annotated ? annotationTick(el.points) : "";
|
|
4590
4664
|
if (el.dashed) {
|
|
4591
4665
|
const dash = DOTTED.dash === null ? "" : ` stroke-dasharray="${DOTTED.dash[0]},${DOTTED.dash[1]}"`;
|
|
4592
|
-
return head + `<path d="${pathData(el.points)}" fill="none" stroke="${EDGE_INK7}" stroke-width="${DOTTED.width}" stroke-opacity="${DOTTED.opacity}"${dash}
|
|
4666
|
+
return head + `<path d="${pathData(el.points)}" fill="none" stroke="${EDGE_INK7}" stroke-width="${DOTTED.width}" stroke-opacity="${DOTTED.opacity}"${dash}/>${tick}</g>`;
|
|
4593
4667
|
}
|
|
4594
4668
|
if (el.points.length === 2) {
|
|
4595
4669
|
const a = el.points[0];
|
|
4596
4670
|
const b = el.points[1];
|
|
4597
|
-
return head + `<line x1="${a.x}" y1="${a.y}" x2="${b.x}" y2="${b.y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"
|
|
4671
|
+
return head + `<line x1="${a.x}" y1="${a.y}" x2="${b.x}" y2="${b.y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/>${tick}</g>`;
|
|
4598
4672
|
}
|
|
4599
|
-
return head + `<path d="${pathData(el.points)}" fill="none" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"
|
|
4673
|
+
return head + `<path d="${pathData(el.points)}" fill="none" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/>${tick}</g>`;
|
|
4600
4674
|
}
|
|
4601
4675
|
var MINI_ATTRS3 = `fill="transparent" stroke="${GLYPH_STROKE5}" stroke-width="1.5"`;
|
|
4602
4676
|
function lineSwatch(x, y) {
|
|
4603
|
-
const x1 =
|
|
4604
|
-
const x2 =
|
|
4677
|
+
const x1 = round11(x + 2);
|
|
4678
|
+
const x2 = round11(x + LEGEND_SWATCH_W - 2);
|
|
4605
4679
|
return `<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/>`;
|
|
4606
4680
|
}
|
|
4607
4681
|
function assistantSwatch(x, y) {
|
|
4608
|
-
const stemX =
|
|
4609
|
-
const boxX =
|
|
4610
|
-
return `<line x1="${stemX}" y1="${
|
|
4682
|
+
const stemX = round11(x + LEGEND_SWATCH_W - 4);
|
|
4683
|
+
const boxX = round11(x + 1);
|
|
4684
|
+
return `<line x1="${stemX}" y1="${round11(y - 4)}" x2="${stemX}" y2="${y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/><line x1="${stemX}" y1="${y}" x2="${round11(boxX + 8)}" y2="${y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/><rect x="${boxX}" y="${round11(y - 3.5)}" width="8" height="7" rx="1" ${MINI_ATTRS3}/>`;
|
|
4611
4685
|
}
|
|
4612
4686
|
function dottedSwatch(x, y) {
|
|
4613
|
-
const x1 =
|
|
4614
|
-
const x2 =
|
|
4687
|
+
const x1 = round11(x + 2);
|
|
4688
|
+
const x2 = round11(x + LEGEND_SWATCH_W - 2);
|
|
4615
4689
|
const dash = DOTTED.dash === null ? "" : ` stroke-dasharray="${DOTTED.dash[0]},${DOTTED.dash[1]}"`;
|
|
4616
4690
|
return `<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="${EDGE_INK7}" stroke-width="${DOTTED.width}" stroke-opacity="${DOTTED.opacity}"${dash}/>`;
|
|
4617
4691
|
}
|
|
4618
4692
|
function vacantSwatch(x, y) {
|
|
4619
|
-
const cx =
|
|
4620
|
-
const bx =
|
|
4621
|
-
const by =
|
|
4622
|
-
return `<rect x="${bx}" y="${by}" width="16" height="10" rx="1" ${MINI_ATTRS3} stroke-dasharray="3,2"/><rect x="${
|
|
4693
|
+
const cx = round11(x + LEGEND_SWATCH_W / 2);
|
|
4694
|
+
const bx = round11(cx - 8);
|
|
4695
|
+
const by = round11(y - 5);
|
|
4696
|
+
return `<rect x="${bx}" y="${by}" width="16" height="10" rx="1" ${MINI_ATTRS3} stroke-dasharray="3,2"/><rect x="${round11(bx + 2)}" y="${round11(by + 2)}" width="12" height="6" rx="1" ${MINI_ATTRS3} stroke-dasharray="3,2"/>`;
|
|
4623
4697
|
}
|
|
4624
4698
|
function orgChartLayoutSvg(layout, opts = {}) {
|
|
4625
4699
|
const labels = opts.labels ?? ORG_CHART_SVG_LABELS_EN;
|
|
@@ -4638,6 +4712,9 @@ function orgChartLayoutSvg(layout, opts = {}) {
|
|
|
4638
4712
|
if (hasAssistant) entries.push({ swatch: assistantSwatch, label: labels.legend.assistant });
|
|
4639
4713
|
if (hasDotted) entries.push({ swatch: dottedSwatch, label: labels.legend.dotted });
|
|
4640
4714
|
if (hasVacant) entries.push({ swatch: vacantSwatch, label: labels.legend.vacant });
|
|
4715
|
+
if (opts.annotationLabel !== void 0 && (layout.nodes.some((n) => n.annotated) || layout.elements.some((e) => e.annotated))) {
|
|
4716
|
+
entries.push({ swatch: annotationSwatch, label: opts.annotationLabel });
|
|
4717
|
+
}
|
|
4641
4718
|
const block = legendBlock(entries, layout.height);
|
|
4642
4719
|
if (block.svg !== "") {
|
|
4643
4720
|
parts.push(block.svg);
|
|
@@ -4658,11 +4735,13 @@ function orgChartSvg(input, opts = {}) {
|
|
|
4658
4735
|
});
|
|
4659
4736
|
const svg = orgChartLayoutSvg(layout, {
|
|
4660
4737
|
...opts.legend === false ? { legend: false } : {},
|
|
4661
|
-
...opts.svgLabels !== void 0 ? { labels: opts.svgLabels } : {}
|
|
4738
|
+
...opts.svgLabels !== void 0 ? { labels: opts.svgLabels } : {},
|
|
4739
|
+
...opts.annotationLabel !== void 0 ? { annotationLabel: opts.annotationLabel } : {}
|
|
4662
4740
|
});
|
|
4663
4741
|
return { svg, layout };
|
|
4664
4742
|
}
|
|
4665
4743
|
|
|
4744
|
+
exports.ANNOTATION_INK = ANNOTATION_INK;
|
|
4666
4745
|
exports.CHAR_W = CHAR_W;
|
|
4667
4746
|
exports.CODE_FONT = CODE_FONT;
|
|
4668
4747
|
exports.ECOMAP_LABELS_EN = ECOMAP_LABELS_EN;
|
|
@@ -4743,6 +4822,9 @@ exports.QUALITY_LEXICON_EN = QUALITY_LEXICON_EN;
|
|
|
4743
4822
|
exports.UNION_NOTATION = UNION_NOTATION;
|
|
4744
4823
|
exports.UNION_REL_ID_BASE = UNION_REL_ID_BASE;
|
|
4745
4824
|
exports.UNION_STATUSES = UNION_STATUSES;
|
|
4825
|
+
exports.annotationDot = annotationDot;
|
|
4826
|
+
exports.annotationSwatch = annotationSwatch;
|
|
4827
|
+
exports.annotationTick = annotationTick;
|
|
4746
4828
|
exports.clampLabel = clampLabel;
|
|
4747
4829
|
exports.classifyRelationshipType = classifyRelationshipType;
|
|
4748
4830
|
exports.computeFaultTreeLayout = computeFaultTreeLayout;
|