import math from ..nvector import NVector def _sign(x): if x < 0: return -1 return 1 class TransformMatrix: scalar = float def __init__(self): """ Creates an Identity matrix """ self.to_identity() def clone(self): m = TransformMatrix() m._mat = list(self._mat) return m return self def __getitem__(self, key): row, col = key return self._mat[row*4+col] def __setitem__(self, key, value): row, col = key self._mat[row*4+col] = self.scalar(value) @property def a(self): return self[0, 0] @a.setter def a(self, v): self[0, 0] = self.scalar(v) @property def b(self): return self[0, 1] @b.setter def b(self, v): self[0, 1] = self.scalar(v) @property def c(self): return self[1, 0] @c.setter def c(self, v): self[1, 0] = self.scalar(v) @property def d(self): return self[1, 1] @d.setter def d(self, v): self[1, 1] = self.scalar(v) @property def tx(self): return self[3, 0] @tx.setter def tx(self, v): self[3, 0] = self.scalar(v) @property def ty(self): return self[3, 1] @ty.setter def ty(self, v): self[3, 1] = self.scalar(v) def __str__(self): return str(self._mat) def scale(self, x, y=None): if y is None: y = x m = TransformMatrix() m.a = x m.d = y self *= m return self def translate(self, x, y=None): if y is None: x, y = x m = TransformMatrix() m.tx = x m.ty = y self *= m return self def skew(self, x_rad, y_rad): m = TransformMatrix() m.c = math.tan(x_rad) m.b = math.tan(y_rad) self *= m return self def skew_from_axis(self, skew, axis): self.rotate(axis) m = TransformMatrix() m.c = math.tan(skew) self *= m self.rotate(-axis) return self def row(self, i): return NVector(self[i, 0], self[i, 1], self[i, 2], self[i, 3]) def column(self, i): return NVector(self[0, i], self[1, i], self[2, i], self[3, i]) def to_identity(self): self._mat = [ 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., ] def apply(self, vector): vector3 = NVector(vector.x, vector.y, 0, 1) return NVector( self.column(0).dot(vector3), self.column(1).dot(vector3), ) @classmethod def rotation(cls, radians): m = cls() m.a = math.cos(radians) m.b = -math.sin(radians) m.c = math.sin(radians) m.d = math.cos(radians) return m def __mul__(self, other): m = TransformMatrix() for row in range(4): for col in range(4): m[row, col] = self.row(row).dot(other.column(col)) return m def __imul__(self, other): m = self * other self._mat = m._mat return self def rotate(self, radians): self *= TransformMatrix.rotation(radians) return self def extract_transform(self): a = self.a b = self.b c = self.c d = self.d tx = self.tx ty = self.ty dest_trans = { "translation": NVector(tx, ty), "angle": 0, "scale": NVector(1, 1), "skew_axis": 0, "skew_angle": 0, } delta = a * d - b * c if a != 0 or b != 0: r = math.hypot(a, b) dest_trans["angle"] = - _sign(b) * math.acos(a/r) sx = r sy = delta / r dest_trans["skew_axis"] = 0 else: r = math.hypot(c, d) dest_trans["angle"] = math.pi / 2 + _sign(d) * math.acos(c / r) sx = delta / r sy = r dest_trans["skew_axis"] = math.pi / 2 dest_trans["scale"] = NVector(sx, sy) skew = math.atan2((a * c + b * d), r * r) dest_trans["skew_angle"] = skew return dest_trans def to_css_2d(self): return "matrix(%s, %s, %s, %s, %s, %s)" % ( self.a, self.b, self.c, self.d, self.tx, self.ty )