PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
BlockIterator.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
22 namespace pocketmine\utils;
23 
27 
31 class BlockIterator implements \Iterator{
32 
34  private $level;
35  private $maxDistance;
36 
37  private static $gridSize = 16777216; //1 << 24
38 
39  private $end = false;
40 
42  private $blockQueue;
43  private $currentBlock = 0;
45  private $currentBlockObject = null;
46  private $currentDistance = 0;
47  private $maxDistanceInt = 0;
48 
49  private $secondError;
50  private $thirdError;
51 
52  private $secondStep;
53  private $thirdStep;
54 
55  private $mainFace;
56  private $secondFace;
57  private $thirdFace;
58 
59  public function __construct(Level $level, Vector3 $start, Vector3 $direction, $yOffset = 0, $maxDistance = 0){
60  $this->level = $level;
61  $this->maxDistance = (int) $maxDistance;
62  $this->blockQueue = new \SplFixedArray(3);
63 
64  $startClone = new Vector3($start->x, $start->y, $start->z);
65  $startClone->y += $yOffset;
66 
67  $this->currentDistance = 0;
68 
69  $mainDirection = 0;
70  $secondDirection = 0;
71  $thirdDirection = 0;
72 
73  $mainPosition = 0;
74  $secondPosition = 0;
75  $thirdPosition = 0;
76 
77  $pos = new Vector3($startClone->x, $startClone->y, $startClone->z);
78  $startBlock = $this->level->getBlock($pos->floor());
79 
80  if($this->getXLength($direction) > $mainDirection){
81  $this->mainFace = $this->getXFace($direction);
82  $mainDirection = $this->getXLength($direction);
83  $mainPosition = $this->getXPosition($direction, $startClone, $startBlock);
84 
85  $this->secondFace = $this->getYFace($direction);
86  $secondDirection = $this->getYLength($direction);
87  $secondPosition = $this->getYPosition($direction, $startClone, $startBlock);
88 
89  $this->thirdFace = $this->getZFace($direction);
90  $thirdDirection = $this->getZLength($direction);
91  $thirdPosition = $this->getZPosition($direction, $startClone, $startBlock);
92  }
93  if($this->getYLength($direction) > $mainDirection){
94  $this->mainFace = $this->getYFace($direction);
95  $mainDirection = $this->getYLength($direction);
96  $mainPosition = $this->getYPosition($direction, $startClone, $startBlock);
97 
98  $this->secondFace = $this->getZFace($direction);
99  $secondDirection = $this->getZLength($direction);
100  $secondPosition = $this->getZPosition($direction, $startClone, $startBlock);
101 
102  $this->thirdFace = $this->getXFace($direction);
103  $thirdDirection = $this->getXLength($direction);
104  $thirdPosition = $this->getXPosition($direction, $startClone, $startBlock);
105  }
106  if($this->getZLength($direction) > $mainDirection){
107  $this->mainFace = $this->getZFace($direction);
108  $mainDirection = $this->getZLength($direction);
109  $mainPosition = $this->getZPosition($direction, $startClone, $startBlock);
110 
111  $this->secondFace = $this->getXFace($direction);
112  $secondDirection = $this->getXLength($direction);
113  $secondPosition = $this->getXPosition($direction, $startClone, $startBlock);
114 
115  $this->thirdFace = $this->getYFace($direction);
116  $thirdDirection = $this->getYLength($direction);
117  $thirdPosition = $this->getYPosition($direction, $startClone, $startBlock);
118  }
119 
120  $d = $mainPosition / $mainDirection;
121  $secondd = $secondPosition - $secondDirection * $d;
122  $thirdd = $thirdPosition - $thirdDirection * $d;
123 
124  $this->secondError = floor($secondd * self::$gridSize);
125  $this->secondStep = round($secondDirection / $mainDirection * self::$gridSize);
126  $this->thirdError = floor($thirdd * self::$gridSize);
127  $this->thirdStep = round($thirdDirection / $mainDirection * self::$gridSize);
128 
129  if($this->secondError + $this->secondStep <= 0){
130  $this->secondError = -$this->secondStep + 1;
131  }
132 
133  if($this->thirdError + $this->thirdStep <= 0){
134  $this->thirdError = -$this->thirdStep + 1;
135  }
136 
137  $lastBlock = $startBlock->getSide(Vector3::getOppositeSide($this->mainFace));
138 
139  if($this->secondError < 0){
140  $this->secondError += self::$gridSize;
141  $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->secondFace));
142  }
143 
144  if($this->thirdError < 0){
145  $this->thirdError += self::$gridSize;
146  $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->thirdFace));
147  }
148 
149  $this->secondError -= self::$gridSize;
150  $this->thirdError -= self::$gridSize;
151 
152  $this->blockQueue[0] = $lastBlock;
153 
154  $this->currentBlock = -1;
155 
156  $this->scan();
157 
158  $startBlockFound = false;
159 
160  for($cnt = $this->currentBlock; $cnt >= 0; --$cnt){
161  if($this->blockEquals($this->blockQueue[$cnt], $startBlock)){
162  $this->currentBlock = $cnt;
163  $startBlockFound = true;
164  break;
165  }
166  }
167 
168  if(!$startBlockFound){
169  throw new \InvalidStateException("Start block missed in BlockIterator");
170  }
171 
172  $this->maxDistanceInt = round($maxDistance / (sqrt($mainDirection ** 2 + $secondDirection ** 2 + $thirdDirection ** 2) / $mainDirection));
173  }
174 
175  private function blockEquals(Block $a, Block $b){
176  return $a->x === $b->x and $a->y === $b->y and $a->z === $b->z;
177  }
178 
179  private function getXFace(Vector3 $direction){
180  return (($direction->x) > 0) ? Vector3::SIDE_EAST : Vector3::SIDE_WEST;
181  }
182 
183  private function getYFace(Vector3 $direction){
184  return (($direction->y) > 0) ? Vector3::SIDE_UP : Vector3::SIDE_DOWN;
185  }
186 
187  private function getZFace(Vector3 $direction){
188  return (($direction->z) > 0) ? Vector3::SIDE_SOUTH : Vector3::SIDE_NORTH;
189  }
190 
191  private function getXLength(Vector3 $direction){
192  return abs($direction->x);
193  }
194 
195  private function getYLength(Vector3 $direction){
196  return abs($direction->y);
197  }
198 
199  private function getZLength(Vector3 $direction){
200  return abs($direction->z);
201  }
202 
203  private function getPosition($direction, $position, $blockPosition){
204  return $direction > 0 ? ($position - $blockPosition) : ($blockPosition + 1 - $position);
205  }
206 
207  private function getXPosition(Vector3 $direction, Vector3 $position, Block $block){
208  return $this->getPosition($direction->x, $position->x, $block->x);
209  }
210 
211  private function getYPosition(Vector3 $direction, Vector3 $position, Block $block){
212  return $this->getPosition($direction->y, $position->y, $block->y);
213  }
214 
215  private function getZPosition(Vector3 $direction, Vector3 $position, Block $block){
216  return $this->getPosition($direction->z, $position->z, $block->z);
217  }
218 
219  public function next(){
220  $this->scan();
221 
222  if($this->currentBlock <= -1){
223  throw new \OutOfBoundsException;
224  }else{
225  $this->currentBlockObject = $this->blockQueue[$this->currentBlock--];
226  }
227  }
228 
234  public function current(){
235  if($this->currentBlockObject === null){
236  throw new \OutOfBoundsException;
237  }
238  return $this->currentBlockObject;
239  }
240 
241  public function rewind(){
242  throw new \InvalidStateException("BlockIterator doesn't support rewind()");
243  }
244 
245  public function key(){
246  return $this->currentBlock - 1;
247  }
248 
249  public function valid(){
250  $this->scan();
251  return $this->currentBlock !== -1;
252  }
253 
254  private function scan(){
255  if($this->currentBlock >= 0){
256  return;
257  }
258 
259  if($this->maxDistance !== 0 and $this->currentDistance > $this->maxDistanceInt){
260  $this->end = true;
261  return;
262  }
263 
264  if($this->end){
265  return;
266  }
267 
268  ++$this->currentDistance;
269 
270  $this->secondError += $this->secondStep;
271  $this->thirdError += $this->thirdStep;
272 
273  if($this->secondError > 0 and $this->thirdError > 0){
274  $this->blockQueue[2] = $this->blockQueue[0]->getSide($this->mainFace);
275 
276  if($this->secondStep * $this->thirdError < $this->thirdStep * $this->secondError){
277  $this->blockQueue[1] = $this->blockQueue[2]->getSide($this->secondFace);
278  $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->thirdFace);
279  }else{
280  $this->blockQueue[1] = $this->blockQueue[2]->getSide($this->thirdFace);
281  $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->secondFace);
282  }
283 
284  $this->thirdError -= self::$gridSize;
285  $this->secondError -= self::$gridSize;
286  $this->currentBlock = 2;
287  }elseif($this->secondError > 0){
288  $this->blockQueue[1] = $this->blockQueue[0]->getSide($this->mainFace);
289  $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->thirdFace);
290  $this->secondError -= self::$gridSize;
291  $this->currentBlock = 1;
292  }elseif($this->thirdError > 0){
293  $this->blockQueue[1] = $this->blockQueue[0]->getSide($this->mainFace);
294  $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->secondFace);
295  $this->thirdError -= self::$gridSize;
296  $this->currentBlock = 1;
297  }else{
298  $this->blockQueue[0] = $this->blockQueue[0]->getSide($this->mainFace);
299  $this->currentBlock = 0;
300  }
301  }
302 }