using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework;
using static MLEM.Misc.Direction2;
namespace MLEM.Misc {
///
/// An enum that represents two-dimensional directions.
/// Both straight and diagonal directions are supported.
/// There are several extension methods and arrays available in .
///
[Flags, DataContract]
public enum Direction2 {
///
/// No direction.
///
[EnumMember]
None = 0,
///
/// The up direction, or -y.
///
[EnumMember]
Up = 1,
///
/// The right direction, or +x.
///
[EnumMember]
Right = 2,
///
/// The down direction, or +y.
///
[EnumMember]
Down = 4,
///
/// The left direction, or -x.
///
[EnumMember]
Left = 8,
///
/// The up and right direction, or +x, -y.
///
[EnumMember]
UpRight = Up | Right,
///
/// The down and right direction, or +x, +y.
///
[EnumMember]
DownRight = Down | Right,
///
/// The up and left direction, or -x, -y.
///
[EnumMember]
UpLeft = Up | Left,
///
/// The down and left direction, or -x, +y.
///
[EnumMember]
DownLeft = Down | Left
}
///
/// A set of helper and extension methods for dealing with
///
public static class Direction2Helper {
///
/// All enum values
///
public static readonly Direction2[] All = EnumHelper.GetValues().ToArray();
///
/// The through directions
///
public static readonly Direction2[] Adjacent = All.Where(IsAdjacent).ToArray();
///
/// The through directions
///
public static readonly Direction2[] Diagonals = All.Where(IsDiagonal).ToArray();
///
/// All directions except
///
public static readonly Direction2[] AllExceptNone = All.Where(dir => dir != None).ToArray();
private static readonly Direction2[] Clockwise = {Up, UpRight, Right, DownRight, Down, DownLeft, Left, UpLeft};
private static readonly Dictionary ClockwiseLookup = Clockwise.Select((d, i) => (d, i)).ToDictionary(kv => kv.d, kv => kv.i);
///
/// Returns if the given direction is considered an "adjacent" direction.
/// An adjacent direction is one that is not a diagonal.
///
/// The direction to query
/// Whether the direction is adjacent
public static bool IsAdjacent(this Direction2 dir) {
return dir == Up || dir == Right || dir == Down || dir == Left;
}
///
/// Returns if the given direction is considered a diagonal direction.
///
/// The direction to query
/// Whether the direction is diagonal
public static bool IsDiagonal(this Direction2 dir) {
return dir == UpRight || dir == DownRight || dir == UpLeft || dir == DownLeft;
}
///
/// Returns the directional offset of a given direction.
/// The offset direction will be exactly one unit in each axis that the direction represents.
///
/// The direction whose offset to query
/// The direction's offset
public static Point Offset(this Direction2 dir) {
switch (dir) {
case Up:
return new Point(0, -1);
case Right:
return new Point(1, 0);
case Down:
return new Point(0, 1);
case Left:
return new Point(-1, 0);
case UpRight:
return new Point(1, -1);
case DownRight:
return new Point(1, 1);
case DownLeft:
return new Point(-1, 1);
case UpLeft:
return new Point(-1, -1);
default:
return Point.Zero;
}
}
///
/// Maps each direction in the given enumerable of directions to its .
///
/// The direction enumerable
/// The directions' offsets, in the same order as the input directions.
public static IEnumerable Offsets(this IEnumerable directions) {
foreach (var dir in directions)
yield return dir.Offset();
}
///
/// Returns the opposite of the given direction.
/// For "adjacent" directions, this is the direction that points into the same axis, but the opposite sign.
/// For diagonal directions, this is the direction that points into the opposite of both the x and y axis.
///
/// The direction whose opposite to get
/// The opposite of the direction
public static Direction2 Opposite(this Direction2 dir) {
switch (dir) {
case Up:
return Down;
case Right:
return Left;
case Down:
return Up;
case Left:
return Right;
case UpRight:
return DownLeft;
case DownRight:
return UpLeft;
case DownLeft:
return UpRight;
case UpLeft:
return DownRight;
default:
return None;
}
}
///
/// Returns the angle of the direction in radians, where has an angle of 0.
///
/// The direction whose angle to get
/// The direction's angle
public static float Angle(this Direction2 dir) {
var (x, y) = dir.Offset();
return (float) Math.Atan2(y, x);
}
///
/// Rotates the given direction clockwise and returns the resulting direction.
///
/// The direction to rotate
/// Whether to rotate by 45 degrees. If this is false, the rotation is 90 degrees instead.
/// The rotated direction
public static Direction2 RotateCw(this Direction2 dir, bool fortyFiveDegrees = false) {
if (!ClockwiseLookup.TryGetValue(dir, out var dirIndex))
return None;
return Clockwise[(dirIndex + (fortyFiveDegrees ? 1 : 2)) % Clockwise.Length];
}
///
/// Rotates the given direction counter-clockwise and returns the resulting direction.
///
/// The direction to rotate counter-clockwise
/// Whether to rotate by 45 degrees. If this is false, the rotation is 90 degrees instead.
/// The rotated direction
public static Direction2 RotateCcw(this Direction2 dir, bool fortyFiveDegrees = false) {
if (!ClockwiseLookup.TryGetValue(dir, out var dirIndex))
return None;
var index = dirIndex - (fortyFiveDegrees ? 1 : 2);
return Clockwise[index < 0 ? index + Clockwise.Length : index];
}
///
/// Returns the that is closest to the given position's facing direction.
///
/// The vector whose corresponding direction to get
/// The vector's direction
public static Direction2 ToDirection(this Vector2 offset) {
var offsetAngle = (float) Math.Atan2(offset.Y, offset.X);
foreach (var dir in AllExceptNone) {
if (Math.Abs(dir.Angle() - offsetAngle) <= MathHelper.PiOver4 / 2)
return dir;
}
return None;
}
///
/// Returns the that is closest to the given position's facing direction, only taking directions into account.
/// Diagonal directions will be rounded to the nearest vertical direction.
///
/// The vector whose corresponding direction to get
/// The vector's direction
public static Direction2 To90Direction(this Vector2 offset) {
if (offset.X == 0 && offset.Y == 0)
return None;
if (Math.Abs(offset.X) > Math.Abs(offset.Y))
return offset.X > 0 ? Right : Left;
return offset.Y > 0 ? Down : Up;
}
///
/// Rotates the given direction by a given reference direction
///
/// The direction to rotate
/// The direction to rotate by
/// The direction to use as the default direction
/// The direction, rotated by the reference direction
public static Direction2 RotateBy(this Direction2 dir, Direction2 reference, Direction2 start = Up) {
if (!ClockwiseLookup.TryGetValue(reference, out var refIndex))
return None;
if (!ClockwiseLookup.TryGetValue(start, out var startIndex))
return None;
if (!ClockwiseLookup.TryGetValue(dir, out var dirIndex))
return None;
var diff = refIndex - startIndex;
if (diff < 0)
diff += Clockwise.Length;
return Clockwise[(dirIndex + diff) % Clockwise.Length];
}
}
}