Difficult-Rocket/pyglet/math.py
2021-05-24 22:28:11 +08:00

280 lines
10 KiB
Python

# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2008 Alex Holkner
# Copyright (c) 2008-2021 pyglet contributors
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of pyglet nor the names of its
# contributors may be used to endorse or promote products
# derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------
"""Matrix and Vector operations.
This module provides classes for Matrix and Vector math.
A :py:class:`~pyglet.matrix.Mat4` class is available for representing
4x4 matricies, including helper methods for rotating, scaling, and
transforming. The internal datatype of :py:class:`~pyglet.matrix.Mat4`
is a 1-dimensional array, so instances can be passed directly to OpenGL.
"""
import math as _math
import operator as _operator
import warnings as _warnings
def create_orthogonal(left, right, bottom, top, znear, zfar):
"""Create a Mat4 orthographic projection matrix."""
width = right - left
height = top - bottom
depth = zfar - znear
sx = 2.0 / width
sy = 2.0 / height
sz = 2.0 / -depth
tx = -(right + left) / width
ty = -(top + bottom) / height
tz = -(zfar + znear) / depth
return Mat4((sx, 0.0, 0.0, 0.0,
0.0, sy, 0.0, 0.0,
0.0, 0.0, sz, 0.0,
tx, ty, tz, 1.0))
def create_perspective(left, right, bottom, top, znear, zfar, fov=60):
"""Create a Mat4 perspective projection matrix."""
width = right - left
height = top - bottom
aspect = width / height
xymax = znear * _math.tan(fov * _math.pi / 360)
ymin = -xymax
xmin = -xymax
width = xymax - xmin
height = xymax - ymin
depth = zfar - znear
q = -(zfar + znear) / depth
qn = -2 * zfar * znear / depth
w = 2 * znear / width
w = w / aspect
h = 2 * znear / height
return Mat4((w, 0, 0, 0,
0, h, 0, 0,
0, 0, q, -1,
0, 0, qn, 0))
class Mat4(tuple):
"""A 4x4 Matrix
`Mat4` is a simple immutable 4x4 Matrix, with a few operators.
Two types of multiplication are possible. The "*" operator
will perform elementwise multiplication, wheras the "@"
operator will perform Matrix multiplication. Internally,
data is stored in a linear 1D array, allowing direct passing
to OpenGL.
"""
def __new__(cls, values=None):
"""Create a 4x4 Matrix
A Matrix can be created with a list or tuple of 16 values.
If no values are provided, an "identity matrix" will be created
(1.0 on the main diagonal). Matrix objects are immutable, so
all operations return a new Mat4 object.
:Parameters:
`values` : tuple of float or int
A tuple or list containing 16 floats or ints.
"""
assert values is None or len(values) == 16, "A 4x4 Matrix requires 16 values"
return super().__new__(Mat4, values or (1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0))
def row(self, index):
"""Get a specific row as a tuple."""
return self[index*4:index*4+4]
def column(self, index):
"""Get a specific column as a tuple."""
return self[index::4]
def scale(self, x=1, y=1, z=1):
"""Get a scale Matrix on x, y, or z axis."""
temp = list(self)
temp[0] *= x
temp[5] *= y
temp[10] *= z
return Mat4(temp)
def translate(self, x=0, y=0, z=0):
"""Get a translate Matrix along x, y, and z axis."""
return Mat4(self) @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1))
def rotate(self, angle=0, x=0, y=0, z=0):
"""Get a rotation Matrix on x, y, or z axis."""
assert all(abs(n) <= 1 for n in (x, y, z)), "x,y,z must be normalized (<=1)"
c = _math.cos(angle)
s = _math.sin(angle)
t = 1 - c
tempx, tempy, tempz = t * x, t * y, t * z
ra = c + tempx * x
rb = 0 + tempx * y + s * z
rc = 0 + tempx * z - s * y
re = 0 + tempy * x - s * z
rf = c + tempy * y
rg = 0 + tempy * z + s * x
ri = 0 + tempz * x + s * y
rj = 0 + tempz * y - s * x
rk = c + tempz * z
# ra, rb, rc, --
# re, rf, rg, --
# ri, rj, rk, --
# --, --, --, --
return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1))
def transpose(self):
"""Get a tranpose of this Matrix."""
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
def __add__(self, other):
assert len(other) == 16, "Can only add to other Mat4 types"
return Mat4(tuple(s + o for s, o in zip(self, other)))
def __sub__(self, other):
assert len(other) == 16, "Can only subtract from other Mat4 types"
return Mat4(tuple(s - o for s, o in zip(self, other)))
def __pos__(self):
return self
def __neg__(self):
return Mat4(tuple(-v for v in self))
def __invert__(self):
a = self[10] * self[15] - self[11] * self[14]
b = self[9] * self[15] - self[11] * self[13]
c = self[9] * self[14] - self[10] * self[13]
d = self[8] * self[15] - self[11] * self[12]
e = self[8] * self[14] - self[10] * self[12]
f = self[8] * self[13] - self[9] * self[12]
g = self[6] * self[15] - self[7] * self[14]
h = self[5] * self[15] - self[7] * self[13]
i = self[5] * self[14] - self[6] * self[13]
j = self[6] * self[11] - self[7] * self[10]
k = self[5] * self[11] - self[7] * self[9]
l = self[5] * self[10] - self[6] * self[9]
m = self[4] * self[15] - self[7] * self[12]
n = self[4] * self[14] - self[6] * self[12]
o = self[4] * self[11] - self[7] * self[8]
p = self[4] * self[10] - self[6] * self[8]
q = self[4] * self[13] - self[5] * self[12]
r = self[4] * self[9] - self[5] * self[8]
det = (self[0] * (self[5] * a - self[6] * b + self[7] * c)
- self[1] * (self[4] * a - self[6] * d + self[7] * e)
+ self[2] * (self[4] * b - self[5] * d + self[7] * f)
- self[3] * (self[4] * c - self[5] * e + self[6] * f))
if det == 0:
_warnings.warn("Unable to calculate inverse of singular Matrix")
return self
pdet = 1 / det
ndet = -pdet
return Mat4((pdet * (self[5] * a - self[6] * b + self[7] * c),
ndet * (self[1] * a - self[2] * b + self[3] * c),
pdet * (self[1] * g - self[2] * h + self[3] * i),
ndet * (self[1] * j - self[2] * k + self[3] * l),
ndet * (self[4] * a - self[6] * d + self[7] * e),
pdet * (self[0] * a - self[2] * d + self[3] * e),
ndet * (self[0] * g - self[2] * m + self[3] * n),
pdet * (self[0] * j - self[2] * o + self[3] * p),
pdet * (self[4] * b - self[5] * d + self[7] * f),
ndet * (self[0] * b - self[1] * d + self[3] * f),
pdet * (self[0] * h - self[1] * m + self[3] * q),
ndet * (self[0] * k - self[1] * o + self[3] * r),
ndet * (self[4] * c - self[5] * e + self[6] * f),
pdet * (self[0] * c - self[1] * e + self[2] * f),
ndet * (self[0] * i - self[1] * n + self[2] * q),
pdet * (self[0] * l - self[1] * p + self[2] * r)))
def __round__(self, n=None):
return Mat4(tuple(round(v, n) for v in self))
def __mul__(self, other):
raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
def __matmul__(self, other):
assert len(other) == 16, "Can only multiply with other Mat4 types"
# Rows:
r0 = self[0:4]
r1 = self[4:8]
r2 = self[8:12]
r3 = self[12:16]
# Columns:
c0 = other[0::4]
c1 = other[1::4]
c2 = other[2::4]
c3 = other[3::4]
# Multiply and sum rows * colums:
return Mat4((sum(map(_operator.mul, r0, c0)),
sum(map(_operator.mul, r0, c1)),
sum(map(_operator.mul, r0, c2)),
sum(map(_operator.mul, r0, c3)),
sum(map(_operator.mul, r1, c0)),
sum(map(_operator.mul, r1, c1)),
sum(map(_operator.mul, r1, c2)),
sum(map(_operator.mul, r1, c3)),
sum(map(_operator.mul, r2, c0)),
sum(map(_operator.mul, r2, c1)),
sum(map(_operator.mul, r2, c2)),
sum(map(_operator.mul, r2, c3)),
sum(map(_operator.mul, r3, c0)),
sum(map(_operator.mul, r3, c1)),
sum(map(_operator.mul, r3, c2)),
sum(map(_operator.mul, r3, c3))))
def __repr__(self):
return f"{self.__class__.__name__}{self[0:4]}\n {self[4:8]}\n {self[8:12]}\n {self[12:16]}"