2次元の座標変換

引き続き行列の復習。2次元の座標変換のしくみをPythonで一通り書いてみました。


回転と拡大だけなら2×2の行列で事足りるけど、移動も含めるために3×3の行列に。3列目の(0, 0, 1)は行列計算のための都合で付け足しています。


計算の流れはこのような感じ。移動、拡大縮小、回転の行列は以下のようにつくってみました。

たとえば(x, y)という座標にこのマトリックスを掛け合わせると

X = (x*1) + (y*0) + (1*5) = x+5
Y = (x*0) + (y*1) + (1*2) = y+2

このように余計なものが0で消えて移動値だけが加算されます。

たとえば(x, y)という座標にこのマトリックスを掛け合わせると

X = (x*3) + (y*0) + (1*0) = x*3
Y = (x*0) + (y*2) + (1*0) = y*2

このように余計なものが0で消えてx, yにそれぞれの倍率が掛かります。

たとえば(x, y)という座標にこのマトリックスを掛け合わせると

X = (x*cosθ) + (y*-sinθ) + (1*0) = x*cosθ – ysinθ
Y = (x*sinθ) + (y*cosθ) + (1*0) = x*sinθ + ycosθ

0で座標部分が消え、ちょうど加法定理の計算式になります。

マトリックスはひとまとめに累積できるところも非常に便利なところ。このような仕組みを考えた人は本当に賢いですね。

Pythonで書いたコードです。コードを数行だけ追加してMayaでも確認できるようにしてみました。MelはMayaがないと動かないけど、PythonならMayaを前提とする必要がないので始めやすいです。

import math

# 行列(3行3列)の積
def multMatrix(a, b):
    matrix = [a[0]*b[0]+a[1]*b[3]+a[2]*b[6], a[0]*b[1]+a[1]*b[4]+a[2]*b[7], a[0]*b[2]+a[1]*b[5]+a[2]*b[8],
              a[3]*b[0]+a[4]*b[3]+a[5]*b[6], a[3]*b[1]+a[4]*b[4]+a[5]*b[7], a[3]*b[2]+a[4]*b[5]+a[5]*b[8],
              a[6]*b[0]+a[7]*b[3]+a[8]*b[6], a[6]*b[1]+a[7]*b[4]+a[8]*b[7], a[6]*b[2]+a[7]*b[5]+a[8]*b[8]]
    return matrix

# 単位行列(空の行列)を作る
def createIdentity():
    matrix = [1, 0, 0,
              0, 1, 0,
              0, 0, 1]
    return matrix

# 移動の行列を作る
def createTranslate(v):
    matrix = [1, 0, 0,
              0, 1, 0,
              v[0], v[1], 1]
    return matrix

# 拡大の行列を作る
def createScale(v):
    matrix = [v[0], 0, 0,
              0, v[1], 0,
              0, 0, 1]
    return matrix

# 回転行列を作る
def createRotation(degree):
    theta = math.radians(degree * -1) # Mayaの回転方向に合わせて-1掛けている
    matrix = [math.cos(theta), math.sin(theta), 0,
              -math.sin(theta), math.cos(theta), 0,
              0, 0, 1]
    return matrix

# トランスフォーム
def transform(p, m):
    pos = [p[0]*m[0] + p[1]*m[3] + 1*m[6],
           p[0]*m[1] + p[1]*m[4] + 1*m[7]]
    return pos

# 行列をつくる
world = createIdentity()
world = multMatrix(world, createScale([2, 2]))
world = multMatrix(world, createRotation(30))
world = multMatrix(world, createTranslate([4, 2]))

サンプルコードではスケールを(2, 2)倍して、30度回転し、(4, 2)で移動する行列で各頂点を移動させてみました。

Mayaで確認するために以下のコードを続けて書きます。

# Mayaで確認(選択オブジェクトの各頂点に行列変換をかける)
import maya.cmds as cmds

obj = cmds.ls(sl=True)
if(len(obj)>0):
    obj = cmds.polyListComponentConversion(obj, toVertex=True)
    vertices = cmds.filterExpand(obj, sm=31)
    for i in range(0, len(vertices)):
        pos = cmds.xform(vertices[i], q=True, translation=True, worldSpace=True)
        newPos = transform([pos[0],pos[2]], world)
        cmds.xform(vertices[i], translation=(newPos[0], pos[1], newPos[1]), worldSpace=True)

 

MayaのXZ平面で実行した結果です。回転前と回転後の頂点です。

コメントを残す