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.
Files changed (63) hide show
  1. package/README.md +29 -0
  2. package/dist/{chunk-RWPGGWO5.js → chunk-FYBABYC7.js} +34 -10
  3. package/dist/chunk-FYBABYC7.js.map +1 -0
  4. package/dist/{chunk-F47C6ZEB.js → chunk-IPE7JZO5.js} +3 -3
  5. package/dist/{chunk-F47C6ZEB.js.map → chunk-IPE7JZO5.js.map} +1 -1
  6. package/dist/{chunk-Q6DVTCXD.js → chunk-LR7BXUWM.js} +18 -6
  7. package/dist/chunk-LR7BXUWM.js.map +1 -0
  8. package/dist/{chunk-LRHHUJFZ.js → chunk-M4WA6ME7.js} +3 -3
  9. package/dist/{chunk-LRHHUJFZ.js.map → chunk-M4WA6ME7.js.map} +1 -1
  10. package/dist/{chunk-JP4N42AY.js → chunk-PGUMLTIM.js} +3 -3
  11. package/dist/{chunk-JP4N42AY.js.map → chunk-PGUMLTIM.js.map} +1 -1
  12. package/dist/{chunk-O3BT2O42.js → chunk-SD4NTRBM.js} +29 -3
  13. package/dist/chunk-SD4NTRBM.js.map +1 -0
  14. package/dist/{chunk-UJVU7B44.js → chunk-TAE2UB7D.js} +30 -14
  15. package/dist/chunk-TAE2UB7D.js.map +1 -0
  16. package/dist/{chunk-ZBDABVIO.js → chunk-WJYYBGZW.js} +3 -3
  17. package/dist/{chunk-ZBDABVIO.js.map → chunk-WJYYBGZW.js.map} +1 -1
  18. package/dist/core/index.cjs +30 -0
  19. package/dist/core/index.cjs.map +1 -1
  20. package/dist/core/index.d.cts +18 -2
  21. package/dist/core/index.d.ts +18 -2
  22. package/dist/core/index.js +1 -1
  23. package/dist/ecomap/index.cjs +34 -11
  24. package/dist/ecomap/index.cjs.map +1 -1
  25. package/dist/ecomap/index.d.cts +12 -0
  26. package/dist/ecomap/index.d.ts +12 -0
  27. package/dist/ecomap/index.js +2 -2
  28. package/dist/fault-tree/index.js +2 -2
  29. package/dist/fishbone/index.js +2 -2
  30. package/dist/genogram/index.cjs +57 -7
  31. package/dist/genogram/index.cjs.map +1 -1
  32. package/dist/genogram/index.d.cts +20 -4
  33. package/dist/genogram/index.d.ts +20 -4
  34. package/dist/genogram/index.js +2 -2
  35. package/dist/index.cjs +325 -243
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +5 -5
  38. package/dist/index.d.ts +5 -5
  39. package/dist/index.js +8 -8
  40. package/dist/{kinship-DqEklrDN.d.ts → kinship-BF90HyyS.d.ts} +1 -1
  41. package/dist/{kinship-Dy_ijjJV.d.cts → kinship-BOUss5cT.d.cts} +1 -1
  42. package/dist/{labels-RtFw9tX1.d.cts → labels-B0aOMbHy.d.cts} +12 -0
  43. package/dist/{labels-RtFw9tX1.d.ts → labels-B0aOMbHy.d.ts} +12 -0
  44. package/dist/{labels-DNqRkWuI.d.ts → labels-CuLbFyrz.d.ts} +1 -1
  45. package/dist/{labels-CBQ_3Ec9.d.cts → labels-DhQe7I8m.d.cts} +1 -1
  46. package/dist/locales/pt-br.d.cts +4 -4
  47. package/dist/locales/pt-br.d.ts +4 -4
  48. package/dist/org-chart/index.cjs +100 -58
  49. package/dist/org-chart/index.cjs.map +1 -1
  50. package/dist/org-chart/index.d.cts +19 -2
  51. package/dist/org-chart/index.d.ts +19 -2
  52. package/dist/org-chart/index.js +2 -2
  53. package/dist/pedigree/index.d.cts +4 -4
  54. package/dist/pedigree/index.d.ts +4 -4
  55. package/dist/pedigree/index.js +2 -2
  56. package/dist/phylo/index.js +2 -2
  57. package/dist/{types-BnMG7TCd.d.cts → types-jE2fdM1t.d.cts} +8 -0
  58. package/dist/{types-BnMG7TCd.d.ts → types-jE2fdM1t.d.ts} +8 -0
  59. package/package.json +1 -1
  60. package/dist/chunk-O3BT2O42.js.map +0 -1
  61. package/dist/chunk-Q6DVTCXD.js.map +0 -1
  62. package/dist/chunk-RWPGGWO5.js.map +0 -1
  63. 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 round = (n) => Math.round(n * 100) / 100;
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
- `${round(tipX)},${round(tipY)}`,
1281
- `${round(bx + px * HALF_W)},${round(by + py * HALF_W)}`,
1282
- `${round(bx - px * HALF_W)},${round(by - py * HALF_W)}`
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="${round(x1)}" y1="${round(y1)}" x2="${round(x2)}" y2="${round(y2)}" stroke="${EDGE_INK2}" stroke-width="${ink.width}" stroke-opacity="${ink.opacity}"${dashAttr}/>`
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="${round(cx)}" y="${round(cy - (centerLines.length - 1) * LINE_H / 2 + i * LINE_H + fontSize * 0.32)}">${xmlEscape(line)}</tspan>`
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="${round(cx)}" cy="${round(cy)}" r="${round(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>`
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="${round(s.x)}" y="${round(s.y - (s.lines.length - 1) * LINE_H / 2 + i * LINE_H + fontSize * 0.32)}">${xmlEscape(line)}</tspan>`
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
- parts.push(
1395
- `<g data-individual-id="e${s.tie.id}"><title>${xmlEscape(s.tie.label)}</title><ellipse cx="${round(s.x)}" cy="${round(s.y)}" rx="${round(s.rx)}" ry="${round(s.ry)}" fill="transparent" stroke="${NODE_STROKE}" stroke-width="1.5"/><text text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${fontSize}" fill="${LABEL_FILL}">${tspans}</text></g>`
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 round2 = (n) => Math.round(n * 100) / 100;
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: round2(inst.cx),
1872
- top: round2(top),
1873
- nodeW: round2(inst.m.nodeW),
1874
- nodeH: round2(inst.m.nodeH),
1875
- glyphW: round2(inst.m.glyphW),
1876
- glyphH: round2(inst.m.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 : round2(labelTop),
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: round2(inst.cx),
1897
- top: round2(gateTop),
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: round2(inst.cx + INHIBIT_W / 2), y: round2(gateTop + INHIBIT_CY) },
1915
- { x: round2(ovalLeft), y: round2(gateTop + INHIBIT_CY) }
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: round2(inst.cx), y: round2(rowTop[d] + inst.m.nodeH) },
1926
- { x: round2(inst.cx), y: round2(gateTop) }
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: round2(inst.cx), y: round2(gateTop + glyphH) },
1937
- { x: round2(inst.cx), y: round2(by) }
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: round2(Math.min(...xs)), y: round2(by) },
1949
- { x: round2(Math.max(...xs)), y: round2(by) }
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: round2(c.cx), y: round2(by) },
1961
- { x: round2(c.cx), y: round2(rowTop[d + 1]) }
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 round3 = (n) => Math.round(n * 100) / 100;
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="${round3(cx - n.nodeW / 2)}" y="${top}" width="${n.nodeW}" height="${n.nodeH}" rx="2" ${GLYPH_ATTRS}/>`;
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="${round3(top + 22)}" r="22" ${GLYPH_ATTRS}/>`;
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} ${round3(cx + 24)},${round3(top + 24)} ${cx},${round3(top + 48)} ${round3(cx - 24)},${round3(top + 24)}`;
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 = round3(top + 40);
1994
- const eave = round3(top + 16);
1995
- const pts2 = `${round3(cx - 22)},${yB} ${round3(cx - 22)},${eave} ${cx},${top} ${round3(cx + 22)},${eave} ${round3(cx + 22)},${yB}`;
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="${round3(top + 16)}" rx="${round3(n.glyphW / 2)}" ry="16" ${GLYPH_ATTRS}/>`;
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} ${round3(cx + 22)},${round3(top + 35)} ${round3(cx - 22)},${round3(top + 35)}`;
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="${round3(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>`
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 ? round3(n.top + 19) : round3(n.labelTop + 10);
2013
- const tspans = n.labelLines.map((line, i) => `<tspan x="${n.cx}" y="${round3(firstBaseline + i * FT_LABEL_LINE_H)}">${xmlEscape(line)}</tspan>`).join("");
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 ${round3(cx - 22)} ${yB} Q ${cx} ${round3(yB - 14)} ${round3(cx + 22)} ${yB} Q ${round3(cx + 22)} ${round3(top + 14)} ${cx} ${top} Q ${round3(cx - 22)} ${round3(top + 14)} ${round3(cx - 22)} ${yB} Z`;
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 = round3(top + 36);
2089
+ const yB = round4(top + 36);
2028
2090
  if (g.type === "and") {
2029
- const d = `M ${round3(cx - 22)} ${yB} L ${round3(cx - 22)} ${round3(yB - 14)} A 22 22 0 0 1 ${round3(cx + 22)} ${round3(yB - 14)} L ${round3(cx + 22)} ${yB} Z`;
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 ${round3(cx - 22)} ${round3(yB + 5)} Q ${cx} ${round3(yB - 9)} ${round3(cx + 22)} ${round3(yB + 5)}`;
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} ${round3(cx + 18)},${round3(top + 12)} ${round3(cx + 18)},${round3(top + 34)} ${cx},${round3(top + 46)} ${round3(cx - 18)},${round3(top + 34)} ${round3(cx - 18)},${round3(top + 12)}`;
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="${round3(yB - 10)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="9" fill="${LABEL_FILL2}">${xmlEscape(g.voteText ?? "")}</text>`;
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 = round3(x + LEGEND_SWATCH_W / 2);
2118
+ const cx = round4(x + LEGEND_SWATCH_W / 2);
2057
2119
  if (kind === "intermediate") {
2058
- return `<rect x="${round3(cx - 7)}" y="${round3(y - 4.5)}" width="14" height="9" rx="1" ${MINI_ATTRS}/>`;
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},${round3(y - 7)} ${round3(cx + 7)},${y} ${cx},${round3(y + 7)} ${round3(cx - 7)},${y}" ${MINI_ATTRS}/>`;
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="${round3(cx - 6)},${round3(y + 5.5)} ${round3(cx - 6)},${round3(y - 1)} ${cx},${round3(y - 5.5)} ${round3(cx + 6)},${round3(y - 1)} ${round3(cx + 6)},${round3(y + 5.5)}" ${MINI_ATTRS}/>`;
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},${round3(y - 5)} ${round3(cx + 6)},${round3(y + 5)} ${round3(cx - 6)},${round3(y + 5)}" ${MINI_ATTRS}/>`;
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 ${round3(cx - 7)} ${round3(y + 5.5)} Q ${cx} ${round3(y + 1)} ${round3(cx + 7)} ${round3(y + 5.5)} Q ${round3(cx + 7)} ${round3(y - 1.5)} ${cx} ${round3(y - 5.5)} Q ${round3(cx - 7)} ${round3(y - 1.5)} ${round3(cx - 7)} ${round3(y + 5.5)} Z`;
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 = round3(x + LEGEND_SWATCH_W / 2);
2136
+ const cx = round4(x + LEGEND_SWATCH_W / 2);
2075
2137
  if (type === "and") {
2076
- const d = `M ${round3(cx - 7)} ${round3(y + 5.5)} L ${round3(cx - 7)} ${round3(y + 1.5)} A 7 7 0 0 1 ${round3(cx + 7)} ${round3(y + 1.5)} L ${round3(cx + 7)} ${round3(y + 5.5)} Z`;
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 ${round3(cx - 7)} ${round3(y + 7.5)} Q ${cx} ${round3(y + 3)} ${round3(cx + 7)} ${round3(y + 7.5)}" ${MINI_ATTRS}/>`;
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},${round3(y - 5.5)} ${round3(cx + 4.5)},${round3(y - 2.5)} ${round3(cx + 4.5)},${round3(y + 2.5)} ${cx},${round3(y + 5.5)} ${round3(cx - 4.5)},${round3(y + 2.5)} ${round3(cx - 4.5)},${round3(y - 2.5)}" ${MINI_ATTRS}/>`;
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 round4 = (n) => Math.round(n * 100) / 100;
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
- `${round4(tipX)},${round4(tipY)}`,
2222
- `${round4(bx + px * HALF_W)},${round4(by + py * HALF_W)}`,
2223
- `${round4(bx - px * HALF_W)},${round4(by - py * HALF_W)}`
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="${round4(x1)}" y1="${round4(y1)}" x2="${round4(x2)}" y2="${round4(y2)}" stroke="${EDGE_INK4}" stroke-width="${w}" stroke-opacity="${op}"/>`;
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="${round4(x)}" y="${round4(ys[i])}">${xmlEscape(line)}</tspan>`).join("") + `</text>`;
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="${round4(tipX - bone.boxW / 2)}" y="${round4(boxTop)}" width="${round4(bone.boxW)}" height="${round4(bone.boxH)}" rx="2" fill="transparent" stroke="${BOX_STROKE}" stroke-width="1.5"/>`
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="${round4(x)}" y="${round4(spineY - headH / 2)}" width="${round4(headW)}" height="${round4(headH)}" rx="2" fill="transparent" stroke="${BOX_STROKE}" stroke-width="2"/>` + textBlock("middle", x + headW / 2, centeredYs(spineY, effLines.length), effLines) + `</g>`
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 round5 = (n) => Math.round(n * 100) / 100;
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: round5(placed.cx),
2889
- cy: round5(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: round5(cy + PED_GLYPH / 2 + PED_LABEL_GAP + rowDipBand[row]),
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: round5(hubStubX), y: round5(glyphBottom) },
2943
- { x: round5(hubStubX), y: round5(dipY) },
2944
- { x: round5(spouseStubX), y: round5(dipY) },
2945
- { x: round5(spouseStubX), y: round5(glyphBottom) }
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: round5((hubStubX + spouseStubX) / 2), y: round5(dipY) });
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: round5(lx), y: round5(y) },
2958
- { x: round5(rx), y: round5(y) }
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: round5(originX), y: round5(y) });
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: round5(upX + (downRight ? -GLYPH_HALF : GLYPH_HALF)), y: round5(upY) },
2988
- { x: round5(upExitX), y: round5(upY) },
2989
- { x: round5(upExitX), y: round5(laneY) },
2990
- { x: round5(downFarX), y: round5(laneY) },
2991
- { x: round5(downFarX), y: round5(downY) },
2992
- { x: round5(downSideX), y: round5(downY) }
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: round5(downFarX), y: round5(downY) });
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: round5(upX + (downX >= upX ? GLYPH_HALF : -GLYPH_HALF)), y: round5(upY) },
3005
- { x: round5(elbowX), y: round5(upY) },
3006
- { x: round5(elbowX), y: round5(downY) },
3007
- { x: round5(downX + (upX >= downX ? GLYPH_HALF : -GLYPH_HALF)), y: round5(downY) }
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: round5(elbowX), y: round5(downY) });
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: round5(mid.x), y: round5(mid.y) },
3035
- { x: round5(mid.x), y: round5(barY) }
3096
+ { x: round6(mid.x), y: round6(mid.y) },
3097
+ { x: round6(mid.x), y: round6(barY) }
3036
3098
  ] : [
3037
- { x: round5(mid.x), y: round5(mid.y) },
3038
- { x: round5(mid.x), y: round5(bendY) },
3039
- { x: round5(barCenterX), y: round5(bendY) },
3040
- { x: round5(barCenterX), y: round5(barY) }
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: round5(barLeft), y: round5(barY) },
3056
- { x: round5(barRight), y: round5(barY) }
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: round5(cx), y: round5(barY) },
3078
- { x: round5(cx), y: round5(childTop) }
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: round5(junctionX), y: round5(junctionY) });
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: round5(junctionX), y: round5(barY) },
3100
- { x: round5(junctionX), y: round5(junctionY) }
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: round5(Math.min(...memberXs)), y: round5(tieY) },
3112
- { x: round5(Math.max(...memberXs)), y: round5(tieY) }
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: round5(cx), y: round5(junctionY) },
3121
- { x: round5(cx), y: round5(childTop) }
3182
+ { x: round6(cx), y: round6(junctionY) },
3183
+ { x: round6(cx), y: round6(childTop) }
3122
3184
  ] : [
3123
- { x: round5(junctionX), y: round5(junctionY) },
3124
- { x: round5(cx), y: round5(junctionY) },
3125
- { x: round5(cx), y: round5(childTop) }
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: round5(glyphCyOfRow(row)) });
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 round6 = (n) => Math.round(n * 100) / 100;
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="${round6(cx - half)}" y="${round6(cy - half)}" width="${half * 2}" height="${half * 2}" ${GLYPH_ATTRS2}/>`;
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},${round6(cy - half)} ${round6(cx + half)},${cy} ${cx},${round6(cy + half)} ${round6(cx - half)},${cy}" ${GLYPH_ATTRS2}/>`;
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(`${round6(x)},${round6(yt)}`);
3212
- bottom.push(`${round6(x)},${round6(yb)}`);
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="${round6(n.cx - ext)}" y1="${round6(n.cy - ext)}" x2="${round6(n.cx + ext)}" y2="${round6(n.cy + ext)}" stroke="${GLYPH_STROKE3}" stroke-width="2"/>`;
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 = round6(n.cx - half);
3231
- const tipY = round6(n.cy + half);
3232
- const tailX = round6(tipX - 16);
3233
- const tailY = round6(tipY + 16);
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} ${round6(tipX - 8)},${round6(tipY + 2)} ${round6(tipX - 2)},${round6(tipY + 8)}" fill="${fill}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/>`;
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 = round6(n.cy + half + PED_ADDRESS_FONT);
3243
- return `<text x="${round6(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>`;
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 = round6(n.labelTop + PED_ADDRESS_FONT);
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 = round6(n.labelTop + PED_LABEL_LINE_H + 10);
3263
- const tspans = n.labelLines.map((line, i) => `<tspan x="${n.cx}" y="${round6(firstBaseline + i * PED_LABEL_LINE_H)}">${xmlEscape(line)}</tspan>`).join("");
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: round6(p.y + offsetY) }));
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="${round6(p.x + 6)}" y="${round6(p.y + 4)}" font-family="${FONT_FAMILY}" font-size="12" font-weight="bold" fill="${LABEL_FILL4}">?</text>`
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 = round6(x + LEGEND_SWATCH_W / 2);
3295
- if (shape === "square") return `<rect x="${round6(cx - 6)}" y="${round6(y - 6)}" width="12" height="12" ${MINI_ATTRS2}/>`;
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},${round6(y - 7)} ${round6(cx + 7)},${y} ${cx},${round6(y + 7)} ${round6(cx - 7)},${y}" ${MINI_ATTRS2}/>`;
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 = round6(x + LEGEND_SWATCH_W / 2);
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 = round6(x + LEGEND_SWATCH_W / 2);
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 = round6(x + LEGEND_SWATCH_W / 2);
3309
- return `<circle cx="${cx}" cy="${y}" r="6" ${MINI_ATTRS2}/><line x1="${round6(cx - 7)}" y1="${round6(y - 7)}" x2="${round6(cx + 7)}" y2="${round6(y + 7)}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/>`;
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 = round6(x + LEGEND_SWATCH_W / 2);
3313
- const tipX = round6(cx - 2);
3314
- const tipY = round6(y + 2);
3315
- return `<line x1="${round6(tipX - 8)}" y1="${round6(tipY + 8)}" x2="${tipX}" y2="${tipY}" stroke="${GLYPH_STROKE3}" stroke-width="1.5"/><polygon points="${tipX},${tipY} ${round6(tipX - 5)},${round6(tipY + 1)} ${round6(tipX - 1)},${round6(tipY + 5)}" fill="${filled ? GLYPH_STROKE3 : "transparent"}" stroke="${GLYPH_STROKE3}" stroke-width="1"/>`;
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 = round6(x + 2);
3319
- const x2 = round6(x + LEGEND_SWATCH_W - 2);
3320
- return `<line x1="${x1}" y1="${round6(y - 1.5)}" x2="${x2}" y2="${round6(y - 1.5)}" stroke="${EDGE_INK5}" stroke-width="1.5"/><line x1="${x1}" y1="${round6(y + 1.5)}" x2="${x2}" y2="${round6(y + 1.5)}" stroke="${EDGE_INK5}" stroke-width="1.5"/>`;
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 = round6(x + LEGEND_SWATCH_W / 2);
3324
- const apexY = round6(y - 6);
3325
- const baseY = round6(y + 6);
3326
- const left = round6(cx - 6);
3327
- const right = round6(cx + 6);
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="${round6(y + 3)}" x2="${right}" y2="${round6(y + 3)}" stroke="${EDGE_INK5}" stroke-width="1.5"/>`;
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="${round6(cx + 8)}" y="${round6(y + 3)}" font-family="${FONT_FAMILY}" font-size="9" fill="${LABEL_FILL4}">?</text>`;
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="${round6(16)}" y="${round6(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>`
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 round7 = (n) => Math.round(n * 100) / 100;
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: round7(Math.max(PADDING6 / 2, rootDrawX - ROOT_STUB)), y: round7(root.cy) },
3723
- { x: round7(rootDrawX), y: round7(root.cy) }
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: round7(drawX),
3739
- cy: round7(w.cy),
3800
+ cx: round8(drawX),
3801
+ cy: round8(w.cy),
3740
3802
  support,
