using System; using System.Collections.Immutable; using System.Numerics; using System.Text; namespace gaemstone.Bloxel; [Flags] public enum Neighbor : byte { None = 0, // FACINGS East = 0b000011, // +X West = 0b000010, // -X Up = 0b001100, // +Y Down = 0b001000, // -Y South = 0b110000, // +Z North = 0b100000, // -Z // CARDINALS SouthEast = South | East, // +X +Z SouthWest = South | West, // -X +Z NorthEast = North | East, // +X -Z NorthWest = North | West, // -X -Z // ALL_AXIS_PLANES UpEast = Up | East , // +X +Y UpWest = Up | West , // -X +Y UpSouth = Up | South, // +Z +Y UpNorth = Up | North, // -Z +Y DownEast = Down | East , // +X -Y DownWest = Down | West , // -X -Y DownSouth = Down | South, // +Z -Y DownNorth = Down | North, // -Z -Y // ALL UpSouthEast = Up | South | East, // +X +Y +Z UpSouthWest = Up | South | West, // -X +Y +Z UpNorthEast = Up | North | East, // +X +Y -Z UpNorthWest = Up | North | West, // -X +Y -Z DownSouthEast = Down | South | East, // +X -Y +Z DownSouthWest = Down | South | West, // -X -Y +Z DownNorthEast = Down | North | East, // +X -Y -Z DownNorthWest = Down | North | West, // -X -Y -Z } public static class Neighbors { public static readonly ImmutableHashSet Horizontals = ImmutableHashSet.Create(Neighbor.East , Neighbor.West , Neighbor.South, Neighbor.North); public static readonly ImmutableHashSet Verticals = ImmutableHashSet.Create(Neighbor.Up, Neighbor.Down); public static readonly ImmutableHashSet Facings = Horizontals.Union(Verticals); public static readonly ImmutableHashSet Cardinals = Horizontals.Union(new[] { Neighbor.SouthEast, Neighbor.SouthWest, Neighbor.NorthEast, Neighbor.NorthWest }); public static readonly ImmutableHashSet AllAxisPlanes = Facings.Union(new[] { Neighbor.SouthEast, Neighbor.SouthWest, Neighbor.NorthEast, Neighbor.NorthWest, Neighbor.UpEast , Neighbor.UpWest , Neighbor.UpSouth , Neighbor.UpNorth , Neighbor.DownEast , Neighbor.DownWest , Neighbor.DownSouth, Neighbor.DownNorth }); public static readonly ImmutableHashSet All = AllAxisPlanes.Union(new[] { Neighbor.UpSouthEast, Neighbor.UpSouthWest, Neighbor.UpNorthEast, Neighbor.UpNorthWest, Neighbor.DownSouthEast, Neighbor.DownSouthWest, Neighbor.DownNorthEast, Neighbor.DownNorthWest }); } public static class NeighborExtensions { const int SetBitX = 0b000010, ValueBitX = 0b000001; const int SetBitY = 0b001000, ValueBitY = 0b000100; const int SetBitZ = 0b100000, ValueBitZ = 0b010000; public static void Deconstruct(this Neighbor self, out int x, out int y, out int z) { x = (((int)self & SetBitX) != 0) ? ((((int)self & ValueBitX) != 0) ? 1 : -1) : 0; y = (((int)self & SetBitY) != 0) ? ((((int)self & ValueBitY) != 0) ? 1 : -1) : 0; z = (((int)self & SetBitZ) != 0) ? ((((int)self & ValueBitZ) != 0) ? 1 : -1) : 0; } // public static Neighbor ToNeighbor(this Axis self, int v) // { // if ((v < -1) || (v > 1)) throw new ArgumentOutOfRangeException( // nameof(v), v, $"{nameof(v)} (={v}) must be within (-1, 1)"); // return self switch { // Axis.X => (v > 0) ? Neighbor.East : Neighbor.West , // Axis.Y => (v > 0) ? Neighbor.Up : Neighbor.Down , // Axis.Z => (v > 0) ? Neighbor.South : Neighbor.North, // _ => Neighbor.None // }; // } // public static Axis GetAxis(this Neighbor self) // => self switch { // Neighbor.East => Axis.X, // Neighbor.West => Axis.X, // Neighbor.Up => Axis.Y, // Neighbor.Down => Axis.Y, // Neighbor.South => Axis.Z, // Neighbor.North => Axis.Z, // _ => throw new ArgumentException(nameof(self), $"{self} is not one of FACINGS") // }; public static Neighbor ToNeighbor(this BlockFacing self) => self switch { BlockFacing.East => Neighbor.East , BlockFacing.West => Neighbor.West , BlockFacing.Up => Neighbor.Up , BlockFacing.Down => Neighbor.Down , BlockFacing.South => Neighbor.South, BlockFacing.North => Neighbor.North, _ => throw new ArgumentException( $"'{self}' is not a valid BlockFacing", nameof(self)) }; public static BlockFacing ToBlockFacing(this Neighbor self) => self switch { Neighbor.East => BlockFacing.East , Neighbor.West => BlockFacing.West , Neighbor.Up => BlockFacing.Up , Neighbor.Down => BlockFacing.Down , Neighbor.South => BlockFacing.South, Neighbor.North => BlockFacing.North, _ => throw new ArgumentException( $"'{self}' can't be converted to a valid BlockFacing", nameof(self)) }; public static Neighbor ToNeighbor(this (int x, int y, int z) p) { var neighbor = Neighbor.None; if (p.x != 0) { if (p.x == 1) neighbor |= Neighbor.East; else if (p.x == -1) neighbor |= Neighbor.West; else throw new ArgumentOutOfRangeException( nameof(p), p.x, $"{nameof(p)}.x (={p.x}) must be within (-1, 1)"); } if (p.y != 0) { if (p.y == 1) neighbor |= Neighbor.Up; else if (p.y == -1) neighbor |= Neighbor.Down; else throw new ArgumentOutOfRangeException( nameof(p), p.y, $"{nameof(p)}.y (={p.y}) must be within (-1, 1)"); } if (p.z != 0) { if (p.z == 1) neighbor |= Neighbor.South; else if (p.z == -1) neighbor |= Neighbor.North; else throw new ArgumentOutOfRangeException( nameof(p), p.z, $"{nameof(p)}.z (={p.z}) must be within (-1, 1)"); } return neighbor; } public static Neighbor GetOpposite(this Neighbor self) { var (x, y, z) = self; return (-x, -y, -z).ToNeighbor(); } public static BlockPos ToProperPos(this Neighbor self) { var (x, y, z) = self; return new(x, y, z); } public static Vector3 ToVector3(this Neighbor self) { var (x, y, z) = self; return new(x, y, z); } public static bool IsNone(this Neighbor self) => (self == Neighbor.None); public static bool IsHorizontal(this Neighbor self) => Neighbors.Horizontals.Contains(self); public static bool IsVertical(this Neighbor self) => Neighbors.Verticals.Contains(self); public static bool IsCardinal(this Neighbor self) => Neighbors.Cardinals.Contains(self); public static bool IsFacing(this Neighbor self) => Neighbors.Facings.Contains(self); public static bool IsValid(this Neighbor self) => Neighbors.All.Contains(self); public static string ToShortString(this Neighbor self) { if (!self.IsValid()) return "-"; var sb = new StringBuilder(3); foreach (var chr in self.ToString()) if ((chr >= 'A') && (chr <= 'Z')) // ASCII IsUpper sb.Append(chr + 0x20); // ASCII ToLower return sb.ToString(); } }