PocketMine-MP 5.15.1 git-fb9a74e8799c71ed8292cfa53abe7a4c9204629d
AxisAlignedBB.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
22declare(strict_types=1);
23
24namespace pocketmine\math;
25
26use function abs;
27use const PHP_INT_MAX;
28
29final class AxisAlignedBB{
30
31 public float $minX;
32 public float $minY;
33 public float $minZ;
34 public float $maxX;
35 public float $maxY;
36 public float $maxZ;
37
38 public function __construct(float $minX, float $minY, float $minZ, float $maxX, float $maxY, float $maxZ){
39 if($minX > $maxX){
40 throw new \InvalidArgumentException("minX $minX is larger than maxX $maxX");
41 }
42 if($minY > $maxY){
43 throw new \InvalidArgumentException("minY $minY is larger than maxY $maxY");
44 }
45 if($minZ > $maxZ){
46 throw new \InvalidArgumentException("minZ $minZ is larger than maxZ $maxZ");
47 }
48 $this->minX = $minX;
49 $this->minY = $minY;
50 $this->minZ = $minZ;
51 $this->maxX = $maxX;
52 $this->maxY = $maxY;
53 $this->maxZ = $maxZ;
54 }
55
61 public function addCoord(float $x, float $y, float $z) : AxisAlignedBB{
62 $minX = $this->minX;
63 $minY = $this->minY;
64 $minZ = $this->minZ;
65 $maxX = $this->maxX;
66 $maxY = $this->maxY;
67 $maxZ = $this->maxZ;
68
69 if($x < 0){
70 $minX += $x;
71 }elseif($x > 0){
72 $maxX += $x;
73 }
74
75 if($y < 0){
76 $minY += $y;
77 }elseif($y > 0){
78 $maxY += $y;
79 }
80
81 if($z < 0){
82 $minZ += $z;
83 }elseif($z > 0){
84 $maxZ += $z;
85 }
86
87 return new AxisAlignedBB($minX, $minY, $minZ, $maxX, $maxY, $maxZ);
88 }
89
95 public function expand(float $x, float $y, float $z){
96 $this->minX -= $x;
97 $this->minY -= $y;
98 $this->minZ -= $z;
99 $this->maxX += $x;
100 $this->maxY += $y;
101 $this->maxZ += $z;
102
103 return $this;
104 }
105
109 public function expandedCopy(float $x, float $y, float $z) : AxisAlignedBB{
110 return (clone $this)->expand($x, $y, $z);
111 }
112
118 public function offset(float $x, float $y, float $z) : AxisAlignedBB{
119 $this->minX += $x;
120 $this->minY += $y;
121 $this->minZ += $z;
122 $this->maxX += $x;
123 $this->maxY += $y;
124 $this->maxZ += $z;
125
126 return $this;
127 }
128
132 public function offsetCopy(float $x, float $y, float $z) : AxisAlignedBB{
133 return (clone $this)->offset($x, $y, $z);
134 }
135
143 public function offsetTowards(int $face, float $distance) : AxisAlignedBB{
144 [$offsetX, $offsetY, $offsetZ] = Facing::OFFSET[$face] ?? throw new \InvalidArgumentException("Invalid Facing $face");
145
146 return $this->offset($offsetX * $distance, $offsetY * $distance, $offsetZ * $distance);
147 }
148
152 public function offsetTowardsCopy(int $face, float $distance) : AxisAlignedBB{
153 return (clone $this)->offsetTowards($face, $distance);
154 }
155
161 public function contract(float $x, float $y, float $z) : AxisAlignedBB{
162 $this->minX += $x;
163 $this->minY += $y;
164 $this->minZ += $z;
165 $this->maxX -= $x;
166 $this->maxY -= $y;
167 $this->maxZ -= $z;
168
169 return $this;
170 }
171
175 public function contractedCopy(float $x, float $y, float $z) : AxisAlignedBB{
176 return (clone $this)->contract($x, $y, $z);
177 }
178
187 public function extend(int $face, float $distance) : AxisAlignedBB{
188 match($face){
189 Facing::DOWN => $this->minY -= $distance,
190 Facing::UP => $this->maxY += $distance,
191 Facing::NORTH => $this->minZ -= $distance,
192 Facing::SOUTH => $this->maxZ += $distance,
193 Facing::WEST => $this->minX -= $distance,
194 Facing::EAST => $this->maxX += $distance,
195 default => throw new \InvalidArgumentException("Invalid face $face"),
196 };
197
198 return $this;
199 }
200
207 public function extendedCopy(int $face, float $distance) : AxisAlignedBB{
208 return (clone $this)->extend($face, $distance);
209 }
210
220 public function trim(int $face, float $distance) : AxisAlignedBB{
221 return $this->extend($face, -$distance);
222 }
223
230 public function trimmedCopy(int $face, float $distance) : AxisAlignedBB{
231 return $this->extendedCopy($face, -$distance);
232 }
233
243 public function stretch(int $axis, float $distance) : AxisAlignedBB{
244 if($axis === Axis::Y){
245 $this->minY -= $distance;
246 $this->maxY += $distance;
247 }elseif($axis === Axis::Z){
248 $this->minZ -= $distance;
249 $this->maxZ += $distance;
250 }elseif($axis === Axis::X){
251 $this->minX -= $distance;
252 $this->maxX += $distance;
253 }else{
254 throw new \InvalidArgumentException("Invalid axis $axis");
255 }
256 return $this;
257 }
258
265 public function stretchedCopy(int $axis, float $distance) : AxisAlignedBB{
266 return (clone $this)->stretch($axis, $distance);
267 }
268
276 public function squash(int $axis, float $distance) : AxisAlignedBB{
277 return $this->stretch($axis, -$distance);
278 }
279
286 public function squashedCopy(int $axis, float $distance) : AxisAlignedBB{
287 return $this->stretchedCopy($axis, -$distance);
288 }
289
290 public function calculateXOffset(AxisAlignedBB $bb, float $x) : float{
291 if($bb->maxY <= $this->minY or $bb->minY >= $this->maxY){
292 return $x;
293 }
294 if($bb->maxZ <= $this->minZ or $bb->minZ >= $this->maxZ){
295 return $x;
296 }
297 if($x > 0 and $bb->maxX <= $this->minX){
298 $x1 = $this->minX - $bb->maxX;
299 if($x1 < $x){
300 $x = $x1;
301 }
302 }elseif($x < 0 and $bb->minX >= $this->maxX){
303 $x2 = $this->maxX - $bb->minX;
304 if($x2 > $x){
305 $x = $x2;
306 }
307 }
308
309 return $x;
310 }
311
312 public function calculateYOffset(AxisAlignedBB $bb, float $y) : float{
313 if($bb->maxX <= $this->minX or $bb->minX >= $this->maxX){
314 return $y;
315 }
316 if($bb->maxZ <= $this->minZ or $bb->minZ >= $this->maxZ){
317 return $y;
318 }
319 if($y > 0 and $bb->maxY <= $this->minY){
320 $y1 = $this->minY - $bb->maxY;
321 if($y1 < $y){
322 $y = $y1;
323 }
324 }elseif($y < 0 and $bb->minY >= $this->maxY){
325 $y2 = $this->maxY - $bb->minY;
326 if($y2 > $y){
327 $y = $y2;
328 }
329 }
330
331 return $y;
332 }
333
334 public function calculateZOffset(AxisAlignedBB $bb, float $z) : float{
335 if($bb->maxX <= $this->minX or $bb->minX >= $this->maxX){
336 return $z;
337 }
338 if($bb->maxY <= $this->minY or $bb->minY >= $this->maxY){
339 return $z;
340 }
341 if($z > 0 and $bb->maxZ <= $this->minZ){
342 $z1 = $this->minZ - $bb->maxZ;
343 if($z1 < $z){
344 $z = $z1;
345 }
346 }elseif($z < 0 and $bb->minZ >= $this->maxZ){
347 $z2 = $this->maxZ - $bb->minZ;
348 if($z2 > $z){
349 $z = $z2;
350 }
351 }
352
353 return $z;
354 }
355
359 public function intersectsWith(AxisAlignedBB $bb, float $epsilon = 0.00001) : bool{
360 if($bb->maxX - $this->minX > $epsilon and $this->maxX - $bb->minX > $epsilon){
361 if($bb->maxY - $this->minY > $epsilon and $this->maxY - $bb->minY > $epsilon){
362 return $bb->maxZ - $this->minZ > $epsilon and $this->maxZ - $bb->minZ > $epsilon;
363 }
364 }
365
366 return false;
367 }
368
372 public function isVectorInside(Vector3 $vector) : bool{
373 if($vector->x <= $this->minX or $vector->x >= $this->maxX){
374 return false;
375 }
376 if($vector->y <= $this->minY or $vector->y >= $this->maxY){
377 return false;
378 }
379
380 return $vector->z > $this->minZ and $vector->z < $this->maxZ;
381 }
382
386 public function getAverageEdgeLength() : float{
387 return ($this->maxX - $this->minX + $this->maxY - $this->minY + $this->maxZ - $this->minZ) / 3;
388 }
389
390 public function getXLength() : float{ return $this->maxX - $this->minX; }
391
392 public function getYLength() : float{ return $this->maxY - $this->minY; }
393
394 public function getZLength() : float{ return $this->maxZ - $this->minZ; }
395
396 public function isCube(float $epsilon = 0.000001) : bool{
397 [$xLen, $yLen, $zLen] = [$this->getXLength(), $this->getYLength(), $this->getZLength()];
398 return abs($xLen - $yLen) < $epsilon && abs($yLen - $zLen) < $epsilon;
399 }
400
404 public function getVolume() : float{
405 return ($this->maxX - $this->minX) * ($this->maxY - $this->minY) * ($this->maxZ - $this->minZ);
406 }
407
411 public function isVectorInYZ(Vector3 $vector) : bool{
412 return $vector->y >= $this->minY and $vector->y <= $this->maxY and $vector->z >= $this->minZ and $vector->z <= $this->maxZ;
413 }
414
418 public function isVectorInXZ(Vector3 $vector) : bool{
419 return $vector->x >= $this->minX and $vector->x <= $this->maxX and $vector->z >= $this->minZ and $vector->z <= $this->maxZ;
420 }
421
425 public function isVectorInXY(Vector3 $vector) : bool{
426 return $vector->x >= $this->minX and $vector->x <= $this->maxX and $vector->y >= $this->minY and $vector->y <= $this->maxY;
427 }
428
434 public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResult{
435 $v1 = $pos1->getIntermediateWithXValue($pos2, $this->minX);
436 $v2 = $pos1->getIntermediateWithXValue($pos2, $this->maxX);
437 $v3 = $pos1->getIntermediateWithYValue($pos2, $this->minY);
438 $v4 = $pos1->getIntermediateWithYValue($pos2, $this->maxY);
439 $v5 = $pos1->getIntermediateWithZValue($pos2, $this->minZ);
440 $v6 = $pos1->getIntermediateWithZValue($pos2, $this->maxZ);
441
442 if($v1 !== null and !$this->isVectorInYZ($v1)){
443 $v1 = null;
444 }
445
446 if($v2 !== null and !$this->isVectorInYZ($v2)){
447 $v2 = null;
448 }
449
450 if($v3 !== null and !$this->isVectorInXZ($v3)){
451 $v3 = null;
452 }
453
454 if($v4 !== null and !$this->isVectorInXZ($v4)){
455 $v4 = null;
456 }
457
458 if($v5 !== null and !$this->isVectorInXY($v5)){
459 $v5 = null;
460 }
461
462 if($v6 !== null and !$this->isVectorInXY($v6)){
463 $v6 = null;
464 }
465
466 $vector = null;
467 $distance = PHP_INT_MAX;
468 $face = -1;
469
470 foreach([
471 Facing::WEST => $v1,
472 Facing::EAST => $v2,
473 Facing::DOWN => $v3,
474 Facing::UP => $v4,
475 Facing::NORTH => $v5,
476 Facing::SOUTH => $v6
477 ] as $f => $v){
478 if($v !== null and ($d = $pos1->distanceSquared($v)) < $distance){
479 $vector = $v;
480 $distance = $d;
481 $face = $f;
482 }
483 }
484
485 if($vector === null){
486 return null;
487 }
488
489 return new RayTraceResult($this, $face, $vector);
490 }
491
492 public function __toString() : string{
493 return "AxisAlignedBB({$this->minX}, {$this->minY}, {$this->minZ}, {$this->maxX}, {$this->maxY}, {$this->maxZ})";
494 }
495
499 public static function one() : AxisAlignedBB{
500 return new AxisAlignedBB(0, 0, 0, 1, 1, 1);
501 }
502}
extend(int $face, float $distance)
squash(int $axis, float $distance)
squashedCopy(int $axis, float $distance)
trimmedCopy(int $face, float $distance)
offset(float $x, float $y, float $z)
extendedCopy(int $face, float $distance)
intersectsWith(AxisAlignedBB $bb, float $epsilon=0.00001)
trim(int $face, float $distance)
stretch(int $axis, float $distance)
expand(float $x, float $y, float $z)
expandedCopy(float $x, float $y, float $z)
contractedCopy(float $x, float $y, float $z)
contract(float $x, float $y, float $z)
offsetTowards(int $face, float $distance)
addCoord(float $x, float $y, float $z)
stretchedCopy(int $axis, float $distance)
offsetCopy(float $x, float $y, float $z)
offsetTowardsCopy(int $face, float $distance)
calculateIntercept(Vector3 $pos1, Vector3 $pos2)
getIntermediateWithZValue(Vector3 $v, float $z)
Definition: Vector3.php:307
getIntermediateWithXValue(Vector3 $v, float $x)
Definition: Vector3.php:269
getIntermediateWithYValue(Vector3 $v, float $y)
Definition: Vector3.php:288