3741
3803
  labelLines: labelLine,
3742
- labelLeft: round7(drawX + PHYLO_TIP_R + PHYLO_LABEL_GAP),
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: round7(w.cx), y: round7(w.cy) },
3752
- { x: round7(alignX), y: round7(w.cy) }
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: round7(w.cx), y: round7(barTop) },
3769
- { x: round7(w.cx), y: round7(barBottom) }
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: round7(w.cx), y: round7(c.cy) },
3782
- { x: round7(c.cx), y: round7(c.cy) }
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: round7(barX),
3800
- y: round7(barY),
3801
- pxLength: round7(scaleBarPx),
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: round7(barX), y: round7(barY) },
3809
- { x: round7(barX + scaleBarPx), y: round7(barY) }
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 round8 = (n) => Math.round(n * 100) / 100;
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="${round8(n.cy + PHYLO_LABEL_FONT * 0.32)}" font-family="${FONT_FAMILY}" font-size="${PHYLO_LABEL_FONT}" fill="${LABEL_FILL5}">${xmlEscape(n.labelLines[0])}</text>`
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="${round8(n.cx - 4)}" y="${round8(n.cy - 3)}" text-anchor="end" font-family="${FONT_FAMILY}" font-size="${PHYLO_SUPPORT_FONT}" fill="${LABEL_FILL5}">${xmlEscape(String(n.support))}</text>`
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="${round8(bar.y - 3)}" x2="${x}" y2="${round8(bar.y + 3)}" stroke="${EDGE_INK6}" stroke-width="1.5" stroke-opacity="0.75"/>`;
3850
- return tick(bar.x) + tick(round8(bar.x + bar.pxLength)) + `<text x="${round8(bar.x + bar.pxLength / 2)}" y="${round8(bar.y - 6)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="${PHYLO_SUPPORT_FONT}" fill="${LABEL_FILL5}">${xmlEscape(bar.valueLabel)}</text>`;
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 = round8(x + LEGEND_SWATCH_W / 2);
3854
- return `<circle cx="${cx}" cy="${y}" r="2.5" fill="${GLYPH_STROKE4}"/><text x="${round8(cx + 5)}" y="${round8(y + PHYLO_SUPPORT_FONT * 0.32)}" font-family="${FONT_FAMILY}" font-size="${PHYLO_SUPPORT_FONT}" fill="${LABEL_FILL5}">95</text>`;
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="${round8(x)}" y1="${y}" x2="${round8(x + LEGEND_SWATCH_W)}" y2="${y}" stroke="${EDGE_INK6}" stroke-width="1.5" stroke-opacity="0.75"/>`;
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="${round8(x)}" y1="${y}" x2="${round8(x + LEGEND_SWATCH_W)}" y2="${y}" stroke="${EDGE_INK6}" stroke-width="1.5" stroke-opacity="0.5" stroke-dasharray="2,3"/>`;
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 round9 = (n) => Math.round(n * 100) / 100;
4109
- var drawnLeftEdge = (cx, boxW) => round9(round9(cx) - round9(boxW) / 2);
4110
- var drawnRightEdge = (cx, boxW) => drawnLeftEdge(cx, boxW) + round9(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: round9(inst.cx),
4279
- top: round9(rowTop[inst.depth]),
4280
- boxW: round9(inst.m.boxW),
4281
- boxH: round9(inst.m.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: round9(a.cx),
4304
- top: round9(assistTop),
4305
- boxW: round9(a.m.boxW),
4306
- boxH: round9(a.m.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: round9(inst.cx), y: round9(assistMidY) },
4321
- { x: drawnRightEdge(a.cx, a.m.boxW), y: round9(assistMidY) }
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: round9(inst.cx), y: round9(boxBottom) },
4337
- { x: round9(inst.cx), y: round9(by) }
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: round9(Math.min(...childCxs)), y: round9(by) },
4349
- { x: round9(Math.max(...childCxs)), y: round9(by) }
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: round9(c.cx), y: round9(by) },
4361
- { x: round9(c.cx), y: round9(rowTop[c.depth]) }
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: round9(nextExitY(r.reportId, rep.midY, rep.boxH)),
4435
- mgrMidY: round9(nextExitY(r.managerId, mgr.midY, mgr.boxH)),
4436
- repBoxEdge: round9(rep.boxEdge),
4437
- mgrBoxEdge: round9(mgr.boxEdge),
4438
- repChY: round9(channelTop + 2 * i * ORG_MATRIX_LANE_PITCH),
4439
- mgrChY: round9(channelTop + (2 * i + 1) * ORG_MATRIX_LANE_PITCH),
4440
- laneX: round9(canvasRight + ORG_MATRIX_GUTTER_GAP + laneOf.get(r.id) * ORG_MATRIX_LANE_PITCH),
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 = round9(v.escCol);
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 = round9(v.escCol + v.side * lane * ORG_MATRIX_ESCAPE_STEP);
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 ${round9(band)}px box-free band`
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 round10 = (n) => Math.round(n * 100) / 100;
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 = round10(n.cx - n.boxW / 2);
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 = round10(left + 3);
4567
- const iy = round10(n.top + 3);
4568
- const iw = round10(n.boxW - 6);
4569
- const ih = round10(n.boxH - 6);
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) => round10(n.top + ORG_BOX_PAD_Y2 + ORG_LABEL_LINE_H / 2 + i * ORG_LABEL_LINE_H + ORG_LABEL_FONT * 0.32);
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}/></g>`;
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"/></g>`;
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"/></g>`;
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 = round10(x + 2);
4604
- const x2 = round10(x + LEGEND_SWATCH_W - 2);
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 = round10(x + LEGEND_SWATCH_W - 4);
4609
- const boxX = round10(x + 1);
4610
- return `<line x1="${stemX}" y1="${round10(y - 4)}" x2="${stemX}" y2="${y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/><line x1="${stemX}" y1="${y}" x2="${round10(boxX + 8)}" y2="${y}" stroke="${EDGE_INK7}" stroke-width="1.5" stroke-opacity="0.75"/><rect x="${boxX}" y="${round10(y - 3.5)}" width="8" height="7" rx="1" ${MINI_ATTRS3}/>`;
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 = round10(x + 2);
4614
- const x2 = round10(x + LEGEND_SWATCH_W - 2);
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 = round10(x + LEGEND_SWATCH_W / 2);
4620
- const bx = round10(cx - 8);
4621
- const by = round10(y - 5);
4622
- return `<rect x="${bx}" y="${by}" width="16" height="10" rx="1" ${MINI_ATTRS3} stroke-dasharray="3,2"/><rect x="${round10(bx + 2)}" y="${round10(by + 2)}" width="12" height="6" rx="1" ${MINI_ATTRS3} stroke-dasharray="3,2"/>`;
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;