@@ -568,16 +568,26 @@ export function VerticalFamilyView({
568568 // Get all nodes at this generation that have parent connectors
569569 const genNodes = nodesWithParentsByGen . get ( node . generation ) || [ node ] ;
570570
571- // Sort nodes by distance from center to get unique rank for each
572- // This ensures even distribution with fan-out pattern (outside = lower)
573- const sortedByOutwardness = [ ...genNodes ] . sort ( ( a , b ) => Math . abs ( a . x ) - Math . abs ( b . x ) ) ;
574- const outwardnessRank = sortedByOutwardness . indexOf ( node ) ;
575-
576- // Distribute lines evenly across the full usable space
577- // Rank 0 (closest to center) near parents, highest rank (most outward) near child
578- const subTrackOffset = genNodes . length > 1
579- ? ( outwardnessRank / ( genNodes . length - 1 ) ) * usableSpace
580- : usableSpace / 2 ;
571+ // Separate nodes into left (paternal) and right (maternal) sides for mirrored fan-out
572+ // Each side fans out independently: outside = lower line, inside = higher line
573+ const leftNodes = genNodes . filter ( n => n . x < 0 ) . sort ( ( a , b ) => a . x - b . x ) ; // most negative (outer) first
574+ const rightNodes = genNodes . filter ( n => n . x >= 0 ) . sort ( ( a , b ) => b . x - a . x ) ; // most positive (outer) first
575+
576+ // Determine which side this node is on and its rank within that side
577+ const isLeft = node . x < 0 ;
578+ const sideNodes = isLeft ? leftNodes : rightNodes ;
579+ const sideRank = sideNodes . indexOf ( node ) ;
580+ const sideCount = sideNodes . length ;
581+
582+ // Use the max count from either side to ensure symmetric spacing
583+ const maxSideCount = Math . max ( leftNodes . length , rightNodes . length , 1 ) ;
584+
585+ // Distribute lines: rank 0 (outermost) gets lowest line, highest rank (innermost) gets highest line
586+ // Invert the rank so outer (rank 0) maps to high offset (low line)
587+ const normalizedRank = sideCount > 1
588+ ? ( sideCount - 1 - sideRank ) / ( maxSideCount - 1 )
589+ : 0.5 ; // Center if only one node on this side
590+ const subTrackOffset = normalizedRank * usableSpace ;
581591 const midY = parentBottomY + trackPadding + subTrackOffset ;
582592
583593 if ( father && mother ) {
0 commit comments