From 40d3fddde561ac3f9a86ac85122dfd7f9a5ae5ce Mon Sep 17 00:00:00 2001 From: Hugo_ <55756021+Dhaiven@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:39:49 +0100 Subject: [PATCH] Facing and Axis to enum (#91) --- src/Axis.php | 24 +---- src/AxisAlignedBB.php | 64 +++++-------- src/Facing.php | 180 +++++++++++++++-------------------- src/RayTraceResult.php | 7 +- src/Vector3.php | 18 ++-- tests/phpunit/FacingTest.php | 18 ++-- 6 files changed, 123 insertions(+), 188 deletions(-) diff --git a/src/Axis.php b/src/Axis.php index 68f3198..f656929 100644 --- a/src/Axis.php +++ b/src/Axis.php @@ -23,24 +23,8 @@ namespace pocketmine\math; -final class Axis{ - private function __construct(){ - //NOOP - } - - public const Y = 0; - public const Z = 1; - public const X = 2; - - /** - * Returns a human-readable string representation of the given axis. - */ - public static function toString(int $axis) : string{ - return match($axis){ - Axis::Y => "y", - Axis::Z => "z", - Axis::X => "x", - default => throw new \InvalidArgumentException("Invalid axis $axis") - }; - } +enum Axis{ + case Y; + case Z; + case X; } diff --git a/src/AxisAlignedBB.php b/src/AxisAlignedBB.php index cf4a3e2..7f6b485 100644 --- a/src/AxisAlignedBB.php +++ b/src/AxisAlignedBB.php @@ -136,12 +136,10 @@ public function offsetCopy(float $x, float $y, float $z) : AxisAlignedBB{ /** * Offsets this AxisAlignedBB in the given direction by the specified distance. * - * @param int $face one of the Facing::* constants - * * @return $this */ - public function offsetTowards(int $face, float $distance) : AxisAlignedBB{ - [$offsetX, $offsetY, $offsetZ] = Facing::OFFSET[$face] ?? throw new \InvalidArgumentException("Invalid Facing $face"); + public function offsetTowards(Facing $face, float $distance) : AxisAlignedBB{ + [$offsetX, $offsetY, $offsetZ] = $face->offset(); return $this->offset($offsetX * $distance, $offsetY * $distance, $offsetZ * $distance); } @@ -149,7 +147,7 @@ public function offsetTowards(int $face, float $distance) : AxisAlignedBB{ /** * Returns an offset clone of this AxisAlignedBB. */ - public function offsetTowardsCopy(int $face, float $distance) : AxisAlignedBB{ + public function offsetTowardsCopy(Facing $face, float $distance) : AxisAlignedBB{ return (clone $this)->offsetTowards($face, $distance); } @@ -182,9 +180,8 @@ public function contractedCopy(float $x, float $y, float $z) : AxisAlignedBB{ * @param float $distance Negative values pull the face in, positive values push out. * * @return $this - * @throws \InvalidArgumentException */ - public function extend(int $face, float $distance) : AxisAlignedBB{ + public function extend(Facing $face, float $distance) : AxisAlignedBB{ match($face){ Facing::DOWN => $this->minY -= $distance, Facing::UP => $this->maxY += $distance, @@ -192,7 +189,6 @@ public function extend(int $face, float $distance) : AxisAlignedBB{ Facing::SOUTH => $this->maxZ += $distance, Facing::WEST => $this->minX -= $distance, Facing::EAST => $this->maxX += $distance, - default => throw new \InvalidArgumentException("Invalid face $face"), }; return $this; @@ -201,10 +197,8 @@ public function extend(int $face, float $distance) : AxisAlignedBB{ /** * Returns an extended clone of this bounding box. * @see AxisAlignedBB::extend() - * - * @throws \InvalidArgumentException */ - public function extendedCopy(int $face, float $distance) : AxisAlignedBB{ + public function extendedCopy(Facing $face, float $distance) : AxisAlignedBB{ return (clone $this)->extend($face, $distance); } @@ -215,32 +209,27 @@ public function extendedCopy(int $face, float $distance) : AxisAlignedBB{ * @param float $distance Positive values pull the face in, negative values push out. * * @return $this - * @throws \InvalidArgumentException */ - public function trim(int $face, float $distance) : AxisAlignedBB{ + public function trim(Facing $face, float $distance) : AxisAlignedBB{ return $this->extend($face, -$distance); } /** * Returns a trimmed clone of this bounding box. * @see AxisAlignedBB::trim() - * - * @throws \InvalidArgumentException */ - public function trimmedCopy(int $face, float $distance) : AxisAlignedBB{ + public function trimmedCopy(Facing $face, float $distance) : AxisAlignedBB{ return $this->extendedCopy($face, -$distance); } /** * Increases the dimension of the AABB along the given axis. * - * @param int $axis one of the Axis::* constants * @param float $distance Negative values reduce width, positive values increase width. * * @return $this - * @throws \InvalidArgumentException */ - public function stretch(int $axis, float $distance) : AxisAlignedBB{ + public function stretch(Axis $axis, float $distance) : AxisAlignedBB{ if($axis === Axis::Y){ $this->minY -= $distance; $this->maxY += $distance; @@ -250,19 +239,16 @@ public function stretch(int $axis, float $distance) : AxisAlignedBB{ }elseif($axis === Axis::X){ $this->minX -= $distance; $this->maxX += $distance; - }else{ - throw new \InvalidArgumentException("Invalid axis $axis"); } + return $this; } /** * Returns a stretched copy of this bounding box. * @see AxisAlignedBB::stretch() - * - * @throws \InvalidArgumentException */ - public function stretchedCopy(int $axis, float $distance) : AxisAlignedBB{ + public function stretchedCopy(Axis $axis, float $distance) : AxisAlignedBB{ return (clone $this)->stretch($axis, $distance); } @@ -271,19 +257,16 @@ public function stretchedCopy(int $axis, float $distance) : AxisAlignedBB{ * @see AxisAlignedBB::stretch() * * @return $this - * @throws \InvalidArgumentException */ - public function squash(int $axis, float $distance) : AxisAlignedBB{ + public function squash(Axis $axis, float $distance) : AxisAlignedBB{ return $this->stretch($axis, -$distance); } /** * Returns a squashed copy of this bounding box. * @see AxisAlignedBB::squash() - * - * @throws \InvalidArgumentException */ - public function squashedCopy(int $axis, float $distance) : AxisAlignedBB{ + public function squashedCopy(Axis $axis, float $distance) : AxisAlignedBB{ return $this->stretchedCopy($axis, -$distance); } @@ -463,29 +446,28 @@ public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?RayTraceResu $v6 = null; } - $vector = null; $distance = PHP_INT_MAX; - $face = -1; + $hitInfo = null; foreach([ - Facing::WEST => $v1, - Facing::EAST => $v2, - Facing::DOWN => $v3, - Facing::UP => $v4, - Facing::NORTH => $v5, - Facing::SOUTH => $v6 - ] as $f => $v){ + [Facing::WEST, $v1], + [Facing::EAST, $v2], + [Facing::DOWN, $v3], + [Facing::UP, $v4], + [Facing::NORTH, $v5], + [Facing::SOUTH, $v6] + ] as [$facing, $v]){ if($v !== null and ($d = $pos1->distanceSquared($v)) < $distance){ - $vector = $v; $distance = $d; - $face = $f; + $hitInfo = [$facing, $v]; } } - if($vector === null){ + if($hitInfo === null){ return null; } + [$face, $vector] = $hitInfo; return new RayTraceResult($this, $face, $vector); } diff --git a/src/Facing.php b/src/Facing.php index 134a29b..f6b0b51 100644 --- a/src/Facing.php +++ b/src/Facing.php @@ -23,31 +23,13 @@ namespace pocketmine\math; -use function in_array; - -final class Facing{ - private function __construct(){ - //NOOP - } - - public const FLAG_AXIS_POSITIVE = 1; - - /* most significant 2 bits = axis, least significant bit = is positive direction */ - public const DOWN = Axis::Y << 1; - public const UP = (Axis::Y << 1) | self::FLAG_AXIS_POSITIVE; - public const NORTH = Axis::Z << 1; - public const SOUTH = (Axis::Z << 1) | self::FLAG_AXIS_POSITIVE; - public const WEST = Axis::X << 1; - public const EAST = (Axis::X << 1) | self::FLAG_AXIS_POSITIVE; - - public const ALL = [ - self::DOWN, - self::UP, - self::NORTH, - self::SOUTH, - self::WEST, - self::EAST - ]; +enum Facing{ + case DOWN; + case UP; + case NORTH; + case SOUTH; + case WEST; + case EAST; public const HORIZONTAL = [ self::NORTH, @@ -56,120 +38,110 @@ private function __construct(){ self::EAST ]; - public const OFFSET = [ - self::DOWN => [ 0, -1, 0], - self::UP => [ 0, +1, 0], - self::NORTH => [ 0, 0, -1], - self::SOUTH => [ 0, 0, +1], - self::WEST => [-1, 0, 0], - self::EAST => [+1, 0, 0] - ]; - - private const CLOCKWISE = [ - Axis::Y => [ - self::NORTH => self::EAST, - self::EAST => self::SOUTH, - self::SOUTH => self::WEST, - self::WEST => self::NORTH - ], - Axis::Z => [ - self::UP => self::EAST, - self::EAST => self::DOWN, - self::DOWN => self::WEST, - self::WEST => self::UP - ], - Axis::X => [ - self::UP => self::NORTH, - self::NORTH => self::DOWN, - self::DOWN => self::SOUTH, - self::SOUTH => self::UP - ] - ]; - /** * Returns the axis of the given direction. */ - public static function axis(int $direction) : int{ - return $direction >> 1; //shift off positive/negative bit + public function axis() : Axis{ + return match($this){ + self::DOWN, self::UP => Axis::Y, + self::NORTH, self::SOUTH => Axis::Z, + self::WEST, self::EAST => Axis::X, + }; + } + + /** + * @phpstan-return array{-1|0|1, -1|0|1, -1|0|1} + */ + public function offset() : array{ + return match($this){ + self::DOWN => [ 0, -1, 0], + self::UP => [ 0, +1, 0], + self::NORTH => [ 0, 0, -1], + self::SOUTH => [ 0, 0, +1], + self::WEST => [-1, 0, 0], + self::EAST => [+1, 0, 0] + }; } /** * Returns whether the direction is facing the positive of its axis. */ - public static function isPositive(int $direction) : bool{ - return ($direction & self::FLAG_AXIS_POSITIVE) === self::FLAG_AXIS_POSITIVE; + public function isPositive() : bool{ + return match($this){ + self::UP, self::SOUTH, self::EAST => true, + self::DOWN, self::NORTH, self::WEST => false, + }; } /** * Returns the opposite Facing of the specified one. - * - * @param int $direction 0-5 one of the Facing::* constants */ - public static function opposite(int $direction) : int{ - return $direction ^ self::FLAG_AXIS_POSITIVE; + public function opposite() : Facing{ + return match($this){ + self::DOWN => self::UP, + self::UP => self::DOWN, + self::NORTH => self::SOUTH, + self::SOUTH => self::NORTH, + self::WEST => self::EAST, + self::EAST => self::WEST, + }; } /** * Rotates the given direction around the axis. * - * @throws \InvalidArgumentException if not possible to rotate $direction around $axis + * @throws \InvalidArgumentException if not possible to rotate this direction around $axis */ - public static function rotate(int $direction, int $axis, bool $clockwise) : int{ - if(!isset(self::CLOCKWISE[$axis])){ - throw new \InvalidArgumentException("Invalid axis $axis"); - } - if(!isset(self::CLOCKWISE[$axis][$direction])){ - throw new \InvalidArgumentException("Cannot rotate facing \"" . self::toString($direction) . "\" around axis \"" . Axis::toString($axis) . "\""); + public function rotate(Axis $axis, bool $clockwise) : Facing{ + $rotated = match($axis){ + Axis::Y => match($this){ + self::NORTH => self::EAST, + self::EAST => self::SOUTH, + self::SOUTH => self::WEST, + self::WEST => self::NORTH, + default => null + }, + Axis::Z => match($this){ + self::UP => self::EAST, + self::EAST => self::DOWN, + self::DOWN => self::WEST, + self::WEST => self::UP, + default => null + }, + Axis::X => match($this){ + self::UP => self::NORTH, + self::NORTH => self::DOWN, + self::DOWN => self::SOUTH, + self::SOUTH => self::UP, + default => null + } + }; + + if($rotated === null){ + throw new \InvalidArgumentException("Cannot rotate facing \"" . strtolower($this->name) . "\" around axis \"" . strtolower($axis->name) . "\""); } - $rotated = self::CLOCKWISE[$axis][$direction]; - return $clockwise ? $rotated : self::opposite($rotated); + return $clockwise ? $rotated : $rotated->opposite(); } /** * @throws \InvalidArgumentException */ - public static function rotateY(int $direction, bool $clockwise) : int{ - return self::rotate($direction, Axis::Y, $clockwise); + public function rotateY(bool $clockwise) : Facing{ + return $this->rotate(Axis::Y, $clockwise); } /** * @throws \InvalidArgumentException */ - public static function rotateZ(int $direction, bool $clockwise) : int{ - return self::rotate($direction, Axis::Z, $clockwise); + public function rotateZ(bool $clockwise) : Facing{ + return $this->rotate(Axis::Z, $clockwise); } /** * @throws \InvalidArgumentException */ - public static function rotateX(int $direction, bool $clockwise) : int{ - return self::rotate($direction, Axis::X, $clockwise); - } - - /** - * Validates the given integer as a Facing direction. - * - * @throws \InvalidArgumentException if the argument is not a valid Facing constant - */ - public static function validate(int $facing) : void{ - if(!in_array($facing, self::ALL, true)){ - throw new \InvalidArgumentException("Invalid direction $facing"); - } - } - - /** - * Returns a human-readable string representation of the given Facing direction. - */ - public static function toString(int $facing) : string{ - return match($facing){ - self::DOWN => "down", - self::UP => "up", - self::NORTH => "north", - self::SOUTH => "south", - self::WEST => "west", - self::EAST => "east", - default => throw new \InvalidArgumentException("Invalid facing $facing") - }; + public function rotateX(bool $clockwise) : Facing{ + return $this->rotate(Axis::X, $clockwise); } } diff --git a/src/RayTraceResult.php b/src/RayTraceResult.php index 4d0928a..156c7b1 100644 --- a/src/RayTraceResult.php +++ b/src/RayTraceResult.php @@ -28,12 +28,9 @@ */ class RayTraceResult{ - /** - * @param int $hitFace one of the Facing::* constants - */ public function __construct( public AxisAlignedBB $bb, - public int $hitFace, + public Facing $hitFace, public Vector3 $hitVector ){} @@ -41,7 +38,7 @@ public function getBoundingBox() : AxisAlignedBB{ return $this->bb; } - public function getHitFace() : int{ + public function getHitFace() : Facing{ return $this->hitFace; } diff --git a/src/Vector3.php b/src/Vector3.php index 11a6484..82ade68 100644 --- a/src/Vector3.php +++ b/src/Vector3.php @@ -117,8 +117,8 @@ public function abs() : Vector3{ /** * @return Vector3 */ - public function getSide(int $side, int $step = 1){ - [$offsetX, $offsetY, $offsetZ] = Facing::OFFSET[$side] ?? [0, 0, 0]; + public function getSide(Facing $side, int $step = 1){ + [$offsetX, $offsetY, $offsetZ] = $side->offset(); return $this->add($offsetX * $step, $offsetY * $step, $offsetZ * $step); } @@ -171,10 +171,10 @@ public function east(int $step = 1){ * @param int $step Distance in each direction to shift the vector * * @return \Generator|Vector3[] - * @phpstan-return \Generator + * @phpstan-return \Generator */ public function sides(int $step = 1) : \Generator{ - foreach(Facing::ALL as $facing){ + foreach(Facing::cases() as $facing){ yield $facing => $this->getSide($facing, $step); } } @@ -191,14 +191,14 @@ public function sidesArray(bool $keys = false, int $step = 1) : array{ /** * Yields vectors stepped out from this one in directions except those on the given axis. * - * @param int $axis Facing directions on this axis will be excluded + * @param Axis $axis Facing directions on this axis will be excluded * * @return \Generator|Vector3[] - * @phpstan-return \Generator + * @phpstan-return \Generator */ - public function sidesAroundAxis(int $axis, int $step = 1) : \Generator{ - foreach(Facing::ALL as $facing){ - if(Facing::axis($facing) !== $axis){ + public function sidesAroundAxis(Axis $axis, int $step = 1) : \Generator{ + foreach(Facing::cases() as $facing){ + if($facing->axis() !== $axis){ yield $facing => $this->getSide($facing, $step); } } diff --git a/tests/phpunit/FacingTest.php b/tests/phpunit/FacingTest.php index 51e3df0..41c988d 100644 --- a/tests/phpunit/FacingTest.php +++ b/tests/phpunit/FacingTest.php @@ -36,8 +36,8 @@ public static function axisProvider() : \Generator{ } #[DataProvider("axisProvider")] - public function testAxis(int $direction, int $axis) : void{ - self::assertEquals($axis, Facing::axis($direction)); + public function testAxis(Facing $direction, Axis $axis) : void{ + self::assertEquals($axis, $direction->axis()); } public static function oppositeProvider() : \Generator{ @@ -47,9 +47,9 @@ public static function oppositeProvider() : \Generator{ } #[DataProvider("oppositeProvider")] - public function testOpposite(int $dir1, int $dir2) : void{ - self::assertEquals($dir2, Facing::opposite($dir1)); - self::assertEquals($dir1, Facing::opposite($dir2)); + public function testOpposite(Facing $dir1, Facing $dir2) : void{ + self::assertEquals($dir2, $dir1->opposite()); + self::assertEquals($dir1, $dir2->opposite()); } public static function positiveProvider() : \Generator{ @@ -62,8 +62,8 @@ public static function positiveProvider() : \Generator{ } #[DataProvider("positiveProvider")] - public function testIsPositive(int $facing, bool $positive) : void{ - self::assertEquals($positive, Facing::isPositive($facing)); + public function testIsPositive(Facing $facing, bool $positive) : void{ + self::assertEquals($positive, $facing->isPositive()); } public static function rotateProvider() : \Generator{ @@ -101,7 +101,7 @@ public static function rotateProvider() : \Generator{ } #[DataProvider("rotateProvider")] - public function testRotate(int $direction, int $axis, bool $clockwise, int $expected) : void{ - self::assertEquals($expected, Facing::rotate($direction, $axis, $clockwise)); + public function testRotate(Facing $direction, Axis $axis, bool $clockwise, Facing $expected) : void{ + self::assertEquals($expected, $direction->rotate($axis, $clockwise)); } }