@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.
- package/README.md +111 -14
- package/dist/ExcelWriter-3ZSYUH4T.cjs +154 -0
- package/dist/GraphType.js +24 -38
- package/dist/PdfWriter-6HJFYMCO.cjs +205 -0
- package/dist/catalog-YWX6FHRM.cjs +8 -0
- package/dist/catalog.js +5 -1
- package/dist/charts/bars/index.js +2 -12
- package/dist/charts/comparison/EffectForestPlot.js +1 -1
- package/dist/charts/comparison/index.js +1 -8
- package/dist/charts/demography/Nomogram.js +3 -3
- package/dist/charts/density/HexbinChart.js +6 -4
- package/dist/charts/diagrams/ArchitectureCanvas.js +68 -0
- package/dist/charts/diagrams/ArchitectureDiagram.js +331 -0
- package/dist/charts/diagrams/ArchitectureLayout.js +106 -0
- package/dist/charts/diagrams/ArchitectureLinkLabels.js +78 -0
- package/dist/charts/diagrams/ArchitectureModel.js +108 -0
- package/dist/charts/diagrams/ArchitectureRouter.js +348 -0
- package/dist/charts/diagrams/ArchitectureSamples.js +312 -0
- package/dist/charts/diagrams/ArchitectureShapes.js +130 -0
- package/dist/charts/diagrams/ArchitectureText.js +25 -0
- package/dist/charts/diagrams/DiagramText.js +38 -0
- package/dist/charts/diagrams/EntityRelationshipDiagram.js +465 -0
- package/dist/charts/diagrams/ErCrowsFoot.js +33 -0
- package/dist/charts/diagrams/ErSamples.js +205 -0
- package/dist/charts/diagrams/ErSchema.js +122 -0
- package/dist/charts/diagrams/LogicCircuit.js +154 -0
- package/dist/charts/diagrams/LogicCircuitLayout.js +600 -0
- package/dist/charts/diagrams/LogicCircuitSamples.js +66 -0
- package/dist/charts/diagrams/LogicGateDiagram.js +216 -0
- package/dist/charts/diagrams/LogicGateSymbol.js +113 -0
- package/dist/charts/diagrams/NetworkTopologyDiagram.js +247 -0
- package/dist/charts/diagrams/SequenceDiagram.js +351 -0
- package/dist/charts/diagrams/SequenceLayout.js +422 -0
- package/dist/charts/diagrams/SequenceModel.js +221 -0
- package/dist/charts/diagrams/TopologyLayout.js +143 -0
- package/dist/charts/diagrams/architectureEnums.js +50 -0
- package/dist/charts/diagrams/index.js +26 -0
- package/dist/charts/diagrams/logicEnums.js +42 -0
- package/dist/charts/diagrams/sequenceEnums.js +40 -0
- package/dist/charts/distribution/HistogramChart.js +25 -3
- package/dist/charts/distribution/index.js +1 -3
- package/dist/charts/earthscience/HovmollerDiagram.js +477 -0
- package/dist/charts/earthscience/HovmollerEnums.js +42 -0
- package/dist/charts/earthscience/HovmollerField.js +180 -0
- package/dist/charts/earthscience/HovmollerSample.js +40 -0
- package/dist/charts/earthscience/HovmollerSpeed.js +71 -0
- package/dist/charts/earthscience/index.js +10 -2
- package/dist/charts/engineering/ConstellationDiagram.js +26 -6
- package/dist/charts/engineering/EyeDiagram.js +2 -1
- package/dist/charts/engineering/PoleZeroPlot.js +32 -8
- package/dist/charts/financial/RelativeStrengthRotation.js +434 -0
- package/dist/charts/financial/RelativeStrengthRotationMath.js +71 -0
- package/dist/charts/financial/RelativeStrengthRotationSamples.js +27 -0
- package/dist/charts/financial/index.js +5 -3
- package/dist/charts/flow/index.js +1 -5
- package/dist/charts/frameworks/AnsoffMatrix.js +523 -0
- package/dist/charts/frameworks/FrameworkText.js +98 -0
- package/dist/charts/frameworks/NpsGauge.js +279 -0
- package/dist/charts/frameworks/NpsSurvey.js +58 -0
- package/dist/charts/frameworks/PerceptualMap.js +419 -0
- package/dist/charts/frameworks/PerceptualProjection.js +142 -0
- package/dist/charts/frameworks/PositioningSamples.js +191 -0
- package/dist/charts/frameworks/RaciMatrix.js +458 -0
- package/dist/charts/frameworks/RaciPlan.js +81 -0
- package/dist/charts/frameworks/RaciSamples.js +76 -0
- package/dist/charts/frameworks/SwotMatrix.js +358 -0
- package/dist/charts/frameworks/SwotSamples.js +85 -0
- package/dist/charts/frameworks/VanWestendorpChart.js +187 -0
- package/dist/charts/frameworks/VanWestendorpSurvey.js +197 -0
- package/dist/charts/frameworks/index.js +26 -0
- package/dist/charts/frameworks/raciEnums.js +30 -0
- package/dist/charts/frameworks/swotEnums.js +34 -0
- package/dist/charts/genomics/CircosPlot.js +1 -1
- package/dist/charts/geoflows/GeoFlowChart.js +2 -2
- package/dist/charts/geoflows/GeoFlowSample.js +2 -1
- package/dist/charts/geomaps/FloorPlanMap.js +324 -0
- package/dist/charts/geomaps/FloorPlanModel.js +179 -0
- package/dist/charts/geomaps/FloorPlanSamples.js +416 -0
- package/dist/charts/geomaps/GeoLand.js +2 -2
- package/dist/charts/geomaps/GeoSamples.js +2 -1
- package/dist/charts/geomaps/GraticuleMap.js +2 -2
- package/dist/charts/geomaps/RoomLabelFit.js +200 -0
- package/dist/charts/geomaps/TissotMap.js +2 -2
- package/dist/charts/geomaps/index.js +8 -4
- package/dist/charts/hierarchy/SunburstChart.js +17 -10
- package/dist/charts/hierarchy/index.js +2 -11
- package/dist/charts/indicators/BulletChart.js +8 -2
- package/dist/charts/indicators/SpeedometerChart.js +1 -1
- package/dist/charts/indicators/ThermometerChart.js +1 -1
- package/dist/charts/lifescience/LoessFit.js +113 -0
- package/dist/charts/lifescience/MaPlot.js +337 -0
- package/dist/charts/lifescience/MaPlotSamples.js +35 -0
- package/dist/charts/lifescience/OncoGlyph.js +137 -0
- package/dist/charts/lifescience/OncoPrint.js +373 -0
- package/dist/charts/lifescience/OncoPrintMatrix.js +107 -0
- package/dist/charts/lifescience/OncoPrintSamples.js +142 -0
- package/dist/charts/lifescience/PedigreeChart.js +306 -0
- package/dist/charts/lifescience/PedigreeLayout.js +261 -0
- package/dist/charts/lifescience/PedigreeRows.js +70 -0
- package/dist/charts/lifescience/PedigreeSamples.js +161 -0
- package/dist/charts/lifescience/PedigreeSymbol.js +47 -0
- package/dist/charts/lifescience/RamachandranPlot.js +364 -0
- package/dist/charts/lifescience/RamachandranRegions.js +2712 -0
- package/dist/charts/lifescience/RamachandranSamples.js +235 -0
- package/dist/charts/lifescience/index.js +25 -0
- package/dist/charts/lifescience/pedigreeEnums.js +21 -0
- package/dist/charts/lifescience/ramachandranEnums.js +34 -0
- package/dist/charts/linesareas/DualAxisChart.js +1 -1
- package/dist/charts/linesareas/LineEnums.js +1 -15
- package/dist/charts/linesareas/MultiLineChart.js +1 -1
- package/dist/charts/linesareas/TrendChart.js +15 -5
- package/dist/charts/linesareas/index.js +2 -6
- package/dist/charts/massive/MassiveChart.js +3 -2
- package/dist/charts/mathematical/FourierSeriesChart.js +7 -1
- package/dist/charts/mathematical/index.js +2 -6
- package/dist/charts/matrix/GridPlot.js +27 -2
- package/dist/charts/matrix/TilegramChart.js +2 -2
- package/dist/charts/mlcharts/AttentionGrid.js +79 -0
- package/dist/charts/mlcharts/AttentionHeatmap.js +329 -0
- package/dist/charts/mlcharts/AttentionModel.js +132 -0
- package/dist/charts/mlcharts/AttentionSamples.js +82 -0
- package/dist/charts/mlcharts/CellNumber.js +8 -0
- package/dist/charts/mlcharts/CurveDiagnosticSamples.js +95 -0
- package/dist/charts/mlcharts/CurvePickMark.js +31 -0
- package/dist/charts/mlcharts/DecisionBoundaryChart.js +316 -0
- package/dist/charts/mlcharts/DecisionSamples.js +60 -0
- package/dist/charts/mlcharts/DecisionSurface.js +130 -0
- package/dist/charts/mlcharts/DistinctTicks.js +13 -0
- package/dist/charts/mlcharts/ElbowMath.js +153 -0
- package/dist/charts/mlcharts/ElbowPlot.js +273 -0
- package/dist/charts/mlcharts/ExplainabilitySamples.js +70 -0
- package/dist/charts/mlcharts/FeatureImportanceChart.js +150 -0
- package/dist/charts/mlcharts/ImportanceRanking.js +98 -0
- package/dist/charts/mlcharts/LearningCurveChart.js +276 -0
- package/dist/charts/mlcharts/LearningCurveMath.js +92 -0
- package/dist/charts/mlcharts/PartialDependence.js +92 -0
- package/dist/charts/mlcharts/PartialDependencePlot.js +225 -0
- package/dist/charts/mlcharts/WideLabel.js +28 -0
- package/dist/charts/mlcharts/curveDiagnosticEnums.js +37 -0
- package/dist/charts/mlcharts/index.js +32 -0
- package/dist/charts/proportion/index.js +5 -9
- package/dist/charts/scatter/CategoryScatterBase.js +1 -4
- package/dist/charts/scatter/ConnectedScatter.js +7 -4
- package/dist/charts/scatter/MarginalScatter.js +13 -8
- package/dist/charts/scatter/index.js +4 -17
- package/dist/charts/scientific/SpectrogramChart.js +9 -3
- package/dist/charts/scientific/index.js +2 -2
- package/dist/charts/signals/index.js +3 -13
- package/dist/charts/statistics/LorenzCurve.js +7 -4
- package/dist/charts/threebars/Candlestick3DChart.js +18 -2
- package/dist/charts/threeglobe/GlobeChart.js +2 -2
- package/dist/charts/threeglobe/GlobeSamples.js +2 -1
- package/dist/charts/threeglobe/RingGlobe.js +1 -1
- package/dist/charts/threenetwork/ConeTreeChart.js +2 -1
- package/dist/charts/threenetwork/Gantt3DChart.js +3 -2
- package/dist/charts/threesurfaces/Ridgeline3DChart.js +22 -4
- package/dist/charts/threevolume/LorenzAttractor.js +19 -0
- package/dist/charts/threevolume/VolumeRaycastChart.js +4 -3
- package/dist/charts/threevolume/volumeEnums.js +1 -0
- package/dist/charts/timeline/index.js +2 -10
- package/dist/chunk-753UBLV2.cjs +33 -0
- package/dist/chunk-VX54A7KA.cjs +17 -0
- package/dist/{chunk-W5GAAAZY.cjs → chunk-WAODDPYX.cjs} +24874 -17620
- package/dist/chunk-Z3JPIJSC.cjs +338 -0
- package/dist/core/CartesianChart.js +87 -11
- package/dist/core/Chart.js +63 -15
- package/dist/core/Chart2D.js +54 -17
- package/dist/core/Chart3D.js +56 -20
- package/dist/core/ChartExportError.js +2 -0
- package/dist/core/ChartExporter.js +50 -8
- package/dist/core/ColumnCheck.js +53 -0
- package/dist/core/Context2D.js +29 -15
- package/dist/core/Context3D.js +7 -2
- package/dist/core/ExcelWriter.js +152 -0
- package/dist/core/ExportText.js +28 -0
- package/dist/core/MarkOrigin.js +88 -0
- package/dist/core/MarkStyle.js +294 -0
- package/dist/core/Palette.js +27 -7
- package/dist/core/PdfPage.js +13 -0
- package/dist/core/PdfWriter.js +245 -0
- package/dist/core/Scales.js +52 -2
- package/dist/core/SceneOverlay.js +4 -4
- package/dist/core/TextFit.js +37 -0
- package/dist/core/Theme.js +28 -1
- package/dist/core/enums.js +33 -1
- package/dist/core/pathMorph.js +15 -0
- package/dist/data/SampleData.js +1 -17
- package/dist/data/WorldSample.js +23 -0
- package/dist/index.cjs +810 -691
- package/dist/index.d.cts +8390 -2925
- package/dist/index.d.ts +8392 -2927
- package/dist/index.js +106 -56
- package/dist/react/ChartView.js +18 -8
- package/dist/react/GraphHandle.js +2 -0
- package/dist/react/components.js +74 -116
- package/package.json +13 -3
- package/dist/catalog-4UP2YFNR.cjs +0 -7
- package/dist/charts/bars/BulletBar.js +0 -184
- package/dist/charts/bars/ErrorBarColumn.js +0 -147
- package/dist/charts/bars/HistogramBar.js +0 -217
- package/dist/charts/bars/MarimekkoBar.js +0 -191
- package/dist/charts/bars/ParetoBar.js +0 -143
- package/dist/charts/comparison/PanelChart.js +0 -70
- package/dist/charts/comparison/SmallMultipleBars.js +0 -134
- package/dist/charts/comparison/SpanChart.js +0 -118
- package/dist/charts/comparison/TrellisChart.js +0 -158
- package/dist/charts/distribution/RidgeSlabs3DChart.js +0 -169
- package/dist/charts/financial/Candlestick3D.js +0 -157
- package/dist/charts/flow/BumpFlow.js +0 -212
- package/dist/charts/flow/StreamFlow.js +0 -174
- package/dist/charts/geomaps/ThiessenMap.js +0 -106
- package/dist/charts/hierarchy/ConeTree3D.js +0 -135
- package/dist/charts/hierarchy/Hierarchy3DChart.js +0 -27
- package/dist/charts/hierarchy/SpherePack3D.js +0 -81
- package/dist/charts/hierarchy/SunburstTower3D.js +0 -93
- package/dist/charts/hierarchy/TreemapSkyline3D.js +0 -98
- package/dist/charts/linesareas/ConnectedScatterTime.js +0 -206
- package/dist/charts/linesareas/SmallMultipleLines.js +0 -153
- package/dist/charts/mathematical/ParametricSurface3D.js +0 -234
- package/dist/charts/mathematical/StrangeAttractor3D.js +0 -251
- package/dist/charts/proportion/ConeFunnel.js +0 -126
- package/dist/charts/proportion/ExtrudedPieChart.js +0 -123
- package/dist/charts/scatter/BeeswarmScatter.js +0 -151
- package/dist/charts/scatter/BinnedScatterBase.js +0 -51
- package/dist/charts/scatter/ContourScatter.js +0 -116
- package/dist/charts/scatter/GridBinScatter.js +0 -98
- package/dist/charts/scatter/HexbinScatter.js +0 -74
- package/dist/charts/scatter/StripScatter.js +0 -107
- package/dist/charts/scatter/SunflowerScatter.js +0 -109
- package/dist/charts/signals/ConstellationChart.js +0 -153
- package/dist/charts/signals/CorrelogramChart.js +0 -171
- package/dist/charts/signals/EyeDiagramChart.js +0 -155
- package/dist/charts/signals/PoleZeroChart.js +0 -164
- package/dist/charts/signals/SpectrogramChart.js +0 -79
- package/dist/charts/timeline/CalendarSkylineChart.js +0 -128
- package/dist/charts/timeline/ScheduleBlocksChart.js +0 -119
- package/dist/charts/timeline/SpiralTimeline.js +0 -154
- package/dist/charts/timeline/StreamTimeline.js +0 -141
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { PedigreeSex } from "./pedigreeEnums.js";
|
|
2
|
+
import { mean } from "d3";
|
|
3
|
+
// src/charts/lifescience/PedigreeLayout.ts
|
|
4
|
+
var SWEEPS = 12;
|
|
5
|
+
var LANE_GAP = .4;
|
|
6
|
+
var FAINT = .01;
|
|
7
|
+
/**
|
|
8
|
+
* Lays a pedigree out in unit slots, one symbol per slot. People married to each other form a family
|
|
9
|
+
* block on one row, a row being a generation. Blocks are ordered depth-first, which keeps birth order
|
|
10
|
+
* within a sibship and avoids crossings on tree-shaped pedigrees, then slid under their parents and
|
|
11
|
+
* over their children by least squares, so nothing swaps. Sibship lines that overlap take separate lanes.
|
|
12
|
+
*/
|
|
13
|
+
var PedigreeLayout = class PedigreeLayout {
|
|
14
|
+
people;
|
|
15
|
+
couples;
|
|
16
|
+
sibships;
|
|
17
|
+
generations;
|
|
18
|
+
extent;
|
|
19
|
+
/** Sibship lanes used in the gap under each generation. */
|
|
20
|
+
lanes;
|
|
21
|
+
#persons;
|
|
22
|
+
#unions = /* @__PURE__ */ new Map();
|
|
23
|
+
#families = /* @__PURE__ */ new Map();
|
|
24
|
+
#ancestors = /* @__PURE__ */ new Map();
|
|
25
|
+
constructor(persons) {
|
|
26
|
+
this.#persons = new Map(persons.map((person) => [person.key, person]));
|
|
27
|
+
this.#link(persons);
|
|
28
|
+
const families = this.#groupFamilies();
|
|
29
|
+
PedigreeLayout.#assignGenerations(families, this.#families);
|
|
30
|
+
const rows = this.#order(families);
|
|
31
|
+
this.generations = rows.length;
|
|
32
|
+
this.#place(rows);
|
|
33
|
+
const shift = families.length ? Math.min(...families.map((family) => family.left)) : 0;
|
|
34
|
+
for (const family of families) family.left -= shift;
|
|
35
|
+
const placed = /* @__PURE__ */ new Map();
|
|
36
|
+
rows.forEach((row, generation) => {
|
|
37
|
+
let number = 0;
|
|
38
|
+
for (const family of [...row].sort((a, b) => a.left - b.left)) family.members.forEach((key, i) => placed.set(key, {
|
|
39
|
+
person: this.#persons.get(key),
|
|
40
|
+
generation,
|
|
41
|
+
number: ++number,
|
|
42
|
+
x: family.left + i
|
|
43
|
+
}));
|
|
44
|
+
});
|
|
45
|
+
this.people = [...placed.values()];
|
|
46
|
+
this.extent = Math.max(0, ...this.people.map((item) => item.x));
|
|
47
|
+
const unions = [...this.#unions.values()];
|
|
48
|
+
this.couples = unions.filter((union) => union.members.length === 2).map((union) => this.#couple(union, placed));
|
|
49
|
+
this.lanes = Array.from({ length: rows.length }, () => 0);
|
|
50
|
+
this.sibships = this.#sibships(unions, placed);
|
|
51
|
+
}
|
|
52
|
+
#link(persons) {
|
|
53
|
+
const union = (members) => {
|
|
54
|
+
const key = [...members].sort().join("\0");
|
|
55
|
+
let found = this.#unions.get(key);
|
|
56
|
+
if (!found) this.#unions.set(key, found = {
|
|
57
|
+
key,
|
|
58
|
+
members,
|
|
59
|
+
children: [],
|
|
60
|
+
flagged: false
|
|
61
|
+
});
|
|
62
|
+
return found;
|
|
63
|
+
};
|
|
64
|
+
for (const person of persons) {
|
|
65
|
+
const parents = [person.father, person.mother].filter((id) => !!id);
|
|
66
|
+
if (parents.length) {
|
|
67
|
+
const family = union(parents);
|
|
68
|
+
family.children.push(person.key);
|
|
69
|
+
family.flagged ||= person.consanguineous;
|
|
70
|
+
}
|
|
71
|
+
if (person.partner) union([person.key, person.partner]).flagged ||= person.consanguineous;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
#groupFamilies() {
|
|
75
|
+
const families = [];
|
|
76
|
+
for (const key of this.#persons.keys()) {
|
|
77
|
+
if (this.#families.has(key)) continue;
|
|
78
|
+
const family = {
|
|
79
|
+
members: [],
|
|
80
|
+
unions: [],
|
|
81
|
+
generation: 0,
|
|
82
|
+
left: 0,
|
|
83
|
+
seen: false
|
|
84
|
+
};
|
|
85
|
+
const stack = [key];
|
|
86
|
+
while (stack.length) {
|
|
87
|
+
const next = stack.pop();
|
|
88
|
+
if (this.#families.has(next)) continue;
|
|
89
|
+
this.#families.set(next, family);
|
|
90
|
+
family.members.push(next);
|
|
91
|
+
for (const union of this.#unions.values()) if (union.members.includes(next) && union.members.length === 2) stack.push(...union.members);
|
|
92
|
+
}
|
|
93
|
+
family.unions = [...this.#unions.values()].filter((union) => union.members.some((member) => family.members.includes(member)));
|
|
94
|
+
family.members = this.#chain(family);
|
|
95
|
+
families.push(family);
|
|
96
|
+
}
|
|
97
|
+
return families;
|
|
98
|
+
}
|
|
99
|
+
/** Partners side by side: a walk from an end of the marriage chain. A partner it cannot reach next to their spouse is routed over. */
|
|
100
|
+
#chain(family) {
|
|
101
|
+
const partners = (key) => family.unions.filter((union) => union.members.length === 2 && union.members.includes(key)).map((union) => union.members.find((member) => member !== key));
|
|
102
|
+
const start = [...family.members].sort((a, b) => partners(a).length - partners(b).length)[0];
|
|
103
|
+
const order = [];
|
|
104
|
+
const walk = (key) => {
|
|
105
|
+
if (order.includes(key)) return;
|
|
106
|
+
order.push(key);
|
|
107
|
+
partners(key).forEach(walk);
|
|
108
|
+
};
|
|
109
|
+
walk(start);
|
|
110
|
+
return order;
|
|
111
|
+
}
|
|
112
|
+
/** Longest path down from the founders, then founders settle onto the row above their children. */
|
|
113
|
+
static #assignGenerations(families, of) {
|
|
114
|
+
const edges = families.flatMap((family) => family.unions.filter((union) => union.members.every((member) => family.members.includes(member))).flatMap((union) => union.children.map((child) => ({
|
|
115
|
+
from: family,
|
|
116
|
+
to: of.get(child)
|
|
117
|
+
})))).filter(({ from, to }) => from !== to);
|
|
118
|
+
for (let pass = 0; pass <= families.length; pass++) {
|
|
119
|
+
let moved = false;
|
|
120
|
+
for (const { from, to } of edges) if (to.generation <= from.generation) {
|
|
121
|
+
to.generation = from.generation + 1;
|
|
122
|
+
moved = true;
|
|
123
|
+
}
|
|
124
|
+
if (!moved) break;
|
|
125
|
+
}
|
|
126
|
+
for (let pass = 0; pass <= families.length; pass++) {
|
|
127
|
+
let moved = false;
|
|
128
|
+
for (const family of families) {
|
|
129
|
+
const below = edges.filter((edge) => edge.from === family);
|
|
130
|
+
const sibling = edges.filter((edge) => edge.to === family).flatMap(({ from }) => edges.filter((edge) => edge.from === from).map((edge) => edge.to.generation));
|
|
131
|
+
const target = below.length ? Math.min(...below.map((edge) => edge.to.generation)) - 1 : sibling.length ? Math.max(...sibling) : family.generation;
|
|
132
|
+
if (target > family.generation) {
|
|
133
|
+
family.generation = target;
|
|
134
|
+
moved = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!moved) break;
|
|
138
|
+
}
|
|
139
|
+
const used = [...new Set(families.map((family) => family.generation))].sort((a, b) => a - b);
|
|
140
|
+
for (const family of families) family.generation = used.indexOf(family.generation);
|
|
141
|
+
}
|
|
142
|
+
#order(families) {
|
|
143
|
+
const rows = Array.from({ length: Math.max(0, ...families.map((family) => family.generation + 1)) }, () => []);
|
|
144
|
+
const parentsOf = (key) => {
|
|
145
|
+
const { father, mother } = this.#persons.get(key);
|
|
146
|
+
return father ?? mother;
|
|
147
|
+
};
|
|
148
|
+
const visit = (family, entry) => {
|
|
149
|
+
if (family.seen) return;
|
|
150
|
+
family.seen = true;
|
|
151
|
+
rows[family.generation].push(family);
|
|
152
|
+
if (family.members.length === 2) {
|
|
153
|
+
const [a, b] = family.members;
|
|
154
|
+
const both = parentsOf(a) && parentsOf(b);
|
|
155
|
+
const male = (key) => this.#persons.get(key).sex === PedigreeSex.Male;
|
|
156
|
+
if (both && entry ? b === entry : male(b) && !male(a)) family.members.reverse();
|
|
157
|
+
}
|
|
158
|
+
for (const member of [...family.members]) for (const union of family.unions) if (union.members.includes(member)) for (const child of union.children) visit(this.#families.get(child), child);
|
|
159
|
+
};
|
|
160
|
+
const founders = families.filter((family) => family.members.every((key) => !parentsOf(key)));
|
|
161
|
+
for (const family of [...founders, ...families]) visit(family, null);
|
|
162
|
+
return rows;
|
|
163
|
+
}
|
|
164
|
+
#x(key) {
|
|
165
|
+
const family = this.#families.get(key);
|
|
166
|
+
return family.left + family.members.indexOf(key);
|
|
167
|
+
}
|
|
168
|
+
#place(rows) {
|
|
169
|
+
for (const row of rows) PedigreeLayout.#solve(row, () => null);
|
|
170
|
+
const down = (family) => mean(family.members.flatMap((key, i) => {
|
|
171
|
+
const parents = [...this.#unions.values()].find((union) => union.children.includes(key));
|
|
172
|
+
return parents ? [mean(parents.members, (member) => this.#x(member)) - i] : [];
|
|
173
|
+
}));
|
|
174
|
+
const up = (family) => mean(family.unions.flatMap((union) => {
|
|
175
|
+
if (!union.children.length || !union.members.every((member) => family.members.includes(member))) return [];
|
|
176
|
+
return [mean(union.children, (child) => this.#x(child)) - mean(union.members, (member) => family.members.indexOf(member))];
|
|
177
|
+
}));
|
|
178
|
+
for (let sweep = 0; sweep < SWEEPS; sweep++) {
|
|
179
|
+
for (const row of rows) PedigreeLayout.#solve(row, down);
|
|
180
|
+
for (const row of [...rows].reverse()) PedigreeLayout.#solve(row, up);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** Pool-adjacent-violators: the closest positions to `wanted`, in least squares, that keep the row's order and spacing. */
|
|
184
|
+
static #solve(row, wanted) {
|
|
185
|
+
const offsets = [], blocks = [];
|
|
186
|
+
let offset = 0;
|
|
187
|
+
for (const family of row) {
|
|
188
|
+
const want = wanted(family);
|
|
189
|
+
offsets.push(offset);
|
|
190
|
+
blocks.push({
|
|
191
|
+
value: (want ?? family.left) - offset,
|
|
192
|
+
weight: want == null ? FAINT : 1,
|
|
193
|
+
size: 1
|
|
194
|
+
});
|
|
195
|
+
offset += family.members.length;
|
|
196
|
+
while (blocks.length > 1 && blocks.at(-2).value > blocks.at(-1).value) {
|
|
197
|
+
const top = blocks.pop(), under = blocks.at(-1), weight = under.weight + top.weight;
|
|
198
|
+
under.value = (under.value * under.weight + top.value * top.weight) / weight;
|
|
199
|
+
under.weight = weight;
|
|
200
|
+
under.size += top.size;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
let at = 0;
|
|
204
|
+
for (const block of blocks) for (let i = 0; i < block.size; i++, at++) row[at].left = block.value + offsets[at];
|
|
205
|
+
}
|
|
206
|
+
/** Everyone this person descends from. A loop in bad data simply stops the walk. */
|
|
207
|
+
#ancestry(key) {
|
|
208
|
+
const known = this.#ancestors.get(key);
|
|
209
|
+
if (known) return known;
|
|
210
|
+
const found = /* @__PURE__ */ new Set();
|
|
211
|
+
this.#ancestors.set(key, found);
|
|
212
|
+
const { father, mother } = this.#persons.get(key);
|
|
213
|
+
for (const parent of [father, mother]) {
|
|
214
|
+
if (!parent) continue;
|
|
215
|
+
found.add(parent);
|
|
216
|
+
for (const ancestor of this.#ancestry(parent)) found.add(ancestor);
|
|
217
|
+
}
|
|
218
|
+
return found;
|
|
219
|
+
}
|
|
220
|
+
#related(a, b) {
|
|
221
|
+
const above = this.#ancestry(a), other = this.#ancestry(b);
|
|
222
|
+
return above.has(b) || other.has(a) || [...above].some((ancestor) => other.has(ancestor));
|
|
223
|
+
}
|
|
224
|
+
#couple(union, placed) {
|
|
225
|
+
const [left, right] = union.members.map((key) => placed.get(key)).sort((a, b) => a.x - b.x);
|
|
226
|
+
return {
|
|
227
|
+
key: union.key,
|
|
228
|
+
left,
|
|
229
|
+
right,
|
|
230
|
+
adjacent: right.x - left.x <= 1.001,
|
|
231
|
+
consanguineous: union.flagged || this.#related(union.members[0], union.members[1])
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
#sibships(unions, placed) {
|
|
235
|
+
const drawn = unions.filter((union) => union.children.length).map((union) => {
|
|
236
|
+
const parents = union.members.map((key) => placed.get(key)), children = union.children.map((key) => placed.get(key));
|
|
237
|
+
const far = Math.max(...parents.map((parent) => parent.x)), near = Math.min(...parents.map((parent) => parent.x));
|
|
238
|
+
const drop = far - near > 1.001 ? far - .5 : (far + near) / 2, spots = [drop, ...children.map((child) => child.x)];
|
|
239
|
+
return {
|
|
240
|
+
key: union.key,
|
|
241
|
+
parents,
|
|
242
|
+
children,
|
|
243
|
+
drop,
|
|
244
|
+
from: Math.min(...spots),
|
|
245
|
+
to: Math.max(...spots),
|
|
246
|
+
generation: Math.max(0, Math.min(...children.map((child) => child.generation)) - 1),
|
|
247
|
+
lane: 0
|
|
248
|
+
};
|
|
249
|
+
}).sort((a, b) => a.from - b.from);
|
|
250
|
+
const ends = this.lanes.map(() => []);
|
|
251
|
+
for (const sibship of drawn) {
|
|
252
|
+
const row = ends[sibship.generation];
|
|
253
|
+
const lane = row.findIndex((end) => end + LANE_GAP <= sibship.from);
|
|
254
|
+
sibship.lane = lane < 0 ? row.length : lane;
|
|
255
|
+
row[sibship.lane] = sibship.to;
|
|
256
|
+
this.lanes[sibship.generation] = row.length;
|
|
257
|
+
}
|
|
258
|
+
return drawn;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
export { PedigreeLayout };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { PedigreeSex } from "./pedigreeEnums.js";
|
|
2
|
+
// src/charts/lifescience/PedigreeRows.ts
|
|
3
|
+
var MALE = /* @__PURE__ */ new Set([
|
|
4
|
+
"m",
|
|
5
|
+
"male",
|
|
6
|
+
"man",
|
|
7
|
+
"boy",
|
|
8
|
+
"1"
|
|
9
|
+
]);
|
|
10
|
+
var FEMALE = /* @__PURE__ */ new Set([
|
|
11
|
+
"f",
|
|
12
|
+
"female",
|
|
13
|
+
"woman",
|
|
14
|
+
"girl",
|
|
15
|
+
"2"
|
|
16
|
+
]);
|
|
17
|
+
var YES = /* @__PURE__ */ new Set([
|
|
18
|
+
"true",
|
|
19
|
+
"yes",
|
|
20
|
+
"y",
|
|
21
|
+
"1",
|
|
22
|
+
"x",
|
|
23
|
+
"affected",
|
|
24
|
+
"carrier",
|
|
25
|
+
"deceased",
|
|
26
|
+
"dead",
|
|
27
|
+
"proband"
|
|
28
|
+
]);
|
|
29
|
+
var text = (cell) => String(cell ?? "").trim();
|
|
30
|
+
/**
|
|
31
|
+
* Reads pedigree rows. Sex accepts words or PED codes (1 male, 2 female). Status columns are booleans,
|
|
32
|
+
* so map PED affection codes first: there, 1 means unaffected. Links to an absent person are dropped.
|
|
33
|
+
*/
|
|
34
|
+
var PedigreeRows = class PedigreeRows {
|
|
35
|
+
static parse(data, columns) {
|
|
36
|
+
const rows = (Array.isArray(data) ? data : []).filter((row) => row !== null && typeof row === "object");
|
|
37
|
+
const known = new Set(rows.map((row) => text(row[columns.id])).filter(Boolean));
|
|
38
|
+
const link = (cell, self) => {
|
|
39
|
+
const id = text(cell);
|
|
40
|
+
return id !== self && known.has(id) ? id : null;
|
|
41
|
+
};
|
|
42
|
+
const flag = (cell) => YES.has(text(cell).toLowerCase());
|
|
43
|
+
const seen = /* @__PURE__ */ new Set();
|
|
44
|
+
return rows.flatMap((row) => {
|
|
45
|
+
const key = text(row[columns.id]);
|
|
46
|
+
if (!key || seen.has(key)) return [];
|
|
47
|
+
seen.add(key);
|
|
48
|
+
const father = link(row[columns.father], key), mother = link(row[columns.mother], key);
|
|
49
|
+
return [{
|
|
50
|
+
key,
|
|
51
|
+
row,
|
|
52
|
+
name: text(row[columns.label]) || key,
|
|
53
|
+
sex: PedigreeRows.#sex(row[columns.sex]),
|
|
54
|
+
father,
|
|
55
|
+
mother: mother === father ? null : mother,
|
|
56
|
+
partner: link(row[columns.partner], key),
|
|
57
|
+
affected: flag(row[columns.affected]),
|
|
58
|
+
carrier: flag(row[columns.carrier]),
|
|
59
|
+
deceased: flag(row[columns.deceased]),
|
|
60
|
+
proband: flag(row[columns.proband]),
|
|
61
|
+
consanguineous: flag(row[columns.consanguineous])
|
|
62
|
+
}];
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
static #sex(cell) {
|
|
66
|
+
const code = text(cell).toLowerCase();
|
|
67
|
+
return MALE.has(code) ? PedigreeSex.Male : FEMALE.has(code) ? PedigreeSex.Female : PedigreeSex.Unknown;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
export { PedigreeRows };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { randomLcg } from "d3";
|
|
2
|
+
// src/charts/lifescience/PedigreeSamples.ts
|
|
3
|
+
/** [id, sex, father, mother] in birth order, four generations with a first-cousin union at 12 and 13. */
|
|
4
|
+
var FAMILY = [
|
|
5
|
+
[
|
|
6
|
+
1,
|
|
7
|
+
"M",
|
|
8
|
+
null,
|
|
9
|
+
null
|
|
10
|
+
],
|
|
11
|
+
[
|
|
12
|
+
2,
|
|
13
|
+
"F",
|
|
14
|
+
null,
|
|
15
|
+
null
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
3,
|
|
19
|
+
"M",
|
|
20
|
+
null,
|
|
21
|
+
null
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
4,
|
|
25
|
+
"F",
|
|
26
|
+
1,
|
|
27
|
+
2
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
5,
|
|
31
|
+
"M",
|
|
32
|
+
1,
|
|
33
|
+
2
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
6,
|
|
37
|
+
"F",
|
|
38
|
+
null,
|
|
39
|
+
null
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
7,
|
|
43
|
+
"F",
|
|
44
|
+
1,
|
|
45
|
+
2
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
8,
|
|
49
|
+
"U",
|
|
50
|
+
1,
|
|
51
|
+
2
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
9,
|
|
55
|
+
"F",
|
|
56
|
+
3,
|
|
57
|
+
4
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
10,
|
|
61
|
+
"M",
|
|
62
|
+
3,
|
|
63
|
+
4
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
11,
|
|
67
|
+
"M",
|
|
68
|
+
3,
|
|
69
|
+
4
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
12,
|
|
73
|
+
"M",
|
|
74
|
+
3,
|
|
75
|
+
4
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
13,
|
|
79
|
+
"F",
|
|
80
|
+
5,
|
|
81
|
+
6
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
14,
|
|
85
|
+
"F",
|
|
86
|
+
5,
|
|
87
|
+
6
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
15,
|
|
91
|
+
"M",
|
|
92
|
+
5,
|
|
93
|
+
6
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
16,
|
|
97
|
+
"M",
|
|
98
|
+
12,
|
|
99
|
+
13
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
17,
|
|
103
|
+
"F",
|
|
104
|
+
12,
|
|
105
|
+
13
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
18,
|
|
109
|
+
"M",
|
|
110
|
+
12,
|
|
111
|
+
13
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
19,
|
|
115
|
+
"F",
|
|
116
|
+
12,
|
|
117
|
+
13
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
20,
|
|
121
|
+
"M",
|
|
122
|
+
12,
|
|
123
|
+
13
|
|
124
|
+
]
|
|
125
|
+
];
|
|
126
|
+
var COUSINS = [12, 13];
|
|
127
|
+
var LAST_CHILDREN = 16;
|
|
128
|
+
var TRIES = 2e3;
|
|
129
|
+
/** A synthetic family, not a real one: autosomal recessive inheritance simulated by Mendel's first law. */
|
|
130
|
+
var PedigreeSamples = class {
|
|
131
|
+
/** Copies of the allele per person, redrawn until the cousins are both carriers with an affected child, so every symbol appears. */
|
|
132
|
+
static #genotypes(seed) {
|
|
133
|
+
const random = randomLcg(seed * .6180339887 % 1);
|
|
134
|
+
const gamete = (copies) => copies === 2 ? 1 : copies === 1 && random() < .5 ? 1 : 0;
|
|
135
|
+
let held = /* @__PURE__ */ new Map();
|
|
136
|
+
for (let attempt = 0; attempt < TRIES; attempt++) {
|
|
137
|
+
held = /* @__PURE__ */ new Map();
|
|
138
|
+
for (const [id, , father, mother] of FAMILY) if (id === 1) held.set(id, 1);
|
|
139
|
+
else held.set(id, father === null || mother === null ? 0 : gamete(held.get(father)) + gamete(held.get(mother)));
|
|
140
|
+
const children = FAMILY.filter(([id]) => id >= LAST_CHILDREN).map(([id]) => held.get(id));
|
|
141
|
+
if (COUSINS.every((id) => held.get(id) === 1) && children.includes(2) && children.includes(1)) return held;
|
|
142
|
+
}
|
|
143
|
+
return held;
|
|
144
|
+
}
|
|
145
|
+
static recessive(seed) {
|
|
146
|
+
const copies = this.#genotypes(seed);
|
|
147
|
+
const proband = FAMILY.find(([id]) => id >= LAST_CHILDREN && copies.get(id) === 2)?.[0];
|
|
148
|
+
return FAMILY.map(([id, sex, father, mother]) => ({
|
|
149
|
+
id,
|
|
150
|
+
sex,
|
|
151
|
+
father,
|
|
152
|
+
mother,
|
|
153
|
+
label: `Syn-${id}`,
|
|
154
|
+
affected: +(copies.get(id) === 2),
|
|
155
|
+
carrier: +(copies.get(id) === 1),
|
|
156
|
+
deceased: +(id <= 3 || id === 7),
|
|
157
|
+
proband: +(id === proband)
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
export { PedigreeSamples };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PedigreeSex } from "./pedigreeEnums.js";
|
|
2
|
+
// src/charts/lifescience/PedigreeSymbol.ts
|
|
3
|
+
var DIAMOND = .62;
|
|
4
|
+
var f = (value) => +value.toFixed(2);
|
|
5
|
+
/** SVG paths for the pedigree symbols: square male, circle female, diamond sex unknown. `size` is the symbol's width. */
|
|
6
|
+
var PedigreeSymbol = class {
|
|
7
|
+
static outline(sex, x, y, size) {
|
|
8
|
+
const r = size / 2;
|
|
9
|
+
if (sex === PedigreeSex.Male) return `M${f(x - r)} ${f(y - r)}H${f(x + r)}V${f(y + r)}H${f(x - r)}Z`;
|
|
10
|
+
if (sex === PedigreeSex.Female) return `M${f(x - r)} ${f(y)}A${f(r)} ${f(r)} 0 1 1 ${f(x + r)} ${f(y)}A${f(r)} ${f(r)} 0 1 1 ${f(x - r)} ${f(y)}Z`;
|
|
11
|
+
const d = size * DIAMOND;
|
|
12
|
+
return `M${f(x)} ${f(y - d)}L${f(x + d)} ${f(y)}L${f(x)} ${f(y + d)}L${f(x - d)} ${f(y)}Z`;
|
|
13
|
+
}
|
|
14
|
+
/** The left half, which shades a carrier. */
|
|
15
|
+
static leftHalf(sex, x, y, size) {
|
|
16
|
+
const r = size / 2;
|
|
17
|
+
if (sex === PedigreeSex.Male) return `M${f(x)} ${f(y - r)}H${f(x - r)}V${f(y + r)}H${f(x)}Z`;
|
|
18
|
+
if (sex === PedigreeSex.Female) return `M${f(x)} ${f(y - r)}A${f(r)} ${f(r)} 0 0 0 ${f(x)} ${f(y + r)}Z`;
|
|
19
|
+
const d = size * DIAMOND;
|
|
20
|
+
return `M${f(x)} ${f(y - d)}L${f(x - d)} ${f(y)}L${f(x)} ${f(y + d)}Z`;
|
|
21
|
+
}
|
|
22
|
+
/** Half the height the symbol spans, which differs for the diamond. */
|
|
23
|
+
static reach(sex, size) {
|
|
24
|
+
return sex === PedigreeSex.Unknown ? size * DIAMOND : size / 2;
|
|
25
|
+
}
|
|
26
|
+
/** Bottom-left to top-right, overhanging the symbol so it reads on a filled one too. */
|
|
27
|
+
static slash(x, y, size) {
|
|
28
|
+
const r = size / 2 + 4;
|
|
29
|
+
return {
|
|
30
|
+
x1: f(x - r),
|
|
31
|
+
y1: f(y + r),
|
|
32
|
+
x2: f(x + r),
|
|
33
|
+
y2: f(y - r)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Arrow from the lower left up to the symbol, as a shaft and a filled head. */
|
|
37
|
+
static arrow(x, y, size) {
|
|
38
|
+
const tip = size / 2 + 3, tail = tip + Math.max(11, size * .55), back = 6 * Math.SQRT1_2;
|
|
39
|
+
const tipX = x - tip, tipY = y + tip, baseX = tipX - back, baseY = tipY + back;
|
|
40
|
+
return {
|
|
41
|
+
shaft: `M${f(x - tail)} ${f(y + tail)}L${f(baseX)} ${f(baseY)}`,
|
|
42
|
+
head: `M${f(tipX)} ${f(tipY)}L${f(baseX - back * .6)} ${f(baseY - back * .6)}L${f(baseX + back * .6)} ${f(baseY + back * .6)}Z`,
|
|
43
|
+
tail: [f(x - tail), f(y + tail)]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export { PedigreeSymbol };
|