(int)$gen[$b]) ?: strcmp($rankKey($a), $rankKey($b)) ?: ($a <=> $b); }); $order = array_merge($order, $rest); $mapIds = static function (array $ids) use ($onTree): array { $out = []; foreach ($ids as $x) { $x = (int)$x; if (isset($onTree[$x])) { $out[] = (string)$x; } } return array_values(array_unique($out)); }; $datum = []; foreach ($order as $pid) { if (!isset($people[$pid]) || !is_array($people[$pid])) { continue; } $p = $people[$pid]; $parents = $mapIds($links['parents'][$pid] ?? []); $spouses = $mapIds($links['spouses'][$pid] ?? []); $kids = $mapIds($links['children'][$pid] ?? []); // 子女按长幼排序:库按 rels.children 的数组顺序摆左右 $kidsInt = array_map('intval', $kids); usort($kidsInt, static fn(int $a, int $b): int => strcmp($rankKey($a), $rankKey($b)) ?: ($a <=> $b)); $kids = array_map('strval', $kidsInt); $g = strtolower((string)($p['gender'] ?? '')); $gender = $g === 'm' ? 'M' : ($g === 'f' ? 'F' : 'U'); $datum[] = [ 'id' => (string)$pid, 'data' => [ 'gender' => $gender, 'name' => \App\Nickname::display((int)$meId, $p) ?: (string)($p['name'] ?? ''), 'label' => \App\Kinship::label($graph, (int)$meId, $pid, $people), 'avatar' => avatar_url($p['avatar'] ?? null), // 「我」在图上高亮;中心那个人另有标记($focusId) 'me' => $pid === (int)$focusId, 'is_me_viewer' => $pid === (int)$meId, 'ph' => (int)($p['is_placeholder'] ?? 0) === 1, ], 'rels' => [ 'parents' => $parents, 'spouses' => $spouses, 'children' => $kids, ], ]; } // v24 第 12 条:本图上「有姻亲但没画出来」的人 —— 卡片上给个入口 $branchOuts = \App\Kinship::branchOuts($graph, $people, $onTree); foreach ($datum as &$dn) { $did = (int)$dn['id']; if (isset($branchOuts[$did])) { $dn['data']['branch'] = $branchOuts[$did]['name']; } } unset($dn); $canChart = count($datum) > 1; } $meName = (string)($people[$meId]['name'] ?? '自己'); $datumJson = json_encode( $datum ?? [], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>