diff --git a/GanjooRazor/Pages/FamilyTreeWindow.cshtml b/GanjooRazor/Pages/FamilyTreeWindow.cshtml index 8cfdaae8..612c3f62 100644 --- a/GanjooRazor/Pages/FamilyTreeWindow.cshtml +++ b/GanjooRazor/Pages/FamilyTreeWindow.cshtml @@ -35,6 +35,13 @@ else spouse dashing (finer dots, lighter/grayer) and gets no heart icon, so it doesn't read as a marriage or as a confirmed shared-parent claim */ .ft-line-sibling { stroke-dasharray: 1,3; stroke: #a0a0a0; } + /* real recorded parent (e.g. a mother) who could not be placed as an + attached spouse-box because she has her own independent position in + the tree (her own recorded ancestry). Drawn as an extra connector to + wherever she actually ended up - distinct dash/color from both the + spouse line (marriage) and the sibling line (unspecified shared + parent), since this one *is* a confirmed parent-child relation. */ + .ft-line-second-parent { stroke-dasharray: 6,2; stroke: #a0455c; stroke-width: 1.25; } .ft-tooltip-panel { position: absolute; top: 10px; left: 10px; max-width: 300px; background: #fffdf7; border: 1px solid #8b6b4a; border-radius: 8px; diff --git a/GanjooRazor/wwwroot/js/familytree.js b/GanjooRazor/wwwroot/js/familytree.js index 00f46167..a0afa4b1 100644 --- a/GanjooRazor/wwwroot/js/familytree.js +++ b/GanjooRazor/wwwroot/js/familytree.js @@ -133,8 +133,23 @@ return false; } + // A secondary parent (e.g. the mother, when the father was picked as the primary/spine + // parent above) who is themselves a root with no children of their own gets drawn as an + // attached spouse-box right next to the primary parent. But when the secondary parent has + // their OWN recorded ancestors (not uncommon - e.g. two fully-documented royal lines + // marrying), tryAttach refuses to move them next to someone else's position, since they + // already have a real, independent one of their own elsewhere in the tree. That used to + // mean the relation was just dropped - the child only ever showed a line to the primary + // parent. Track those cases here instead, so an extra connector can still be drawn straight + // to wherever that parent actually ended up, once every node has a position (see + // unattachedSecondaryParents below and its use in renderFamilyTree). + var unattachedSecondaryParents = []; // [{parentId, childId}] Object.keys(secondaryParentOf).forEach(function (childId) { - tryAttach(primaryParentOf[childId], secondaryParentOf[childId]); + var parentId = secondaryParentOf[childId]; + var primaryId = primaryParentOf[childId]; + if (!tryAttach(primaryId, parentId)) { + unattachedSecondaryParents.push({ parentId: parentId, childId: parseInt(childId, 10) }); + } }); Object.keys(spouseOf).forEach(function (a) { spouseOf[a].forEach(function (b) { @@ -208,6 +223,7 @@ secondaryParentOf: secondaryParentOf, attachedList: attachedList, attachedAsSibling: attachedAsSibling, + unattachedSecondaryParents: unattachedSecondaryParents, topLevelRoots: topLevelRoots, nodeX: nodeX, nodeDepth: nodeDepth @@ -347,6 +363,22 @@ }); }); + // extra connector for a recorded second parent who couldn't be drawn attached next to the + // primary parent (see unattachedSecondaryParents in buildLayout) - drawn last, once every + // box (both plain tree nodes and attached ones) has a real position in drawnPos, straight + // from wherever that parent actually ended up to the child. Styled distinctly from the + // primary parent-child line (solid) and from spouse/sibling attachments (both dashed) so it + // doesn't get misread as either - it's a real, independently-documented parent, just not the + // one the chart chose as this child's spine. + layoutInfo.unattachedSecondaryParents.forEach(function (edge) { + var pp = drawnPos[edge.parentId], cp = drawnPos[edge.childId]; + if (!pp || !cp) return; // shouldn't happen, but never let a bad edge break the whole chart + var parentH = boxSizeFor(layoutInfo.persons[edge.parentId], BOX_W, BOX_H).h; + var childH = boxSizeFor(layoutInfo.persons[edge.childId], BOX_W, BOX_H).h; + var dy = cp.y > pp.y ? 1 : -1; // usually the child is drawn lower, but not guaranteed + line(pp.x, pp.y + dy * parentH / 2, cp.x, cp.y - dy * childH / 2, 'ft-line ft-line-second-parent'); + }); + svg.setAttribute('viewBox', '0 0 ' + (maxX + MARGIN) + ' ' + (maxY + MARGIN)); function showDetails(p) {