回転行列を使って配列を回転させる

2次元配列を回転させる方法を調べていたら、回転行列っていうのが何なのか気になった。

定義には三角関数が入ってて難しげだけど、Wikipediaによると右に90°回すだけなら簡単な数字で回せるらしい。これに座標を入れたベクトルをかけると回した後の座標が出て来る。

Ruby の matrix ライブラリを使って座標 (0.5, 0) を回転させるとこんなコードになる。

pt = Vector[0.5,0]
4.times { p(pt = Matrix[[0,-1],[1,0]] * pt) }
Vector[0.0, 0.5]
Vector[-0.5, 0.0]
Vector[0.0, -0.5]
Vector[0.5, 0.0]

y 軸は下を向いていて、こういうことらしい。

どうしたらこれを配列の回転に応用できるだろうか? 座標を配列の添え字と考えて、3×3の配列をグラフに貼り付けてみると次のようになる。

回転行列でこれを回転させるとこうなる。

これだと x 座標がマイナスになってしまって都合が悪い。座標が0から始まるように、元の配列の高さの分だけ右に移動しなければならない。

実装したのが次のコード。

require 'matrix'

ROT_MATRIX = Matrix[[0,-1],[1,0]]

# 2次元配列を表示する。
def ap ary
  ary.each do |row|
    puts row.join
  end
  puts
end

# 右に90°回転させる。
def rotate src
  src_height = src.size
  src_width = src[0].size
  # 幅が高さになり高さが幅になる。
  dest = Array.new(src_width) { Array.new(src_height) }

  (0...src_height).each do |y|
    (0...src_width).each do |x|
      xx, yy = ( ROT_MATRIX * Vector[x, y] ).to_a
      dest[yy][xx + src_height - 1] = src[y][x]
    end
  end
  dest
end

# 4×2の配列を回転させてみる。
src = [[1,2,3,4],[5,6,7,8]]

ap src
ap rotate src
ap rotate rotate src
ap rotate rotate rotate src
ap rotate rotate rotate rotate src

実行結果。

1234
5678

51
62
73
84

8765
4321

48
37
26
15

1234
5678

ちゃんと回転した。めでたしめでたし。