using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using Microsoft.Xna.Framework; 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 != Direction2.None).ToArray(); /// /// 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 == Direction2.Up || dir == Direction2.Right || dir == Direction2.Down || dir == Direction2.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 == Direction2.UpRight || dir == Direction2.DownRight || dir == Direction2.UpLeft || dir == Direction2.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 Direction2.Up: return new Point(0, -1); case Direction2.Right: return new Point(1, 0); case Direction2.Down: return new Point(0, 1); case Direction2.Left: return new Point(-1, 0); case Direction2.UpRight: return new Point(1, -1); case Direction2.DownRight: return new Point(1, 1); case Direction2.DownLeft: return new Point(-1, 1); case Direction2.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 Direction2.Up: return Direction2.Down; case Direction2.Right: return Direction2.Left; case Direction2.Down: return Direction2.Up; case Direction2.Left: return Direction2.Right; case Direction2.UpRight: return Direction2.DownLeft; case Direction2.DownRight: return Direction2.UpLeft; case Direction2.DownLeft: return Direction2.UpRight; case Direction2.UpLeft: return Direction2.DownRight; default: return Direction2.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) { switch (dir) { case Direction2.Up: return fortyFiveDegrees ? Direction2.UpRight : Direction2.Right; case Direction2.Right: return fortyFiveDegrees ? Direction2.DownRight : Direction2.Down; case Direction2.Down: return fortyFiveDegrees ? Direction2.DownLeft : Direction2.Left; case Direction2.Left: return fortyFiveDegrees ? Direction2.UpLeft : Direction2.Up; case Direction2.UpRight: return fortyFiveDegrees ? Direction2.Right : Direction2.DownRight; case Direction2.DownRight: return fortyFiveDegrees ? Direction2.Down : Direction2.DownLeft; case Direction2.DownLeft: return fortyFiveDegrees ? Direction2.Left : Direction2.UpLeft; case Direction2.UpLeft: return fortyFiveDegrees ? Direction2.Up : Direction2.UpRight; default: return Direction2.None; } } /// /// 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) { switch (dir) { case Direction2.Up: return fortyFiveDegrees ? Direction2.UpLeft : Direction2.Left; case Direction2.Right: return fortyFiveDegrees ? Direction2.UpRight : Direction2.Up; case Direction2.Down: return fortyFiveDegrees ? Direction2.DownRight : Direction2.Right; case Direction2.Left: return fortyFiveDegrees ? Direction2.DownLeft : Direction2.Down; case Direction2.UpRight: return fortyFiveDegrees ? Direction2.Up : Direction2.UpLeft; case Direction2.DownRight: return fortyFiveDegrees ? Direction2.Right : Direction2.UpRight; case Direction2.DownLeft: return fortyFiveDegrees ? Direction2.Down : Direction2.DownRight; case Direction2.UpLeft: return fortyFiveDegrees ? Direction2.Left : Direction2.DownLeft; default: return Direction2.None; } } /// /// 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 Direction2.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 Direction2.None; if (Math.Abs(offset.X) > Math.Abs(offset.Y)) return offset.X > 0 ? Direction2.Right : Direction2.Left; return offset.Y > 0 ? Direction2.Down : Direction2.Up; } } }