@mstone6969/graph 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/README.md +111 -14
  2. package/dist/ExcelWriter-3ZSYUH4T.cjs +154 -0
  3. package/dist/GraphType.js +24 -38
  4. package/dist/PdfWriter-6HJFYMCO.cjs +205 -0
  5. package/dist/catalog-YWX6FHRM.cjs +8 -0
  6. package/dist/catalog.js +5 -1
  7. package/dist/charts/bars/index.js +2 -12
  8. package/dist/charts/comparison/EffectForestPlot.js +1 -1
  9. package/dist/charts/comparison/index.js +1 -8
  10. package/dist/charts/demography/Nomogram.js +3 -3
  11. package/dist/charts/density/HexbinChart.js +6 -4
  12. package/dist/charts/diagrams/ArchitectureCanvas.js +68 -0
  13. package/dist/charts/diagrams/ArchitectureDiagram.js +331 -0
  14. package/dist/charts/diagrams/ArchitectureLayout.js +106 -0
  15. package/dist/charts/diagrams/ArchitectureLinkLabels.js +78 -0
  16. package/dist/charts/diagrams/ArchitectureModel.js +108 -0
  17. package/dist/charts/diagrams/ArchitectureRouter.js +348 -0
  18. package/dist/charts/diagrams/ArchitectureSamples.js +312 -0
  19. package/dist/charts/diagrams/ArchitectureShapes.js +130 -0
  20. package/dist/charts/diagrams/ArchitectureText.js +25 -0
  21. package/dist/charts/diagrams/DiagramText.js +38 -0
  22. package/dist/charts/diagrams/EntityRelationshipDiagram.js +465 -0
  23. package/dist/charts/diagrams/ErCrowsFoot.js +33 -0
  24. package/dist/charts/diagrams/ErSamples.js +205 -0
  25. package/dist/charts/diagrams/ErSchema.js +122 -0
  26. package/dist/charts/diagrams/LogicCircuit.js +154 -0
  27. package/dist/charts/diagrams/LogicCircuitLayout.js +600 -0
  28. package/dist/charts/diagrams/LogicCircuitSamples.js +66 -0
  29. package/dist/charts/diagrams/LogicGateDiagram.js +216 -0
  30. package/dist/charts/diagrams/LogicGateSymbol.js +113 -0
  31. package/dist/charts/diagrams/NetworkTopologyDiagram.js +247 -0
  32. package/dist/charts/diagrams/SequenceDiagram.js +351 -0
  33. package/dist/charts/diagrams/SequenceLayout.js +422 -0
  34. package/dist/charts/diagrams/SequenceModel.js +221 -0
  35. package/dist/charts/diagrams/TopologyLayout.js +143 -0
  36. package/dist/charts/diagrams/architectureEnums.js +50 -0
  37. package/dist/charts/diagrams/index.js +26 -0
  38. package/dist/charts/diagrams/logicEnums.js +42 -0
  39. package/dist/charts/diagrams/sequenceEnums.js +40 -0
  40. package/dist/charts/distribution/HistogramChart.js +25 -3
  41. package/dist/charts/distribution/index.js +1 -3
  42. package/dist/charts/earthscience/HovmollerDiagram.js +477 -0
  43. package/dist/charts/earthscience/HovmollerEnums.js +42 -0
  44. package/dist/charts/earthscience/HovmollerField.js +180 -0
  45. package/dist/charts/earthscience/HovmollerSample.js +40 -0
  46. package/dist/charts/earthscience/HovmollerSpeed.js +71 -0
  47. package/dist/charts/earthscience/index.js +10 -2
  48. package/dist/charts/engineering/ConstellationDiagram.js +26 -6
  49. package/dist/charts/engineering/EyeDiagram.js +2 -1
  50. package/dist/charts/engineering/PoleZeroPlot.js +32 -8
  51. package/dist/charts/financial/RelativeStrengthRotation.js +434 -0
  52. package/dist/charts/financial/RelativeStrengthRotationMath.js +71 -0
  53. package/dist/charts/financial/RelativeStrengthRotationSamples.js +27 -0
  54. package/dist/charts/financial/index.js +5 -3
  55. package/dist/charts/flow/index.js +1 -5
  56. package/dist/charts/frameworks/AnsoffMatrix.js +523 -0
  57. package/dist/charts/frameworks/FrameworkText.js +98 -0
  58. package/dist/charts/frameworks/NpsGauge.js +279 -0
  59. package/dist/charts/frameworks/NpsSurvey.js +58 -0
  60. package/dist/charts/frameworks/PerceptualMap.js +419 -0
  61. package/dist/charts/frameworks/PerceptualProjection.js +142 -0
  62. package/dist/charts/frameworks/PositioningSamples.js +191 -0
  63. package/dist/charts/frameworks/RaciMatrix.js +458 -0
  64. package/dist/charts/frameworks/RaciPlan.js +81 -0
  65. package/dist/charts/frameworks/RaciSamples.js +76 -0
  66. package/dist/charts/frameworks/SwotMatrix.js +358 -0
  67. package/dist/charts/frameworks/SwotSamples.js +85 -0
  68. package/dist/charts/frameworks/VanWestendorpChart.js +187 -0
  69. package/dist/charts/frameworks/VanWestendorpSurvey.js +197 -0
  70. package/dist/charts/frameworks/index.js +26 -0
  71. package/dist/charts/frameworks/raciEnums.js +30 -0
  72. package/dist/charts/frameworks/swotEnums.js +34 -0
  73. package/dist/charts/genomics/CircosPlot.js +1 -1
  74. package/dist/charts/geoflows/GeoFlowChart.js +2 -2
  75. package/dist/charts/geoflows/GeoFlowSample.js +2 -1
  76. package/dist/charts/geomaps/FloorPlanMap.js +324 -0
  77. package/dist/charts/geomaps/FloorPlanModel.js +179 -0
  78. package/dist/charts/geomaps/FloorPlanSamples.js +416 -0
  79. package/dist/charts/geomaps/GeoLand.js +2 -2
  80. package/dist/charts/geomaps/GeoSamples.js +2 -1
  81. package/dist/charts/geomaps/GraticuleMap.js +2 -2
  82. package/dist/charts/geomaps/RoomLabelFit.js +200 -0
  83. package/dist/charts/geomaps/TissotMap.js +2 -2
  84. package/dist/charts/geomaps/index.js +8 -4
  85. package/dist/charts/hierarchy/SunburstChart.js +17 -10
  86. package/dist/charts/hierarchy/index.js +2 -11
  87. package/dist/charts/indicators/BulletChart.js +8 -2
  88. package/dist/charts/indicators/SpeedometerChart.js +1 -1
  89. package/dist/charts/indicators/ThermometerChart.js +1 -1
  90. package/dist/charts/lifescience/LoessFit.js +113 -0
  91. package/dist/charts/lifescience/MaPlot.js +337 -0
  92. package/dist/charts/lifescience/MaPlotSamples.js +35 -0
  93. package/dist/charts/lifescience/OncoGlyph.js +137 -0
  94. package/dist/charts/lifescience/OncoPrint.js +373 -0
  95. package/dist/charts/lifescience/OncoPrintMatrix.js +107 -0
  96. package/dist/charts/lifescience/OncoPrintSamples.js +142 -0
  97. package/dist/charts/lifescience/PedigreeChart.js +306 -0
  98. package/dist/charts/lifescience/PedigreeLayout.js +261 -0
  99. package/dist/charts/lifescience/PedigreeRows.js +70 -0
  100. package/dist/charts/lifescience/PedigreeSamples.js +161 -0
  101. package/dist/charts/lifescience/PedigreeSymbol.js +47 -0
  102. package/dist/charts/lifescience/RamachandranPlot.js +364 -0
  103. package/dist/charts/lifescience/RamachandranRegions.js +2712 -0
  104. package/dist/charts/lifescience/RamachandranSamples.js +235 -0
  105. package/dist/charts/lifescience/index.js +25 -0
  106. package/dist/charts/lifescience/pedigreeEnums.js +21 -0
  107. package/dist/charts/lifescience/ramachandranEnums.js +34 -0
  108. package/dist/charts/linesareas/DualAxisChart.js +1 -1
  109. package/dist/charts/linesareas/LineEnums.js +1 -15
  110. package/dist/charts/linesareas/MultiLineChart.js +1 -1
  111. package/dist/charts/linesareas/TrendChart.js +15 -5
  112. package/dist/charts/linesareas/index.js +2 -6
  113. package/dist/charts/massive/MassiveChart.js +3 -2
  114. package/dist/charts/mathematical/FourierSeriesChart.js +7 -1
  115. package/dist/charts/mathematical/index.js +2 -6
  116. package/dist/charts/matrix/GridPlot.js +27 -2
  117. package/dist/charts/matrix/TilegramChart.js +2 -2
  118. package/dist/charts/mlcharts/AttentionGrid.js +79 -0
  119. package/dist/charts/mlcharts/AttentionHeatmap.js +329 -0
  120. package/dist/charts/mlcharts/AttentionModel.js +132 -0
  121. package/dist/charts/mlcharts/AttentionSamples.js +82 -0
  122. package/dist/charts/mlcharts/CellNumber.js +8 -0
  123. package/dist/charts/mlcharts/CurveDiagnosticSamples.js +95 -0
  124. package/dist/charts/mlcharts/CurvePickMark.js +31 -0
  125. package/dist/charts/mlcharts/DecisionBoundaryChart.js +316 -0
  126. package/dist/charts/mlcharts/DecisionSamples.js +60 -0
  127. package/dist/charts/mlcharts/DecisionSurface.js +130 -0
  128. package/dist/charts/mlcharts/DistinctTicks.js +13 -0
  129. package/dist/charts/mlcharts/ElbowMath.js +153 -0
  130. package/dist/charts/mlcharts/ElbowPlot.js +273 -0
  131. package/dist/charts/mlcharts/ExplainabilitySamples.js +70 -0
  132. package/dist/charts/mlcharts/FeatureImportanceChart.js +150 -0
  133. package/dist/charts/mlcharts/ImportanceRanking.js +98 -0
  134. package/dist/charts/mlcharts/LearningCurveChart.js +276 -0
  135. package/dist/charts/mlcharts/LearningCurveMath.js +92 -0
  136. package/dist/charts/mlcharts/PartialDependence.js +92 -0
  137. package/dist/charts/mlcharts/PartialDependencePlot.js +225 -0
  138. package/dist/charts/mlcharts/WideLabel.js +28 -0
  139. package/dist/charts/mlcharts/curveDiagnosticEnums.js +37 -0
  140. package/dist/charts/mlcharts/index.js +32 -0
  141. package/dist/charts/proportion/index.js +5 -9
  142. package/dist/charts/scatter/CategoryScatterBase.js +1 -4
  143. package/dist/charts/scatter/ConnectedScatter.js +7 -4
  144. package/dist/charts/scatter/MarginalScatter.js +13 -8
  145. package/dist/charts/scatter/index.js +4 -17
  146. package/dist/charts/scientific/SpectrogramChart.js +9 -3
  147. package/dist/charts/scientific/index.js +2 -2
  148. package/dist/charts/signals/index.js +3 -13
  149. package/dist/charts/statistics/LorenzCurve.js +7 -4
  150. package/dist/charts/threebars/Candlestick3DChart.js +18 -2
  151. package/dist/charts/threeglobe/GlobeChart.js +2 -2
  152. package/dist/charts/threeglobe/GlobeSamples.js +2 -1
  153. package/dist/charts/threeglobe/RingGlobe.js +1 -1
  154. package/dist/charts/threenetwork/ConeTreeChart.js +2 -1
  155. package/dist/charts/threenetwork/Gantt3DChart.js +3 -2
  156. package/dist/charts/threesurfaces/Ridgeline3DChart.js +22 -4
  157. package/dist/charts/threevolume/LorenzAttractor.js +19 -0
  158. package/dist/charts/threevolume/VolumeRaycastChart.js +4 -3
  159. package/dist/charts/threevolume/volumeEnums.js +1 -0
  160. package/dist/charts/timeline/index.js +2 -10
  161. package/dist/chunk-753UBLV2.cjs +33 -0
  162. package/dist/chunk-VX54A7KA.cjs +17 -0
  163. package/dist/{chunk-W5GAAAZY.cjs → chunk-WAODDPYX.cjs} +24874 -17620
  164. package/dist/chunk-Z3JPIJSC.cjs +338 -0
  165. package/dist/core/CartesianChart.js +87 -11
  166. package/dist/core/Chart.js +63 -15
  167. package/dist/core/Chart2D.js +54 -17
  168. package/dist/core/Chart3D.js +56 -20
  169. package/dist/core/ChartExportError.js +2 -0
  170. package/dist/core/ChartExporter.js +50 -8
  171. package/dist/core/ColumnCheck.js +53 -0
  172. package/dist/core/Context2D.js +29 -15
  173. package/dist/core/Context3D.js +7 -2
  174. package/dist/core/ExcelWriter.js +152 -0
  175. package/dist/core/ExportText.js +28 -0
  176. package/dist/core/MarkOrigin.js +88 -0
  177. package/dist/core/MarkStyle.js +294 -0
  178. package/dist/core/Palette.js +27 -7
  179. package/dist/core/PdfPage.js +13 -0
  180. package/dist/core/PdfWriter.js +245 -0
  181. package/dist/core/Scales.js +52 -2
  182. package/dist/core/SceneOverlay.js +4 -4
  183. package/dist/core/TextFit.js +37 -0
  184. package/dist/core/Theme.js +28 -1
  185. package/dist/core/enums.js +33 -1
  186. package/dist/core/pathMorph.js +15 -0
  187. package/dist/data/SampleData.js +1 -17
  188. package/dist/data/WorldSample.js +23 -0
  189. package/dist/index.cjs +810 -691
  190. package/dist/index.d.cts +8390 -2925
  191. package/dist/index.d.ts +8392 -2927
  192. package/dist/index.js +106 -56
  193. package/dist/react/ChartView.js +18 -8
  194. package/dist/react/GraphHandle.js +2 -0
  195. package/dist/react/components.js +74 -116
  196. package/package.json +13 -3
  197. package/dist/catalog-4UP2YFNR.cjs +0 -7
  198. package/dist/charts/bars/BulletBar.js +0 -184
  199. package/dist/charts/bars/ErrorBarColumn.js +0 -147
  200. package/dist/charts/bars/HistogramBar.js +0 -217
  201. package/dist/charts/bars/MarimekkoBar.js +0 -191
  202. package/dist/charts/bars/ParetoBar.js +0 -143
  203. package/dist/charts/comparison/PanelChart.js +0 -70
  204. package/dist/charts/comparison/SmallMultipleBars.js +0 -134
  205. package/dist/charts/comparison/SpanChart.js +0 -118
  206. package/dist/charts/comparison/TrellisChart.js +0 -158
  207. package/dist/charts/distribution/RidgeSlabs3DChart.js +0 -169
  208. package/dist/charts/financial/Candlestick3D.js +0 -157
  209. package/dist/charts/flow/BumpFlow.js +0 -212
  210. package/dist/charts/flow/StreamFlow.js +0 -174
  211. package/dist/charts/geomaps/ThiessenMap.js +0 -106
  212. package/dist/charts/hierarchy/ConeTree3D.js +0 -135
  213. package/dist/charts/hierarchy/Hierarchy3DChart.js +0 -27
  214. package/dist/charts/hierarchy/SpherePack3D.js +0 -81
  215. package/dist/charts/hierarchy/SunburstTower3D.js +0 -93
  216. package/dist/charts/hierarchy/TreemapSkyline3D.js +0 -98
  217. package/dist/charts/linesareas/ConnectedScatterTime.js +0 -206
  218. package/dist/charts/linesareas/SmallMultipleLines.js +0 -153
  219. package/dist/charts/mathematical/ParametricSurface3D.js +0 -234
  220. package/dist/charts/mathematical/StrangeAttractor3D.js +0 -251
  221. package/dist/charts/proportion/ConeFunnel.js +0 -126
  222. package/dist/charts/proportion/ExtrudedPieChart.js +0 -123
  223. package/dist/charts/scatter/BeeswarmScatter.js +0 -151
  224. package/dist/charts/scatter/BinnedScatterBase.js +0 -51
  225. package/dist/charts/scatter/ContourScatter.js +0 -116
  226. package/dist/charts/scatter/GridBinScatter.js +0 -98
  227. package/dist/charts/scatter/HexbinScatter.js +0 -74
  228. package/dist/charts/scatter/StripScatter.js +0 -107
  229. package/dist/charts/scatter/SunflowerScatter.js +0 -109
  230. package/dist/charts/signals/ConstellationChart.js +0 -153
  231. package/dist/charts/signals/CorrelogramChart.js +0 -171
  232. package/dist/charts/signals/EyeDiagramChart.js +0 -155
  233. package/dist/charts/signals/PoleZeroChart.js +0 -164
  234. package/dist/charts/signals/SpectrogramChart.js +0 -79
  235. package/dist/charts/timeline/CalendarSkylineChart.js +0 -128
  236. package/dist/charts/timeline/ScheduleBlocksChart.js +0 -119
  237. package/dist/charts/timeline/SpiralTimeline.js +0 -154
  238. package/dist/charts/timeline/StreamTimeline.js +0 -141
