1aca28033SGreg Roach<?php 23976b470SGreg Roach 3aca28033SGreg Roach/** 4aca28033SGreg Roach * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 6aca28033SGreg Roach * This program is free software: you can redistribute it and/or modify 7aca28033SGreg Roach * it under the terms of the GNU General Public License as published by 8aca28033SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9aca28033SGreg Roach * (at your option) any later version. 10aca28033SGreg Roach * This program is distributed in the hope that it will be useful, 11aca28033SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12aca28033SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13aca28033SGreg Roach * GNU General Public License for more details. 14aca28033SGreg Roach * You should have received a copy of the GNU General Public License 1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 16aca28033SGreg Roach */ 17fcfa147eSGreg Roach 18aca28033SGreg Roachdeclare(strict_types=1); 19aca28033SGreg Roach 20aca28033SGreg Roachnamespace Fisharebest\Webtrees\Services; 21aca28033SGreg Roach 22aca28033SGreg Roachuse Fisharebest\Webtrees\Family; 23aca28033SGreg Roachuse Fisharebest\Webtrees\Individual; 24aca28033SGreg Roachuse Illuminate\Support\Collection; 25aca28033SGreg Roach 26aca28033SGreg Roach/** 27aca28033SGreg Roach * Find ancestors, descendants, cousins, etc for drawing charts. 28aca28033SGreg Roach */ 29aca28033SGreg Roachclass ChartService 30aca28033SGreg Roach{ 31aca28033SGreg Roach /** 32aca28033SGreg Roach * Find the ancestors of an individual, indexed by their Sosa-Stradonitz number. 33aca28033SGreg Roach * 34aca28033SGreg Roach * @param Individual $individual Start with this individual 35aca28033SGreg Roach * @param int $generations Fetch this number of generations 36aca28033SGreg Roach * 3736779af1SGreg Roach * @return Collection<int,Individual> 38aca28033SGreg Roach */ 39aca28033SGreg Roach public function sosaStradonitzAncestors(Individual $individual, int $generations): Collection 40aca28033SGreg Roach { 41aca28033SGreg Roach $ancestors = [1 => $individual]; 42aca28033SGreg Roach 43aca28033SGreg Roach $queue = [1]; 44aca28033SGreg Roach 45aca28033SGreg Roach $max = 2 ** ($generations - 1); 46aca28033SGreg Roach 4754c1ab5eSGreg Roach while ($queue !== []) { 48aca28033SGreg Roach $sosa_stradonitz_number = array_shift($queue); 49aca28033SGreg Roach 50aca28033SGreg Roach if ($sosa_stradonitz_number >= $max) { 51aca28033SGreg Roach break; 52aca28033SGreg Roach } 53aca28033SGreg Roach 541afbbc50SGreg Roach $family = $ancestors[$sosa_stradonitz_number]->childFamilies()->first(); 55aca28033SGreg Roach 56aca28033SGreg Roach if ($family instanceof Family) { 5739ca88baSGreg Roach if ($family->husband() instanceof Individual) { 5839ca88baSGreg Roach $ancestors[$sosa_stradonitz_number * 2] = $family->husband(); 59aca28033SGreg Roach $queue[] = $sosa_stradonitz_number * 2; 60aca28033SGreg Roach } 61aca28033SGreg Roach 6239ca88baSGreg Roach if ($family->wife() instanceof Individual) { 6339ca88baSGreg Roach $ancestors[$sosa_stradonitz_number * 2 + 1] = $family->wife(); 64aca28033SGreg Roach $queue[] = $sosa_stradonitz_number * 2 + 1; 65aca28033SGreg Roach } 66aca28033SGreg Roach } 67aca28033SGreg Roach } 68aca28033SGreg Roach 69aca28033SGreg Roach return new Collection($ancestors); 70aca28033SGreg Roach } 71b8e79c89SGreg Roach 72b8e79c89SGreg Roach /** 73b8e79c89SGreg Roach * Find the descendants of an individual. 74b8e79c89SGreg Roach * 75b8e79c89SGreg Roach * @param Individual $individual Start with this individual 76b8e79c89SGreg Roach * @param int $generations Fetch this number of generations 77b8e79c89SGreg Roach * 7836779af1SGreg Roach * @return Collection<int,Individual> 79b8e79c89SGreg Roach */ 80b8e79c89SGreg Roach public function descendants(Individual $individual, int $generations): Collection 81b8e79c89SGreg Roach { 829bbda1e6SGreg Roach $descendants = new Collection([$individual->xref() => $individual]); 83b8e79c89SGreg Roach 84b8e79c89SGreg Roach if ($generations > 0) { 8539ca88baSGreg Roach foreach ($individual->spouseFamilies() as $family) { 8639ca88baSGreg Roach foreach ($family->children() as $child) { 879bbda1e6SGreg Roach if (!$descendants->has($child->xref())) { 88b8e79c89SGreg Roach $descendants = $descendants->merge($this->descendants($child, $generations - 1)); 89b8e79c89SGreg Roach } 90b8e79c89SGreg Roach } 91b8e79c89SGreg Roach } 929bbda1e6SGreg Roach } 93b8e79c89SGreg Roach 94*7413816eSGreg Roach return $descendants->values(); 95b8e79c89SGreg Roach } 96b8e79c89SGreg Roach 97b8e79c89SGreg Roach /** 98b8e79c89SGreg Roach * Find the descendants of an individual. 99b8e79c89SGreg Roach * 100b8e79c89SGreg Roach * @param Individual $individual Start with this individual 101b8e79c89SGreg Roach * @param int $generations Fetch this number of generations 102b8e79c89SGreg Roach * 1035fef3bfaSGreg Roach * @return Collection<int,Family> 104b8e79c89SGreg Roach */ 105b8e79c89SGreg Roach public function descendantFamilies(Individual $individual, int $generations): Collection 106b8e79c89SGreg Roach { 107*7413816eSGreg Roach $descendants = $individual->spouseFamilies(); 108b8e79c89SGreg Roach 109b8e79c89SGreg Roach if ($generations > 0) { 110b8e79c89SGreg Roach foreach ($descendants as $family) { 11139ca88baSGreg Roach foreach ($family->children() as $child) { 112b8e79c89SGreg Roach $descendants = $descendants->merge($this->descendantFamilies($child, $generations - 1)); 113b8e79c89SGreg Roach } 114b8e79c89SGreg Roach } 115b8e79c89SGreg Roach } 116b8e79c89SGreg Roach 117b8e79c89SGreg Roach return $descendants; 118b8e79c89SGreg Roach } 119aca28033SGreg Roach} 120