You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

205 lines
6.5 KiB

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<Neighbor> Horizontals
= ImmutableHashSet.Create(Neighbor.East , Neighbor.West ,
Neighbor.South, Neighbor.North);
public static readonly ImmutableHashSet<Neighbor> Verticals
= ImmutableHashSet.Create(Neighbor.Up, Neighbor.Down);
public static readonly ImmutableHashSet<Neighbor> Facings
= Horizontals.Union(Verticals);
public static readonly ImmutableHashSet<Neighbor> Cardinals
= Horizontals.Union(new[] {
Neighbor.SouthEast, Neighbor.SouthWest,
Neighbor.NorthEast, Neighbor.NorthWest });
public static readonly ImmutableHashSet<Neighbor> 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<Neighbor> 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();
}
}