doc/transformations.py
07a62564
 #!/usr/bin/env python3
 
 from sympy import *
 
 
 def mat(*args):
     return Matrix(list(map(list, args))).T
 
 def vec(*args):
     return Matrix(args)
 
 def normalize(v):
     return v / sqrt(v.dot(v))
 
 def elementwise(a, b, f):
     return Matrix([
         [
             f(a[r, c], b[r, c])
             for c in range(a.cols)
         ]
         for r in range(a.rows)
     ])
 
 def mul(a, b):
     return elementwise(a, b, lambda a, b: a * b)
 
 def div(a, b):
     return elementwise(a, b, lambda a, b: a / b)
 
 
 def window(viewport, depth_range):
     size = vec(*viewport[2:4], depth_range[1]-depth_range[0])
     offs = vec(*viewport[0:2],                depth_range[0])
     s = 0.5 * size
     t = 0.5 * size + offs
     return Matrix([
         [s[0], 0,    0,    t[0]],
         [0,    s[1], 0,    t[1]],
         [0,    0,    s[2], t[2]],
         [0,    0,    0,    1],
     ])
 
 def ortho(left, right, bottom, top, near, far):
     size = vec(right-left, top-bottom, far-near)
     offs = vec(     -left,    -bottom,    +near)
     s = div(vec(2, 2, -2), size)
     t = mul(s, offs) - vec(1, 1, 1)
     return Matrix([
         [s[0], 0,    0,    t[0]],
         [0,    s[1], 0,    t[1]],
         [0,    0,    s[2], t[2]],
         [0,    0,    0,    1],
     ])
 
 def frustum(left, right, bottom, top, near, far):
     z = vec(0, 0, near+far, -1)
     w = vec(0, 0, near*far,  0)
     return ortho(left, right, bottom, top, near, far) * Matrix([
         [near, 0,    z[0], z[0]],
         [0,    near, z[1], w[1]],
         [0,    0,    z[2], w[2]],
         [0,    0,    z[3], w[3]],
     ])
 
 def perspective(fovy, aspect, near, far):
     y = near * tan(0.5 * fovy)
     x = y * aspect
     return frustum(-x, +x, -y, +y, near, far)
 
 def lookat(position, target, up):
     z = normalize(position - target)
     x = normalize(up.cross(z))
     y = z.cross(x)
     R_inv = mat(x, y, z).T
     t_inv = -position
     return mat(R_inv.row(0), R_inv.row(0), R_inv.row(0), R_inv * t_inv)
 
 
 # https://registry.khronos.org/OpenGL-Refpages/gl4/html/glViewport.xhtml
 # https://registry.khronos.org/OpenGL-Refpages/gl4/html/glDepthRange.xhtml
 # https://www.khronos.org/opengl/wiki/Vertex_Post-Processing#Viewport_transform
 # https://www.songho.ca/opengl/gl_viewport.html
 viewport    = symbols('x, y, width, height')
 depth_range = symbols('near, far')
 pprint(simplify(window(viewport, depth_range)))
 # ⎡0.5⋅width      0               0             0.5⋅width + x   ⎤
 # ⎢                                                             ⎥
 # ⎢    0      0.5⋅height          0             0.5⋅height + y  ⎥
 # ⎢                                                             ⎥
 # ⎢    0          0       0.5⋅far - 0.5⋅near  0.5⋅far + 0.5⋅near⎥
 # ⎢                                                             ⎥
 # ⎣    0          0               0                   1         ⎦
 
 # https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml
 # https://www.songho.ca/opengl/gl_projectionmatrix.html#ortho
 left, right, bottom, top, near, far = symbols('left, right, bottom, top, near, far')
 pprint(simplify(ortho(left, right, bottom, top, near, far)))
 # ⎡    -2                                  left + right⎤
 # ⎢────────────       0            0       ────────────⎥
 # ⎢left - right                            left - right⎥
 # ⎢                                                    ⎥
 # ⎢                  -2                    bottom + top⎥
 # ⎢     0        ────────────      0       ────────────⎥
 # ⎢              bottom - top              bottom - top⎥
 # ⎢                                                    ⎥
 # ⎢                               -2       -far - near ⎥
 # ⎢     0             0        ──────────  ─────────── ⎥
 # ⎢                            far - near   far - near ⎥
 # ⎢                                                    ⎥
 # ⎣     0             0            0            1      ⎦
 
 # https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml
 # https://www.songho.ca/opengl/gl_projectionmatrix.html#perspective
 left, right, bottom, top, near, far = symbols('left, right, bottom, top, near, far')
 pprint(simplify(frustum(left, right, bottom, top, near, far)))
 # ⎡  -2⋅near                   -left - right              ⎤
 # ⎢────────────       0        ─────────────       0      ⎥
 # ⎢left - right                 left - right              ⎥
 # ⎢                                                       ⎥
 # ⎢                -2⋅near     -bottom - top              ⎥
 # ⎢     0        ────────────  ─────────────       0      ⎥
 # ⎢              bottom - top   bottom - top              ⎥
 # ⎢                                                       ⎥
 # ⎢                             -far - near   -2⋅far⋅near ⎥
 # ⎢     0             0         ───────────   ────────────⎥
 # ⎢                              far - near    far - near ⎥
 # ⎢                                                       ⎥
 # ⎣     0             0             -1             0      ⎦
 
 # https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
 # https://www.songho.ca/opengl/gl_projectionmatrix.html#fov
 fovy, aspect, near, far = symbols('fovy, aspect, near, far')
 pprint(simplify(perspective(fovy, aspect, near, far)))
 # ⎡         1                                                    ⎤
 # ⎢────────────────────        0             0            0      ⎥
 # ⎢aspect⋅tan(0.5⋅fovy)                                          ⎥
 # ⎢                                                              ⎥
 # ⎢                            1                                 ⎥
 # ⎢         0            ─────────────       0            0      ⎥
 # ⎢                      tan(0.5⋅fovy)                           ⎥
 # ⎢                                                              ⎥
 # ⎢                                     -far - near  -2⋅far⋅near ⎥
 # ⎢         0                  0        ───────────  ────────────⎥
 # ⎢                                      far - near   far - near ⎥
 # ⎢                                                              ⎥
 # ⎣         0                  0            -1            0      ⎦
 
 # https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml
 # https://www.songho.ca/opengl/gl_camera.html#lookat
 position = Matrix(MatrixSymbol('position', 3, 1))
 target   = Matrix(MatrixSymbol('target',   3, 1))
 up       = Matrix(MatrixSymbol('up',       3, 1))
 # pprint(simplify(lookat(position, target, up)))