てくメモ

trivial な notes

【C#】一次元配列を二次元に回転

width と height のあるデータを一次元で扱っているとき、それを回転することを考える。

なお、並べ替え先に外部領域を使う。

また、記事名は配列としているが、記事内のコードはSpanとして扱っている。


参考イメージ画像。

右回転

右90°

行と列が入れ替わる。
並びは、Y軸は逆順・X軸は正順

public static void Rotate2dRight<T>(scoped ReadOnlySpan<T> source, int width, int height, scoped Span<T> destination)
{
    int i = 0;
    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            var row = (height - y - 1) * width;
            destination[i++] = source[row + x];
        }
    }
}

左回転

左90°

行と列が入れ替わる。
並びは、Y軸は正順・X軸は逆順

public static void Rotate2dLeft<T>(scoped ReadOnlySpan<T> source, int width, int height, scoped Span<T> destination)
{
    int i = 0;
    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            var row = y * width;
            destination[i++] = source[row + (width - x - 1)];
        }
    }
}

180°回転

180°

全体を逆順にするだけ。

public static void Rotate2d180<T>(scoped ReadOnlySpan<T> source, scoped Span<T> destination)
{
    source.CopyTo(destination);
    destination[..source.Length].Reverse();
}

左右(X軸)反転

ついでに反転も。

左右反転

回転と異なり、行と列が入れ替わらない。
X軸を逆順にするだけ。

public static void Invert2dX<T>(scoped ReadOnlySpan<T> source, int width, int height, scoped Span<T> destination)
{
    for (int y = 0; y < height; y++)
    {
        var row = y * width;
        source.Slice(row, width).CopyTo(destination);
        destination[..width].Reverse();
        destination = destination[width..];
    }
}

上下(Y軸)反転

上下反転

Y軸を逆順にするだけ。

public static void Invert2dY<T>(scoped ReadOnlySpan<T> source, int width, int height, scoped Span<T> destination)
{
    for (int y = 0; y < height; y++)
    {
        var row = (height - y - 1) * width;
        source.Slice(row, width).CopyTo(destination);
        destination = destination[width..];
    }
}