File indexing completed on 2025-01-19 03:59:55

0001 import math
0002 from ..nvector import NVector
0003 
0004 
0005 def _sign(x):
0006     if x < 0:
0007         return -1
0008     return 1
0009 
0010 
0011 class TransformMatrix:
0012     scalar = float
0013 
0014     def __init__(self):
0015         """ Creates an Identity matrix """
0016         self.to_identity()
0017 
0018     def clone(self):
0019         m = TransformMatrix()
0020         m._mat = list(self._mat)
0021         return m
0022 
0023         return self
0024 
0025     def __getitem__(self, key):
0026         row, col = key
0027         return self._mat[row*4+col]
0028 
0029     def __setitem__(self, key, value):
0030         row, col = key
0031         self._mat[row*4+col] = self.scalar(value)
0032 
0033     @property
0034     def a(self):
0035         return self[0, 0]
0036 
0037     @a.setter
0038     def a(self, v):
0039         self[0, 0] = self.scalar(v)
0040 
0041     @property
0042     def b(self):
0043         return self[0, 1]
0044 
0045     @b.setter
0046     def b(self, v):
0047         self[0, 1] = self.scalar(v)
0048 
0049     @property
0050     def c(self):
0051         return self[1, 0]
0052 
0053     @c.setter
0054     def c(self, v):
0055         self[1, 0] = self.scalar(v)
0056 
0057     @property
0058     def d(self):
0059         return self[1, 1]
0060 
0061     @d.setter
0062     def d(self, v):
0063         self[1, 1] = self.scalar(v)
0064 
0065     @property
0066     def tx(self):
0067         return self[3, 0]
0068 
0069     @tx.setter
0070     def tx(self, v):
0071         self[3, 0] = self.scalar(v)
0072 
0073     @property
0074     def ty(self):
0075         return self[3, 1]
0076 
0077     @ty.setter
0078     def ty(self, v):
0079         self[3, 1] = self.scalar(v)
0080 
0081     def __str__(self):
0082         return str(self._mat)
0083 
0084     def scale(self, x, y=None):
0085         if y is None:
0086             y = x
0087 
0088         m = TransformMatrix()
0089         m.a = x
0090         m.d = y
0091         self *= m
0092         return self
0093 
0094     def translate(self, x, y=None):
0095         if y is None:
0096             x, y = x
0097         m = TransformMatrix()
0098         m.tx = x
0099         m.ty = y
0100         self *= m
0101         return self
0102 
0103     def skew(self, x_rad, y_rad):
0104         m = TransformMatrix()
0105         m.c = math.tan(x_rad)
0106         m.b = math.tan(y_rad)
0107         self *= m
0108         return self
0109 
0110     def skew_from_axis(self, skew, axis):
0111         self.rotate(axis)
0112         m = TransformMatrix()
0113         m.c = math.tan(skew)
0114         self *= m
0115         self.rotate(-axis)
0116         return self
0117 
0118     def row(self, i):
0119         return NVector(self[i, 0], self[i, 1], self[i, 2], self[i, 3])
0120 
0121     def column(self, i):
0122         return NVector(self[0, i], self[1, i], self[2, i], self[3, i])
0123 
0124     def to_identity(self):
0125         self._mat = [
0126             1., 0., 0., 0.,
0127             0., 1., 0., 0.,
0128             0., 0., 1., 0.,
0129             0., 0., 0., 1.,
0130         ]
0131 
0132     def apply(self, vector):
0133         vector3 = NVector(vector.x, vector.y, 0, 1)
0134         return NVector(
0135             self.column(0).dot(vector3),
0136             self.column(1).dot(vector3),
0137         )
0138 
0139     @classmethod
0140     def rotation(cls, radians):
0141         m = cls()
0142         m.a = math.cos(radians)
0143         m.b = -math.sin(radians)
0144         m.c = math.sin(radians)
0145         m.d = math.cos(radians)
0146 
0147         return m
0148 
0149     def __mul__(self, other):
0150         m = TransformMatrix()
0151         for row in range(4):
0152             for col in range(4):
0153                 m[row, col] = self.row(row).dot(other.column(col))
0154         return m
0155 
0156     def __imul__(self, other):
0157         m = self * other
0158         self._mat = m._mat
0159         return self
0160 
0161     def rotate(self, radians):
0162         self *= TransformMatrix.rotation(radians)
0163         return self
0164 
0165     def extract_transform(self):
0166         a = self.a
0167         b = self.b
0168         c = self.c
0169         d = self.d
0170         tx = self.tx
0171         ty = self.ty
0172 
0173         dest_trans = {
0174             "translation": NVector(tx, ty),
0175             "angle": 0,
0176             "scale": NVector(1, 1),
0177             "skew_axis": 0,
0178             "skew_angle": 0,
0179         }
0180 
0181         delta = a * d - b * c
0182         if a != 0 or b != 0:
0183             r = math.hypot(a, b)
0184             dest_trans["angle"] = - _sign(b) * math.acos(a/r)
0185             sx = r
0186             sy = delta / r
0187             dest_trans["skew_axis"] = 0
0188         else:
0189             r = math.hypot(c, d)
0190             dest_trans["angle"] = math.pi / 2 + _sign(d) * math.acos(c / r)
0191             sx = delta / r
0192             sy = r
0193             dest_trans["skew_axis"] = math.pi / 2
0194 
0195         dest_trans["scale"] = NVector(sx, sy)
0196 
0197         skew = math.atan2((a * c + b * d), r * r)
0198         dest_trans["skew_angle"] = skew
0199 
0200         return dest_trans
0201 
0202     def to_css_2d(self):
0203         return "matrix(%s, %s, %s, %s, %s, %s)" % (
0204             self.a, self.b, self.c, self.d, self.tx, self.ty
0205         )