import math class EmbMatrix: def __init__(self, m=None): if m is None: self.m = self.get_identity() else: self.m = m def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): return self.m == other.m def __matmul__(self, other): return EmbMatrix(EmbMatrix.matrix_multiply(self.m, other.m)) def __rmatmul__(self, other): return EmbMatrix(EmbMatrix.matrix_multiply(self.m, other.m)) def __imatmul__(self, other): self.m = EmbMatrix.matrix_multiply(self.m, other.m) def __str__(self): return "[%3f, %3f, %3f\n %3f, %3f, %3f\n %3f, %3f, %3f]" % self.m def get_matrix(self): return self.m def reset(self): self.m = self.get_identity() def inverse(self): m = self.m m48s75 = m[4] * m[8] - m[7] * m[5] m38s56 = m[5] * m[6] - m[3] * m[8] m37s46 = m[3] * m[7] - m[4] * m[6] det = m[0] * m48s75 + m[1] * m38s56 + m[2] * m37s46 inverse_det = 1.0 / float(det) self.m = [ m48s75 * inverse_det, (m[2] * m[7] - m[1] * m[8]) * inverse_det, (m[1] * m[5] - m[2] * m[4]) * inverse_det, m38s56 * inverse_det, (m[0] * m[8] - m[2] * m[6]) * inverse_det, (m[3] * m[2] - m[0] * m[5]) * inverse_det, m37s46 * inverse_det, (m[6] * m[1] - m[0] * m[7]) * inverse_det, (m[0] * m[4] - m[3] * m[1]) * inverse_det, ] def post_scale(self, sx=1, sy=None, x=0, y=0): if sy is None: sy = sx if x is None: x = 0 if y is None: y = 0 if x == 0 and y == 0: self.m = self.matrix_multiply(self.m, self.get_scale(sx, sy)) else: self.post_translate(x, y) self.post_scale(sx, sy) self.post_translate(-x, -y) def post_translate(self, tx, ty): self.m = self.matrix_multiply(self.m, self.get_translate(tx, ty)) def post_rotate(self, theta, x=0, y=0): if x is None: x = 0 if y is None: y = 0 if x == 0 and y == 0: self.m = self.matrix_multiply(self.m, self.get_rotate(theta)) else: self.post_translate(x, y) self.post_rotate(theta) self.post_translate(-x, -y) def post_cat(self, matrix_list): for mx in matrix_list: self.m = self.matrix_multiply(self.m, mx) def pre_scale(self, sx=1, sy=None): if sy is None: sy = sx self.m = self.matrix_multiply(self.get_scale(sx, sy), self.m) def pre_translate(self, tx, ty): self.m = self.matrix_multiply(self.get_translate(tx, ty), self.m) def pre_rotate(self, theta): self.m = self.matrix_multiply(self.get_rotate(theta), self.m) def pre_cat(self, matrix_list): for mx in matrix_list: self.m = self.matrix_multiply(mx, self.m) def point_in_matrix_space(self, v0, v1=None): m = self.m if v1 is None: try: return [ v0[0] * m[0] + v0[1] * m[3] + 1 * m[6], v0[0] * m[1] + v0[1] * m[4] + 1 * m[7], v0[2], ] except IndexError: return [ v0[0] * m[0] + v0[1] * m[3] + 1 * m[6], v0[0] * m[1] + v0[1] * m[4] + 1 * m[7] # Must not have had a 3rd element. ] return [v0 * m[0] + v1 * m[3] + 1 * m[6], v0 * m[1] + v1 * m[4] + 1 * m[7]] def apply(self, v): m = self.m nx = v[0] * m[0] + v[1] * m[3] + 1 * m[6] ny = v[0] * m[1] + v[1] * m[4] + 1 * m[7] v[0] = nx v[1] = ny @staticmethod def get_identity(): return 1, 0, 0, 0, 1, 0, 0, 0, 1 # identity @staticmethod def get_scale(sx, sy=None): if sy is None: sy = sx return sx, 0, 0, 0, sy, 0, 0, 0, 1 @staticmethod def get_translate(tx, ty): return 1, 0, 0, 0, 1, 0, tx, ty, 1 @staticmethod def get_rotate(theta): tau = math.pi * 2 theta *= tau / 360 ct = math.cos(theta) st = math.sin(theta) return ct, st, 0, -st, ct, 0, 0, 0, 1 @staticmethod def matrix_multiply(m0, m1): return [ m1[0] * m0[0] + m1[1] * m0[3] + m1[2] * m0[6], m1[0] * m0[1] + m1[1] * m0[4] + m1[2] * m0[7], m1[0] * m0[2] + m1[1] * m0[5] + m1[2] * m0[8], m1[3] * m0[0] + m1[4] * m0[3] + m1[5] * m0[6], m1[3] * m0[1] + m1[4] * m0[4] + m1[5] * m0[7], m1[3] * m0[2] + m1[4] * m0[5] + m1[5] * m0[8], m1[6] * m0[0] + m1[7] * m0[3] + m1[8] * m0[6], m1[6] * m0[1] + m1[7] * m0[4] + m1[8] * m0[7], m1[6] * m0[2] + m1[7] * m0[5] + m1[8] * m0[8], ]