マウスで線を引く(Pythonでリストのリストのアイテムの値を変える)

OpenGLで絵を描く練習をいろいろとやっている。
GLUTによる「手抜き」OpenGL入門を読みながら、サンプルコードをPythonコードに変換している。今回は、マウスで画面に線を引くサンプルコードを変換した。
ここで、マウスでドラッグする始点と終点をpointというリストに入れているのだが、リストのリスト(2次元配列ですね)が予期せぬ挙動を示したので少しコード変換に手間取った。問題だった点は最後に書く。最初は、今回のOpenGLを使ったPythonコード。

PyOpenGLを使ったコード

from OpenGL.GL import *
from OpenGL.GLUT import *

MAXPOINTS = 100
point = [[0, 0]]*MAXPOINTS  #リストの初期化
pointnum = 0

def display():
    glClear(GL_COLOR_BUFFER_BIT)
    if pointnum > 1:
        glColor3d(0.0, 0.0, 0.0)
        glBegin(GL_LINES)
        for i in range(pointnum):
            glVertex2iv(point[i])
        glEnd()
    glFlush()

def mouse(button, state, x, y):
    global point
    global pointnum
    if button == GLUT_LEFT_BUTTON:
        point[pointnum] = [x,y]
#       point[pointnum][0] = x   # 最初はこう書いた。これだと動かない
#       point[pointnum][1] = y   # 最初はこう書いた。これだと動かない
        if state == GLUT_UP:
            glColor3d(0.0, 0.0, 0.0)
            glBegin(GL_LINES)
            glVertex2iv(point[pointnum-1])
            glVertex2iv(point[pointnum])
            glEnd()
            glFlush()
        if pointnum < MAXPOINTS-1:
            pointnum += 1
        else:
            pass

def resize(w, h):
    glViewport(0, 0, w, h)
    glLoadIdentity()
    glOrtho(-0.5, float(w)-0.5, float(h)-0.5, -0.5, -1.0, 1.0)

def init():
    glClearColor(1.0, 1.0, 1.0, 1.0)

glutInitWindowPosition(100, 100)
glutInitWindowSize(320, 240)
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA)
glutCreateWindow("sample")
glutDisplayFunc(display)
glutReshapeFunc(resize)
glutMouseFunc(mouse)
init()
glutMainLoop()


実行すると、このようにマウスでドラッグしたところに線を描く。

リストのリストの扱いで手間取ったところ

コードの中でコメントしたように、変数pointをリストのリストとして使っている。
リストのリストの簡単なテストをしてみる。

>>> list1 = [[0,1], [2,3]]
>>> list1
[[0, 1], [2, 3]]
>>> list1[0][1]
1
>>> list1[0][1] = 6
>>> list1
[[0, 6], [2, 3]]
>>>

リストのリストのアイテムに値を代入した。これは期待通り。次に初期化してから値を変えてみる。このとき予期せぬ挙動...。

>>> list2 = [[0, 0]]*2   <--初期化(2次元配列の初期化)
>>> list2
[[0, 0], [0, 0]]
>>> list2[0][1]
0
>>> list2[0][1] = 5   <--代入した
>>> list2
[[0, 5], [0, 5]]     <--え?!なんで全部のアイテムが変わるの?
>>> list2[0] = [2,3]   <--これはOK。問題なし
>>> list2
[[2, 3], [0, 5]]
>>>

こんなふうになるとは、今まで知らなかった。初期化の方法が原因なのだろうが、どういう理屈かな。要素の数が多いときには便利な初期化方法なのだが。