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.
206 lines
6.5 KiB
206 lines
6.5 KiB
2 years ago
|
using System;
|
||
|
using System.Collections.Immutable;
|
||
|
using System.Text;
|
||
|
using Silk.NET.Maths;
|
||
|
|
||
|
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 Vector3D<float> 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();
|
||
|
}
|
||
|
}
|