Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
47 / 47 |
|
100.00% |
6 / 6 |
CRAP | |
100.00% |
1 / 1 |
Path | |
100.00% |
47 / 47 |
|
100.00% |
6 / 6 |
18 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addNode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getLength | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
getPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
expandPath | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
4 | |||
interpolate | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
9 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Algorithm\PathFinding |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | * |
13 | * Extended based on: |
14 | * MIT License |
15 | * (c) 2011-2012 Xueqiao Xu <xueqiaoxu@gmail.com> |
16 | * (c) PathFinding.js |
17 | */ |
18 | declare(strict_types=1); |
19 | |
20 | namespace phpOMS\Algorithm\PathFinding; |
21 | |
22 | /** |
23 | * Path in grids. |
24 | * |
25 | * @package phpOMS\Algorithm\PathFinding |
26 | * @license OMS License 2.0 |
27 | * @link https://jingga.app |
28 | * @since 1.0.0 |
29 | */ |
30 | class Path |
31 | { |
32 | /** |
33 | * Nodes in the path |
34 | * |
35 | * @var Node[] |
36 | * @since 1.0.0 |
37 | */ |
38 | public array $nodes = []; |
39 | |
40 | /** |
41 | * Grid this path belongs to |
42 | * |
43 | * @var Grid |
44 | * @since 1.0.0 |
45 | */ |
46 | private Grid $grid; |
47 | |
48 | /** |
49 | * Nodes in the path |
50 | * |
51 | * @var Node[] |
52 | * @since 1.0.0 |
53 | */ |
54 | private array $expandedNodes = []; |
55 | |
56 | /** |
57 | * Cosntructor. |
58 | * |
59 | * @param Grid $grid Grid this path belongs to |
60 | * |
61 | * @since 1.0.0 |
62 | */ |
63 | public function __construct(Grid $grid) |
64 | { |
65 | $this->grid = $grid; |
66 | } |
67 | |
68 | /** |
69 | * Add node to the path |
70 | * |
71 | * @param Node $node Node |
72 | * |
73 | * @return void |
74 | * |
75 | * @since 1.0.0 |
76 | */ |
77 | public function addNode(Node $node) : void |
78 | { |
79 | $this->nodes[] = $node; |
80 | } |
81 | |
82 | /** |
83 | * Get the path length (euclidean) |
84 | * |
85 | * @return float |
86 | * |
87 | * @since 1.0.0 |
88 | */ |
89 | public function getLength() : float |
90 | { |
91 | $n = \count($this->nodes); |
92 | |
93 | $dist = 0.0; |
94 | |
95 | for ($i = 1; $i < $n; ++$i) { |
96 | $dx = $this->nodes[$i - 1]->getX() - $this->nodes[$i]->getX(); |
97 | $dy = $this->nodes[$i - 1]->getY() - $this->nodes[$i]->getY(); |
98 | |
99 | $dist += \sqrt($dx * $dx + $dy * $dy); |
100 | } |
101 | |
102 | return $dist; |
103 | } |
104 | |
105 | /** |
106 | * Get the incomplete node path |
107 | * |
108 | * @return Node[] |
109 | * |
110 | * @since 1.0.0 |
111 | */ |
112 | public function getPath() : array |
113 | { |
114 | return $this->nodes; |
115 | } |
116 | |
117 | /** |
118 | * Get the complete node path |
119 | * |
120 | * The path may only contain the jump points or pivot points. |
121 | * In order to get every node it needs to be expanded. |
122 | * |
123 | * @return Node[] |
124 | * |
125 | * @since 1.0.0 |
126 | */ |
127 | public function expandPath() : array |
128 | { |
129 | if (empty($this->expandedNodes)) { |
130 | //$reverse = \array_reverse($this->nodes); |
131 | $reverse = $this->nodes; |
132 | $length = \count($reverse); |
133 | |
134 | if ($length < 2) { |
135 | return $reverse; |
136 | } |
137 | |
138 | $expanded = []; |
139 | for ($i = 0; $i < $length - 1; ++$i) { |
140 | $coord0 = $reverse[$i]; |
141 | $coord1 = $reverse[$i + 1]; |
142 | |
143 | $interpolated = $this->interpolate($coord0, $coord1); |
144 | $expanded = \array_merge($expanded, $interpolated); |
145 | } |
146 | |
147 | $expanded[] = $reverse[$length - 1]; |
148 | $this->expandedNodes = $expanded; |
149 | } |
150 | |
151 | return $this->expandedNodes; |
152 | } |
153 | |
154 | /** |
155 | * Find nodes in bettween two nodes. |
156 | * |
157 | * The path may only contain the jump points or pivot points. |
158 | * In order to get every node it needs to be expanded. |
159 | * |
160 | * @param Node $node1 Node |
161 | * @param Node $node2 Node |
162 | * |
163 | * @return Node[] |
164 | * |
165 | * @since 1.0.0 |
166 | */ |
167 | private function interpolate(Node $node1, Node $node2) : array |
168 | { |
169 | $dx = \abs($node2->getX() - $node1->getX()); |
170 | $dy = \abs($node2->getY() - $node1->getY()); |
171 | |
172 | $sx = ($node1->getX() < $node2->getX()) ? 1 : -1; |
173 | $sy = ($node1->getY() < $node2->getY()) ? 1 : -1; |
174 | |
175 | $node = $node1; |
176 | $err = $dx - $dy; |
177 | |
178 | $x0 = $node->getX(); |
179 | $y0 = $node->getY(); |
180 | |
181 | $line = []; |
182 | while (true) { |
183 | if ($node->getX() === $node2->getX() && $node->getY() === $node2->getY()) { |
184 | break; |
185 | } |
186 | |
187 | $line[] = $node; |
188 | |
189 | $e2 = 2 * $err; |
190 | |
191 | if ($e2 > -$dy) { |
192 | $err -= $dy; |
193 | $x0 += $sx; |
194 | } |
195 | |
196 | if ($e2 < $dx) { |
197 | $err += $dx; |
198 | $y0 += $sy; |
199 | } |
200 | |
201 | $node = $this->grid->getNode($x0, $y0); |
202 | |
203 | if ($node === null) { |
204 | break; // @codeCoverageIgnore |
205 | } |
206 | } |
207 | |
208 | return $line; |
209 | } |
210 | } |