File: test_math.py

package info (click to toggle)
pyglet 2.0.17%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 15,560 kB
  • sloc: python: 80,579; xml: 50,988; ansic: 171; makefile: 146
file content (141 lines) | stat: -rw-r--r-- 3,993 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import pytest

from pyglet.math import Mat3, Mat4, Vec3


@pytest.fixture()
def mat3():
    return Mat3()


@pytest.fixture()
def mat4():
    return Mat4()


##################
# helper functions
##################

def inverse(matrix):
    """The inverse of a Matrix.

    Using Gauss-Jordan elimination, the matrix supplied is transformed into
    the identity matrix using a sequence of elementary row operations (below).
    The same sequence of operations is applied to the identity matrix,
    transforming it into the supplied matrix's inverse.
    Elementary row operations:
        - multiply row by a scalar
        - swap two rows
        - add two rows together"""

    i = Mat4()  # identity matrix

    # The pivot in each column is the element at Matrix[c, c] (diagonal elements).
    # The pivot row is the row containing the pivot element. Pivot elements must
    # be non-zero.
    # Any time matrix is changed, so is i.
    for c in range(4):
        # Find and swap pivot row into place
        if matrix[4*c + c] == 0:
            for r in range(c + 1, 4):
                if matrix[4*r + c] != 0:
                    matrix = _row_swap(matrix, c, r)
                    i = _row_swap(i, c, r)

        # Make 0's in column for rows that aren't pivot row
        for r in range(4):
            if r != c:  # not the pivot row
                r_piv = matrix[4*r + c]
                if r_piv != 0:
                    piv = matrix[4*c + c]
                    scalar = r_piv / piv
                    matrix = _row_mul(matrix, c, scalar)
                    matrix = row_sub(matrix, c, r)
                    i = _row_mul(i, c, scalar)
                    i = row_sub(i, c, r)

        # Put matrix in reduced row-echelon form.
        piv = matrix[4*c + c]
        matrix = _row_mul(matrix, c, 1 / piv)
        i = _row_mul(i, c, 1 / piv)
    return i


def _row_swap(matrix, r1, r2):
    lo = min(r1, r2)
    hi = max(r1, r2)
    values = (matrix[:lo*4]
              + matrix[hi*4:hi*4 + 4]
              + matrix[lo*4 + 4:hi*4]
              + matrix[lo*4:lo*4 + 4]
              + matrix[hi*4 + 4:])
    return Mat4(values)


def _row_mul(matrix, r, x):
    values = (matrix[:r*4]
              + tuple(v * x for v in matrix[r*4:r*4 + 4])
              + matrix[r*4 + 4:])
    return Mat4(values)


# subtracts r1 from r2
def row_sub(matrix, r1, r2):
    row1 = matrix[4*r1:4*r1 + 4]
    row2 = matrix[4*r2:4*r2 + 4]
    values = (matrix[:r2*4]
              + tuple(v2 - v1 for (v1, v2) in zip(row1, row2))
              + matrix[r2*4 + 4:])
    return matrix.Mat4(values)


############
# test cases
############
def test_mat3_creation(mat3):
    assert len(mat3) == 9
    assert mat3 == (1, 0, 0,
                    0, 1, 0,
                    0, 0, 1)


def test_mat3_creation_from_list(mat3):
    mat3_from_list = Mat3([1, 0, 0,
                           0, 1, 0,
                           0, 0, 1])
    assert mat3_from_list == mat3


def test_mat4_creation(mat4):
    assert len(mat4) == 16
    assert mat4 == (1, 0, 0, 0,
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1)


def test_mat4_creation_from_list(mat4):
    mat4_from_list = Mat4([1, 0, 0, 0,
                           0, 1, 0, 0,
                           0, 0, 1, 0,
                           0, 0, 0, 1])
    assert mat4_from_list == mat4


def test_mat4_inversion(mat4):
    # Confirm `__invert__` method matches long hand utility method:
    inverse_1 = inverse(mat4)
    inverse_2 = ~mat4
    assert round(inverse_1, 9) == round(inverse_2, 9)
    # Confirm that Matrix @ its inverse == identity Matrix:
    assert round(mat4 @ inverse_1, 9) == Mat4()
    assert round(mat4 @ inverse_2, 9) == Mat4()


def test_mat3_associative_mul():
    swap_xy = Mat3((0,1,0, 1,0,0, 0,0,1))
    scale_x = Mat3((2,0,0, 0,1,0, 0,0,1))
    v1 = (swap_xy @ scale_x) @ Vec3(0,1,0)
    v2 = swap_xy @ (scale_x @ Vec3(0,1,0))
    assert v1 == v2 and abs(v1) != 0