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