Initial commit: webtrees full diagram chart module

Interactive SVG family tree visualization using ELK (Sugiyama) for
layout and D3 for rendering. Shows ancestors, descendants, and siblings
in a single diagram with orthogonal bus-line connectors.

Features:
- Bidirectional tree traversal (ancestors + descendants + siblings)
- Generation-aligned layout with post-processing Y-snap
- Person cards with photos, names, dates, and hover bio cards
- "More ancestors" indicator for persons with hidden parents
- Pan/zoom navigation
- Docker dev environment
This commit is contained in:
2026-03-14 18:51:19 +01:00
commit 273e398431
38 changed files with 5232 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
/**
* Family connector utilities.
*
* Draws special connectors between spouses and from couples to children.
*/
/**
* Draw a family connector: horizontal line between spouses,
* then vertical drop to children.
*
* @param {d3.Selection} group
* @param {object} parent1 - First parent position {x, y}
* @param {object} parent2 - Second parent position {x, y}
* @param {Array} children - Child positions [{x, y}, ...]
* @param {Configuration} config
*/
export function drawFamilyConnector(group, parent1, parent2, children, config) {
const halfHeight = config.cardHeight / 2;
// Horizontal line between spouses
if (parent2) {
group
.append("line")
.attr("class", "link spouse-link")
.attr("x1", parent1.x)
.attr("y1", parent1.y)
.attr("x2", parent2.x)
.attr("y2", parent2.y)
.attr("stroke", "#d4a87b")
.attr("stroke-width", 2);
}
if (children.length === 0) return;
// Midpoint between parents (or just parent1 if single parent)
const coupleX = parent2 ? (parent1.x + parent2.x) / 2 : parent1.x;
const coupleY = parent1.y + halfHeight;
// Vertical drop from couple midpoint
const childrenY = children[0].y - halfHeight;
const midY = (coupleY + childrenY) / 2;
group
.append("line")
.attr("class", "link descendant-link")
.attr("x1", coupleX)
.attr("y1", coupleY)
.attr("x2", coupleX)
.attr("y2", midY);
if (children.length === 1) {
// Single child — straight line down
group
.append("line")
.attr("class", "link descendant-link")
.attr("x1", coupleX)
.attr("y1", midY)
.attr("x2", children[0].x)
.attr("y2", childrenY);
} else {
// Multiple children — horizontal rail
const minX = Math.min(...children.map((c) => c.x));
const maxX = Math.max(...children.map((c) => c.x));
group
.append("line")
.attr("class", "link descendant-link")
.attr("x1", minX)
.attr("y1", midY)
.attr("x2", maxX)
.attr("y2", midY);
for (const child of children) {
group
.append("line")
.attr("class", "link descendant-link")
.attr("x1", child.x)
.attr("y1", midY)
.attr("x2", child.x)
.attr("y2", childrenY);
}
}
}