@@ -0,0 +1,142 @@
1
+ import { SampleData } from "../../data/SampleData.js";
2
+ import { StatMath } from "../statistics/StatMath.js";
3
+ // src/charts/lifescience/OncoPrintSamples.ts
4
+ /** Each list is one pathway: a sample takes at most one gene from it, so the staircase is designed in. */
5
+ var PATHWAYS = [
6
+ [
7
+ [
8
+ "SYN1",
9
+ .3,
10
+ [
11
+ "Missense",
12
+ "Missense",
13
+ "Amplification"
14
+ ]
15
+ ],
16
+ [
17
+ "SYN2",
18
+ .16,
19
+ [
20
+ "Missense",
21
+ "Amplification",
22
+ "In-frame"
23
+ ]
24
+ ],
25
+ [
26
+ "SYN3",
27
+ .08,
28
+ ["Missense"]
29
+ ],
30
+ [
31
+ "SYN4",
32
+ .05,
33
+ ["Fusion"]
34
+ ],
35
+ [
36
+ "SYN5",
37
+ .05,
38
+ ["Splice", "Amplification"]
39
+ ]
40
+ ],
41
+ [
42
+ [
43
+ "SYN6",
44
+ .2,
45
+ ["Deep deletion", "Truncating"]
46
+ ],
47
+ [
48
+ "SYN7",
49
+ .08,
50
+ ["Amplification", "Gain"]
51
+ ],
52
+ [
53
+ "SYN8",
54
+ .07,
55
+ ["Truncating", "Deep deletion"]
56
+ ]
57
+ ],
58
+ [[
59
+ "SYN9",
60
+ .15,
61
+ ["Missense", "Amplification"]
62
+ ], [
63
+ "SYN10",
64
+ .1,
65
+ [
66
+ "Deep deletion",
67
+ "Truncating",
68
+ "Shallow loss"
69
+ ]
70
+ ]],
71
+ [[
72
+ "SYN11",
73
+ .14,
74
+ ["Truncating", "Splice"]
75
+ ], [
76
+ "SYN12",
77
+ .1,
78
+ ["Missense", "Truncating"]
79
+ ]]
80
+ ];
81
+ var TP53 = [
82
+ "SYN13",
83
+ .55,
84
+ [
85
+ "Missense",
86
+ "Missense",
87
+ "Truncating",
88
+ "Splice"
89
+ ]
90
+ ];
91
+ var MYC = [
92
+ "SYN14",
93
+ .12,
94
+ ["Amplification", "Gain"]
95
+ ];
96
+ var QUIET = .1;
97
+ /** An invented cohort, not a measurement: placeholder `SYN` gene names and invented frequencies. */
98
+ var OncoPrintSamples = class {
99
+ /** `{ sample, gene, alteration, source }`, long form. A row with no gene marks a profiled sample with nothing to show. */
100
+ static cohort(seed, samples = 64) {
101
+ const draws = SampleData.distribution(seed, samples * 20, 1).map((row) => StatMath.normalCdf((row.value - 40) / 8));
102
+ let next = 0;
103
+ const u = () => draws[next++ % draws.length];
104
+ const rows = [
105
+ TP53,
106
+ MYC,
107
+ ...PATHWAYS.flat()
108
+ ].map(([gene]) => ({
109
+ sample: null,
110
+ gene,
111
+ alteration: null,
112
+ source: "synthetic"
113
+ }));
114
+ for (let i = 0; i < samples; i++) {
115
+ const sample = `SYN-${String(i + 1).padStart(3, "0")}`;
116
+ const hit = ([gene, , kinds]) => {
117
+ rows.push({
118
+ sample,
119
+ gene,
120
+ alteration: kinds[Math.floor(u() * kinds.length)],
121
+ source: "synthetic"
122
+ });
123
+ };
124
+ rows.push({
125
+ sample,
126
+ gene: null,
127
+ alteration: null,
128
+ source: "synthetic"
129
+ });
130
+ if (u() < QUIET) continue;
131
+ if (u() < TP53[1]) hit(TP53);
132
+ if (u() < MYC[1]) hit(MYC);
133
+ for (const pathway of PATHWAYS) {
134
+ let pick = u();
135
+ const chosen = pathway.find(([, share]) => (pick -= share) < 0);
136
+ if (chosen) hit(chosen);
137
+ }
138
+ }
139
+ return rows;
140
+ }
141
+ };
142
+ export { OncoPrintSamples };
@@ -0,0 +1,306 @@
1
+ import { Chart2D } from "../../core/Chart2D.js";
2
+ import { ChartFamily, DataShape, Dimension } from "../../core/enums.js";
3
+ import { PedigreeCaption, PedigreeCarrierMark, PedigreeSex } from "./pedigreeEnums.js";
4
+ import { PedigreeLayout } from "./PedigreeLayout.js";
5
+ import { PedigreeRows } from "./PedigreeRows.js";
6
+ import { PedigreeSamples } from "./PedigreeSamples.js";
7
+ import { PedigreeSymbol } from "./PedigreeSymbol.js";
8
+ // src/charts/lifescience/PedigreeChart.ts
9
+ var NUMERALS = [
10
+ [1e3, "M"],
11
+ [900, "CM"],
12
+ [500, "D"],
13
+ [400, "CD"],
14
+ [100, "C"],
15
+ [90, "XC"],
16
+ [50, "L"],
17
+ [40, "XL"],
18
+ [10, "X"],
19
+ [9, "IX"],
20
+ [5, "V"],
21
+ [4, "IV"],
22
+ [1, "I"]
23
+ ];
24
+ var MAX_SIZE = 30;
25
+ var MIN_SIZE = 8;
26
+ var SLOT_SHARE = .66;
27
+ var CHAR = 6.2;
28
+ var NAME_CHAR = 5.6;
29
+ var LINE = 2;
30
+ var THIN_LINE = 1;
31
+ var LINE_SHARE = 7;
32
+ var CONSANGUINITY_GAP = 2.5;
33
+ var LANE_STEP = 3;
34
+ var LANE_ROOM = 8;
35
+ var NUMBER_ROOM = 13;
36
+ var KEY_ROW = 19;
37
+ var KEY_MARK = 11;
38
+ var OVER_LIFT = 7;
39
+ var ARROW_ROOM = 34;
40
+ var LETTER_DROP = 12;
41
+ var LETTER_HALF = 5;
42
+ var SEX_LABELS = {
43
+ [PedigreeSex.Male]: "Male",
44
+ [PedigreeSex.Female]: "Female",
45
+ [PedigreeSex.Unknown]: "Sex unknown"
46
+ };
47
+ /**
48
+ * Trace how a trait runs through a family, in the standard human pedigree nomenclature of Bennett et al.
49
+ * (2008) J Genet Couns 17:424-433: square male, circle female, diamond sex unknown, filled affected,
50
+ * dot carrier, slash deceased, arrow and P proband, double line consanguinity. Generations are numbered
51
+ * in Roman numerals and people left to right within each. Unions of relatives are detected from shared
52
+ * ancestors. Parents, siblings in birth order and marriages come from `father`, `mother` and `partner`.
53
+ */
54
+ var PedigreeChart = class PedigreeChart extends Chart2D {
55
+ static meta = {
56
+ id: "pedigree",
57
+ name: "Pedigree chart",
58
+ family: ChartFamily.Scientific,
59
+ dimension: Dimension.Two,
60
+ shape: DataShape.Table,
61
+ description: "Draws a family tree in standard genetics notation, showing who is affected, a carrier or deceased across numbered generations.",
62
+ tags: [
63
+ "genetics",
64
+ "inheritance",
65
+ "family",
66
+ "nsgc",
67
+ "proband",
68
+ "consanguinity"
69
+ ],
70
+ sample: (seed) => PedigreeSamples.recessive(seed),
71
+ variants: {
72
+ carrierMark: Object.values(PedigreeCarrierMark),
73
+ caption: Object.values(PedigreeCaption),
74
+ showKey: [true, false]
75
+ }
76
+ };
77
+ defaults() {
78
+ return {
79
+ id: "id",
80
+ father: "father",
81
+ mother: "mother",
82
+ sex: "sex",
83
+ affected: "affected",
84
+ carrier: "carrier",
85
+ deceased: "deceased",
86
+ proband: "proband",
87
+ partner: "partner",
88
+ consanguineous: "consanguineous",
89
+ label: "label",
90
+ carrierMark: PedigreeCarrierMark.Dot,
91
+ caption: PedigreeCaption.Number,
92
+ showKey: true
93
+ };
94
+ }
95
+ plotMargin() {
96
+ return {
97
+ top: 8,
98
+ right: 12,
99
+ bottom: 8,
100
+ left: 12
101
+ };
102
+ }
103
+ static roman(value) {
104
+ let left = value, out = "";
105
+ for (const [size, glyph] of NUMERALS) while (left >= size) {
106
+ out += glyph;
107
+ left -= size;
108
+ }
109
+ return out;
110
+ }
111
+ static #tag(item) {
112
+ return `${PedigreeChart.roman(item.generation + 1)}-${item.number}`;
113
+ }
114
+ #frame(ctx, layout, keyHeight) {
115
+ const { width, height } = ctx.plot, generations = layout.generations || 1, { caption } = this.options;
116
+ const captionRoom = caption === PedigreeCaption.None ? 0 : caption === PedigreeCaption.Number ? NUMBER_ROOM : 26;
117
+ const gutter = Math.max(24, PedigreeChart.roman(generations).length * 7.4 + 12);
118
+ const slot = (width - gutter) / (layout.extent + 1);
119
+ const room = height - keyHeight;
120
+ const arrowDrop = layout.people.some((item) => item.person.proband) ? ARROW_ROOM : 0;
121
+ const across = Math.max(slot * SLOT_SHARE, Math.min(MIN_SIZE, slot * .85));
122
+ const lanes = Math.max(0, ...layout.lanes);
123
+ const down = (room - 8 - arrowDrop + captionRoom) / generations - captionRoom - LANE_ROOM - LANE_STEP * Math.max(0, lanes - 1);
124
+ const size = Math.max(4, Math.min(MAX_SIZE, across, down));
125
+ const top = size * .62 + 5;
126
+ const floor = room - 4 - size / 2 - Math.max(captionRoom, arrowDrop);
127
+ const step = generations > 1 ? Math.max(0, floor - top) / (generations - 1) : 0;
128
+ const y = (generation) => top + generation * step;
129
+ const laneY = (generation, lane) => {
130
+ const under = y(generation + 1) - size * .62 - 3, gap = under - (y(generation) + size / 2 + captionRoom);
131
+ return under - lane * Math.min(LANE_STEP, gap / Math.max(1, layout.lanes[generation] ?? 1));
132
+ };
133
+ return {
134
+ size,
135
+ line: Math.max(THIN_LINE, Math.min(LINE, size / LINE_SHARE)),
136
+ x: (at) => gutter + slot * (at + .5),
137
+ y,
138
+ laneY,
139
+ captionRoom,
140
+ gutter,
141
+ keyTop: room
142
+ };
143
+ }
144
+ #couplePath(couple, { x, y, size }) {
145
+ const from = x(couple.left.x), to = x(couple.right.x), at = y(couple.left.generation);
146
+ if (!couple.adjacent) {
147
+ const over = at - size / 2 - OVER_LIFT + (couple.consanguineous ? CONSANGUINITY_GAP : 0);
148
+ const span = (lift) => `M${from} ${at - size / 2}V${over + lift}H${to}V${at - size / 2}`;
149
+ return couple.consanguineous ? span(-2.5) + span(CONSANGUINITY_GAP) : span(0);
150
+ }
151
+ return couple.consanguineous ? `M${from} ${at - CONSANGUINITY_GAP}H${to}M${from} ${at + CONSANGUINITY_GAP}H${to}` : `M${from} ${at}H${to}`;
152
+ }
153
+ #sibshipPath(sibship, frame, couples) {
154
+ const { x, y, laneY, size } = frame;
155
+ const lane = laneY(sibship.generation, sibship.lane), parentRow = sibship.parents[0].generation;
156
+ const couple = couples.get(sibship.key);
157
+ const start = sibship.parents.length === 1 ? y(parentRow) + PedigreeSymbol.reach(sibship.parents[0].person.sex, size) : couple && !couple.adjacent ? y(parentRow) - size / 2 - OVER_LIFT : y(parentRow) + (couple?.consanguineous ? CONSANGUINITY_GAP : 0);
158
+ const drops = sibship.children.map((child) => `M${x(child.x)} ${lane}V${y(child.generation) - PedigreeSymbol.reach(child.person.sex, size)}`).join("");
159
+ return `M${x(sibship.drop)} ${start}V${lane}M${x(sibship.from)} ${lane}H${x(sibship.to)}${drops}`;
160
+ }
161
+ #drawLines(ctx, layout, frame) {
162
+ const line = (selection) => selection.attr("fill", "none").attr("stroke", ctx.theme.inkSecondary).attr("stroke-width", frame.line);
163
+ const known = new Map(layout.couples.map((couple) => [couple.key, couple]));
164
+ ctx.set(line(ctx.marks("descent-lines", "path", layout.sibships, (d) => d.key)), { d: (d) => this.#sibshipPath(d, frame, known) });
165
+ ctx.set(line(ctx.marks("union-lines", "path", layout.couples, (d) => d.key)), { d: (d) => this.#couplePath(d, frame) });
166
+ }
167
+ #drawSymbols(ctx, people, frame) {
168
+ const { x, y, size, line } = frame, { ink, surface } = ctx.theme;
169
+ const dotted = this.options.carrierMark === PedigreeCarrierMark.Dot;
170
+ const carriers = people.filter((item) => item.person.carrier && !item.person.affected);
171
+ const shapes = ctx.marks("symbols", "path", people, (d) => d.person.key).attr("fill", (d) => d.person.affected ? ink : surface).attr("stroke", ink).attr("stroke-width", line);
172
+ ctx.set(shapes, {
173
+ d: (d) => PedigreeSymbol.outline(d.person.sex, x(d.x), y(d.generation), size),
174
+ "fill-opacity": 1
175
+ }, { "fill-opacity": 0 }, 12);
176
+ ctx.hover(shapes, (d) => ({
177
+ title: d.person.name,
178
+ rows: [
179
+ ["Individual", PedigreeChart.#tag(d)],
180
+ ["Sex", SEX_LABELS[d.person.sex]],
181
+ ["Status", [
182
+ d.person.affected ? "Affected" : d.person.carrier ? "Carrier" : "Unaffected",
183
+ d.person.deceased && "deceased",
184
+ d.person.proband && "proband"
185
+ ].filter(Boolean).join(", ")]
186
+ ]
187
+ }));
188
+ const halves = ctx.marks("carrier-halves", "path", dotted ? [] : carriers, (d) => d.person.key).attr("fill", ink).attr("pointer-events", "none");
189
+ ctx.set(halves, { d: (d) => PedigreeSymbol.leftHalf(d.person.sex, x(d.x), y(d.generation), size) });
190
+ const dots = ctx.marks("carrier-dots", "circle", dotted ? carriers : [], (d) => d.person.key).attr("fill", ink).attr("pointer-events", "none");
191
+ ctx.set(dots, {
192
+ cx: (d) => x(d.x),
193
+ cy: (d) => y(d.generation),
194
+ r: Math.max(1.5, size * .14)
195
+ });
196
+ const slashes = ctx.marks("slashes", "line", people.filter((item) => item.person.deceased), (d) => d.person.key).attr("stroke", ink).attr("stroke-width", line).attr("pointer-events", "none");
197
+ const slash = (d) => PedigreeSymbol.slash(x(d.x), y(d.generation), size);
198
+ ctx.set(slashes, {
199
+ x1: (d) => slash(d).x1,
200
+ y1: (d) => slash(d).y1,
201
+ x2: (d) => slash(d).x2,
202
+ y2: (d) => slash(d).y2
203
+ });
204
+ }
205
+ #drawProbands(ctx, people, { x, y, size, line }) {
206
+ const probands = people.filter((item) => item.person.proband);
207
+ const arrow = (d) => PedigreeSymbol.arrow(x(d.x), y(d.generation), size);
208
+ const shafts = ctx.marks("proband-shafts", "path", probands, (d) => d.person.key).attr("fill", "none").attr("stroke", ctx.theme.ink).attr("stroke-width", line).attr("pointer-events", "none");
209
+ ctx.set(shafts, { d: (d) => arrow(d).shaft });
210
+ const heads = ctx.marks("proband-heads", "path", probands, (d) => d.person.key).attr("fill", ctx.theme.ink).attr("pointer-events", "none");
211
+ ctx.set(heads, { d: (d) => arrow(d).head });
212
+ ctx.text(ctx.marks("proband-letters", "text", probands, (d) => d.person.key), 11, "ink").attr("font-weight", 600).attr("text-anchor", "middle").attr("pointer-events", "none").attr("stroke", ctx.theme.surface).attr("stroke-width", 3).attr("paint-order", "stroke").attr("x", (d) => arrow(d).tail[0]).attr("y", (d) => arrow(d).tail[1] + LETTER_DROP).text("P");
213
+ }
214
+ #drawCaptions(ctx, people, layout, { x, y, size, captionRoom }) {
215
+ const { caption } = this.options;
216
+ const slot = layout.extent > 0 ? Math.abs(x(1) - x(0)) : ctx.plot.width;
217
+ const fit = (text) => {
218
+ const most = Math.floor((slot - 4) / NAME_CHAR);
219
+ if (most < 2) return "";
220
+ return text.length > most ? `${text.slice(0, most - 1)}…` : text;
221
+ };
222
+ const solo = new Set(layout.sibships.filter((item) => item.parents.length === 1).map((item) => item.parents[0].person.key));
223
+ const sideways = (d) => solo.has(d.person.key) ? 4 : 0;
224
+ const under = (d) => y(d.generation) + PedigreeSymbol.reach(d.person.sex, size);
225
+ const shown = captionRoom ? people.filter(() => slot >= 12) : [];
226
+ ctx.text(ctx.marks("numbers", "text", shown, (d) => d.person.key), 10.5, "secondary").attr("pointer-events", "none").attr("text-anchor", (d) => sideways(d) ? "start" : "middle").attr("x", (d) => x(d.x) + sideways(d)).attr("y", (d) => under(d) + 13).text((d) => d.number);
227
+ const letters = people.filter((d) => d.person.proband).map((d) => PedigreeSymbol.arrow(x(d.x), y(d.generation), size).tail);
228
+ const clear = (d) => {
229
+ const half = fit(d.person.name).length * NAME_CHAR / 2 + LETTER_HALF, base = under(d) + 25;
230
+ return !letters.some(([at, top]) => Math.abs(at - x(d.x)) < half && top + LETTER_DROP + 2 > base - 9 && top < base);
231
+ };
232
+ const names = caption === PedigreeCaption.NumberAndName ? shown.filter((d) => fit(d.person.name) && clear(d)) : [];
233
+ ctx.text(ctx.marks("names", "text", names, (d) => d.person.key), 10, "muted").attr("pointer-events", "none").attr("text-anchor", (d) => sideways(d) ? "start" : "middle").attr("x", (d) => x(d.x) + sideways(d)).attr("y", (d) => under(d) + 25).text((d) => fit(d.person.name));
234
+ }
235
+ #drawGenerations(ctx, layout, { y }) {
236
+ const rows = Array.from({ length: layout.generations }, (_, i) => i);
237
+ ctx.text(ctx.marks("generations", "text", rows, (d) => d), 12, "ink").attr("font-weight", 600).attr("dy", "0.35em").attr("x", 0).attr("y", (d) => y(d)).text((d) => PedigreeChart.roman(d + 1));
238
+ }
239
+ #keyEntries(layout) {
240
+ const m = KEY_MARK, c = m / 2, { people, couples } = layout, some = (test) => people.some(test);
241
+ const square = PedigreeSymbol.outline(PedigreeSex.Male, c, 0, m);
242
+ const entry = (label, shown, stroke, fill = "", dot) => shown ? [{
243
+ label,
244
+ stroke,
245
+ fill,
246
+ dot
247
+ }] : [];
248
+ const carrier = this.options.carrierMark === PedigreeCarrierMark.Dot;
249
+ const slash = PedigreeSymbol.slash(c, 0, m);
250
+ const arrow = PedigreeSymbol.arrow(11.5, -7, 7);
251
+ return [
252
+ ...entry(SEX_LABELS[PedigreeSex.Male], some((d) => d.person.sex === PedigreeSex.Male), square),
253
+ ...entry(SEX_LABELS[PedigreeSex.Female], some((d) => d.person.sex === PedigreeSex.Female), PedigreeSymbol.outline(PedigreeSex.Female, c, 0, m)),
254
+ ...entry(SEX_LABELS[PedigreeSex.Unknown], some((d) => d.person.sex === PedigreeSex.Unknown), PedigreeSymbol.outline(PedigreeSex.Unknown, c, 0, m * .85)),
255
+ ...entry("Affected", some((d) => d.person.affected), square, square),
256
+ ...entry("Carrier", some((d) => d.person.carrier && !d.person.affected), square, carrier ? "" : PedigreeSymbol.leftHalf(PedigreeSex.Male, c, 0, m), carrier ? [c, 2] : void 0),
257
+ ...entry("Deceased", some((d) => d.person.deceased), `${square}M${slash.x1} ${slash.y1}L${slash.x2} ${slash.y2}`),
258
+ ...entry("Proband", some((d) => d.person.proband), arrow.shaft, arrow.head),
259
+ ...entry("Consanguineous", couples.some((couple) => couple.consanguineous), `M0 -2.5H14M0 2.5H14`)
260
+ ];
261
+ }
262
+ /** Greedy rows of symbol and name. Height comes first, since it takes room from the generations. */
263
+ #placeKey(entries, width) {
264
+ let x = 0, row = 0;
265
+ return {
266
+ placed: entries.map((entry) => {
267
+ const cost = 19 + entry.label.length * CHAR + 14;
268
+ if (x && x + cost - 14 > width) {
269
+ x = 0;
270
+ row++;
271
+ }
272
+ const at = {
273
+ ...entry,
274
+ key: entry.label,
275
+ x,
276
+ y: row
277
+ };
278
+ x += cost;
279
+ return at;
280
+ }),
281
+ height: entries.length ? (row + 1) * KEY_ROW + 4 : 0
282
+ };
283
+ }
284
+ #drawKey(ctx, placed, top) {
285
+ const groups = ctx.marks("key", "g", placed, (d) => d.key);
286
+ ctx.set(groups, { transform: (d) => `translate(${d.x + 4},${top + 12 + d.y * KEY_ROW})` });
287
+ groups.selectAll("path.key-line").data((d) => [d]).join("path").attr("class", "key-line").attr("fill", "none").attr("stroke", ctx.theme.ink).attr("stroke-width", 1.5).attr("d", (d) => d.stroke);
288
+ groups.selectAll("path.key-fill").data((d) => [d]).join("path").attr("class", "key-fill").attr("fill", ctx.theme.ink).attr("d", (d) => d.fill);
289
+ groups.selectAll("circle").data((d) => d.dot ? [d.dot] : []).join("circle").attr("fill", ctx.theme.ink).attr("cx", (d) => d[0]).attr("cy", 0).attr("r", 2);
290
+ ctx.text(groups.selectAll("text").data((d) => [d]).join("text"), 10.5).attr("x", 18).attr("dy", "0.35em").text((d) => d.label);
291
+ }
292
+ draw(ctx) {
293
+ const { options } = this;
294
+ const columns = options;
295
+ const layout = new PedigreeLayout(PedigreeRows.parse(options.data, columns));
296
+ const key = this.#placeKey(options.showKey ? this.#keyEntries(layout) : [], ctx.plot.width);
297
+ const frame = this.#frame(ctx, layout, key.height);
298
+ this.#drawGenerations(ctx, layout, frame);
299
+ this.#drawLines(ctx, layout, frame);
300
+ this.#drawSymbols(ctx, layout.people, frame);
301
+ this.#drawProbands(ctx, layout.people, frame);
302
+ this.#drawCaptions(ctx, layout.people, layout, frame);
303
+ this.#drawKey(ctx, key.placed, frame.keyTop);
304
+ }
305
+ };
306
+ export { PedigreeChart };