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