xref: /webtrees/app/Services/ChartService.php (revision 7413816e6dd2d50e569034fb804f3dce7471bb94)
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