#region Copyright & License Information /* * Copyright (c) The OpenRA Developers and Contributors * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System; using System.Collections.Immutable; using System.Linq; namespace OpenRA.Mods.Common.MapGenerator { /// /// Utilities for simple directions and adjacency. Note that coordinate systems might not agree /// as to which directions are conceptually left/right or up/down. /// public static class Direction { /// No direction. public const int None = -1; /// +X ("right"). public const int R = 0; /// +X+Y ("right down"). public const int RD = 1; /// +Y ("down"). public const int D = 2; /// -X+Y ("left down"). public const int LD = 3; /// -X ("left"). public const int L = 4; /// -X-Y ("left up"). public const int LU = 5; /// -Y ("up"). public const int U = 6; /// +X-Y ("right up"). public const int RU = 7; /// Bitmask right. public const int MR = 1 << R; /// Bitmask right-down. public const int MRD = 1 << RD; /// Bitmask down. public const int MD = 1 << D; /// Bitmask left-down. public const int MLD = 1 << LD; /// Bitmask left. public const int ML = 1 << L; /// Bitmask left-up. public const int MLU = 1 << LU; /// Bitmask up. public const int MU = 1 << U; /// Bitmask right-up. public const int MRU = 1 << RU; /// Adjacent offsets with directions, excluding diagonals. public static readonly ImmutableArray<(int2, int)> Spread4D = ImmutableArray.Create(new[] { (new int2(1, 0), R), (new int2(0, 1), D), (new int2(-1, 0), L), (new int2(0, -1), U) }); /// Adjacent offsets, excluding diagonals. public static readonly ImmutableArray Spread4 = Spread4D.Select(((int2 XY, int _) v) => v.XY).ToImmutableArray(); /// /// Adjacent offsets, excluding diagonals. Assumes that CVec(1, 0) /// corresponds to Direction.R. /// public static readonly ImmutableArray Spread4CVec = Spread4.Select(xy => new CVec(xy.X, xy.Y)).ToImmutableArray(); /// Adjacent offsets with directions, including diagonals. public static readonly ImmutableArray<(int2, int)> Spread8D = ImmutableArray.Create(new[] { (new int2(1, 0), R), (new int2(1, 1), RD), (new int2(0, 1), D), (new int2(-1, 1), LD), (new int2(-1, 0), L), (new int2(-1, -1), LU), (new int2(0, -1), U), (new int2(1, -1), RU) }); /// Adjacent offsets, including diagonals. public static readonly ImmutableArray Spread8 = Spread8D.Select(((int2 XY, int _) v) => v.XY).ToImmutableArray(); /// /// Adjacent offsets, including diagonals. Assumes that CVec(1, 0) /// corresponds to Direction.R. /// public static readonly ImmutableArray Spread8CVec = Spread8.Select(xy => new CVec(xy.X, xy.Y)).ToImmutableArray(); /// Convert a non-none direction to an int2 offset. public static int2 ToInt2(int d) { if (d >= 0 && d < 8) return Spread8[d]; else throw new ArgumentException("bad direction"); } /// /// Convert a non-none direction to a CVec offset. Assumes that /// CVec(1, 0) corresponds to Direction.R. /// public static CVec ToCVec(int d) { if (d >= 0 && d < 8) return Spread8CVec[d]; else throw new ArgumentException("bad direction"); } /// /// Convert an offset (of arbitrary non-zero magnitude) to a direction. /// Supplying a zero-offset will throw. /// public static int FromOffset(int dx, int dy) { if (dx > 0) { if (dy > 0) return RD; else if (dy < 0) return RU; else return R; } else if (dx < 0) { if (dy > 0) return LD; else if (dy < 0) return LU; else return L; } else { if (dy > 0) return D; else if (dy < 0) return U; else throw new ArgumentException("Bad direction"); } } /// /// Convert an offset (of arbitrary non-zero magnitude) to a direction. /// Supplying a zero-offset will throw. /// public static int FromInt2(int2 delta) => FromOffset(delta.X, delta.Y); /// /// Convert an offset (of arbitrary non-zero magnitude) to a direction. /// Supplying a zero-offset will throw. Assumes that CVec(1, 0) /// corresponds to Direction.R. /// public static int FromCVec(CVec delta) => FromOffset(delta.X, delta.Y); /// /// Convert an offset (of arbitrary non-zero magnitude) to a non-diagonal direction. /// Supplying a zero-offset will throw. /// public static int FromOffsetNonDiagonal(int dx, int dy) { if (dx - dy > 0 && dx + dy >= 0) return R; if (dy + dx > 0 && dy - dx >= 0) return D; if (-dx + dy > 0 && -dx - dy >= 0) return L; if (-dy - dx > 0 && -dy + dx >= 0) return U; throw new ArgumentException("bad direction"); } /// /// Convert an offset (of arbitrary non-zero magnitude) to a /// non-diagonal direction. Supplying a zero-offset will throw. /// public static int FromInt2NonDiagonal(int2 delta) => FromOffsetNonDiagonal(delta.X, delta.Y); /// /// Convert an offset (of arbitrary non-zero magnitude) to a /// non-diagonal direction. Supplying a zero-offset will throw. Assumes /// that CVec(1, 0) corresponds to Direction.R. /// public static int FromCVecNonDiagonal(CVec delta) => FromOffsetNonDiagonal(delta.X, delta.Y); /// Return the opposite direction. public static int Reverse(int direction) { if (direction == None) return None; return direction ^ 4; } /// Convert a direction to a short string, like "None", "R", "RD", etc. public static string ToString(int direction) { switch (direction) { case None: return "None"; case R: return "R"; case RD: return "RD"; case D: return "D"; case LD: return "LD"; case L: return "L"; case LU: return "LU"; case U: return "U"; case RU: return "RU"; default: throw new ArgumentException("bad direction"); } } /// Count the number of set bits in a direction mask. public static int Count(int dm) { var count = 0; for (var m = dm; m != 0; m >>= 1) if ((m & 1) == 1) count++; return count; } /// Finds the only direction set in a direction mask or returns NONE. public static int FromMask(int mask) { switch (mask) { case MR: return R; case MRD: return RD; case MD: return D; case MLD: return LD; case ML: return L; case MLU: return LU; case MU: return U; case MRU: return RU; default: return None; } } /// True if diagonal, false if horizontal/vertical, throws otherwise. public static bool IsDiagonal(int direction) { switch (direction) { case R: case D: case L: case U: return false; case RD: case LD: case LU: case RU: return true; default: throw new ArgumentException("NONE or bad direction"); } } } }