xref: /aosp_15_r20/external/fonttools/Tests/misc/transform_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1from fontTools.misc.transform import (
2    Transform,
3    Identity,
4    Offset,
5    Scale,
6    DecomposedTransform,
7)
8import math
9import pytest
10
11
12class TransformTest(object):
13    def test_examples(self):
14        t = Transform()
15        assert repr(t) == "<Transform [1 0 0 1 0 0]>"
16        assert t.scale(2) == Transform(2, 0, 0, 2, 0, 0)
17        assert t.scale(2.5, 5.5) == Transform(2.5, 0, 0, 5.5, 0, 0)
18        assert t.scale(2, 3).transformPoint((100, 100)) == (200, 300)
19
20    def test__init__(self):
21        assert Transform(12) == Transform(12, 0, 0, 1, 0, 0)
22        assert Transform(dx=12) == Transform(1, 0, 0, 1, 12, 0)
23        assert Transform(yx=12) == Transform(1, 0, 12, 1, 0, 0)
24
25    def test_transformPoints(self):
26        t = Transform(2, 0, 0, 3, 0, 0)
27        assert t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)]) == [
28            (0, 0),
29            (0, 300),
30            (200, 300),
31            (200, 0),
32        ]
33
34    def test_transformVector(self):
35        t = Transform(2, 0, 0, 3, -10, 30)
36        assert t.transformVector((-4, 5)) == (-8, 15)
37
38    def test_transformVectors(self):
39        t = Transform(2, 0, 0, 3, -10, 30)
40        assert t.transformVectors([(-4, 5), (-6, 7)]) == [(-8, 15), (-12, 21)]
41
42    def test_translate(self):
43        t = Transform()
44        assert t.translate(20, 30) == Transform(1, 0, 0, 1, 20, 30)
45
46    def test_scale(self):
47        t = Transform()
48        assert t.scale(5) == Transform(5, 0, 0, 5, 0, 0)
49        assert t.scale(5, 6) == Transform(5, 0, 0, 6, 0, 0)
50
51    def test_rotate(self):
52        t = Transform()
53        assert t.rotate(math.pi / 2) == Transform(0, 1, -1, 0, 0, 0)
54        t = Transform()
55        assert t.rotate(-math.pi / 2) == Transform(0, -1, 1, 0, 0, 0)
56        t = Transform()
57        assert tuple(t.rotate(math.radians(30))) == pytest.approx(
58            tuple(Transform(0.866025, 0.5, -0.5, 0.866025, 0, 0))
59        )
60
61    def test_skew(self):
62        t = Transform().skew(math.pi / 4)
63        assert tuple(t) == pytest.approx(tuple(Transform(1, 0, 1, 1, 0, 0)))
64
65    def test_transform(self):
66        t = Transform(2, 0, 0, 3, 1, 6)
67        assert t.transform((4, 3, 2, 1, 5, 6)) == Transform(8, 9, 4, 3, 11, 24)
68
69    def test_reverseTransform(self):
70        t = Transform(2, 0, 0, 3, 1, 6)
71        reverse_t = t.reverseTransform((4, 3, 2, 1, 5, 6))
72        assert reverse_t == Transform(8, 6, 6, 3, 21, 15)
73        t = Transform(4, 3, 2, 1, 5, 6)
74        reverse_t = t.transform((2, 0, 0, 3, 1, 6))
75        assert reverse_t == Transform(8, 6, 6, 3, 21, 15)
76
77    def test_inverse(self):
78        t = Transform().translate(2, 3).scale(4, 5)
79        assert t.transformPoint((10, 20)) == (42, 103)
80        it = t.inverse()
81        assert it.transformPoint((42, 103)) == (10.0, 20.0)
82        assert Transform().inverse() == Transform()
83
84    def test_toPS(self):
85        t = Transform().scale(2, 3).translate(4, 5)
86        assert t.toPS() == "[2 0 0 3 8 15]"
87
88    def test__ne__(self):
89        assert Transform() != Transform(2, 0, 0, 2, 0, 0)
90
91    def test__hash__(self):
92        t = Transform(12, 0, 0, 13, 0, 0)
93        d = {t: None}
94        assert t in d.keys()
95
96    def test__bool__(self):
97        assert not bool(Transform())
98        assert Transform(2, 0, 0, 2, 0, 0)
99        assert Transform(1, 0, 0, 1, 1, 0)
100
101    def test__repr__(self):
102        assert repr(Transform(1, 2, 3, 4, 5, 6)) == "<Transform [1 2 3 4 5 6]>"
103
104    def test_Identity(self):
105        assert isinstance(Identity, Transform)
106        assert Identity == Transform(1, 0, 0, 1, 0, 0)
107
108    def test_Offset(self):
109        assert Offset() == Transform(1, 0, 0, 1, 0, 0)
110        assert Offset(1) == Transform(1, 0, 0, 1, 1, 0)
111        assert Offset(1, 2) == Transform(1, 0, 0, 1, 1, 2)
112
113    def test_Scale(self):
114        assert Scale(1) == Transform(1, 0, 0, 1, 0, 0)
115        assert Scale(2) == Transform(2, 0, 0, 2, 0, 0)
116        assert Scale(1, 2) == Transform(1, 0, 0, 2, 0, 0)
117
118    def test_decompose(self):
119        t = Transform(2, 0, 0, 3, 5, 7)
120        d = t.toDecomposed()
121        assert d.scaleX == 2
122        assert d.scaleY == 3
123        assert d.translateX == 5
124        assert d.translateY == 7
125
126    def test_decompose(self):
127        t = Transform(-1, 0, 0, 1, 0, 0)
128        d = t.toDecomposed()
129        assert d.scaleX == -1
130        assert d.scaleY == 1
131        assert d.rotation == 0
132
133        t = Transform(1, 0, 0, -1, 0, 0)
134        d = t.toDecomposed()
135        assert d.scaleX == 1
136        assert d.scaleY == -1
137        assert d.rotation == 0
138
139
140class DecomposedTransformTest(object):
141    def test_identity(self):
142        t = DecomposedTransform()
143        assert (
144            repr(t)
145            == "DecomposedTransform(translateX=0, translateY=0, rotation=0, scaleX=1, scaleY=1, skewX=0, skewY=0, tCenterX=0, tCenterY=0)"
146        )
147        assert t == DecomposedTransform(scaleX=1.0)
148
149    def test_scale(self):
150        t = DecomposedTransform(scaleX=2, scaleY=3)
151        assert t.scaleX == 2
152        assert t.scaleY == 3
153
154    def test_toTransform(self):
155        t = DecomposedTransform(scaleX=2, scaleY=3)
156        assert t.toTransform() == (2, 0, 0, 3, 0, 0)
157
158    @pytest.mark.parametrize(
159        "decomposed",
160        [
161            DecomposedTransform(scaleX=1, scaleY=0),
162            DecomposedTransform(scaleX=0, scaleY=1),
163            DecomposedTransform(scaleX=1, scaleY=0, rotation=30),
164            DecomposedTransform(scaleX=0, scaleY=1, rotation=30),
165            DecomposedTransform(scaleX=1, scaleY=1),
166            DecomposedTransform(scaleX=-1, scaleY=1),
167            DecomposedTransform(scaleX=1, scaleY=-1),
168            DecomposedTransform(scaleX=-1, scaleY=-1),
169            DecomposedTransform(rotation=90),
170            DecomposedTransform(rotation=-90),
171            DecomposedTransform(skewX=45),
172            DecomposedTransform(skewY=45),
173            DecomposedTransform(scaleX=-1, skewX=45),
174            DecomposedTransform(scaleX=-1, skewY=45),
175            DecomposedTransform(scaleY=-1, skewX=45),
176            DecomposedTransform(scaleY=-1, skewY=45),
177            DecomposedTransform(scaleX=-1, skewX=45, rotation=30),
178            DecomposedTransform(scaleX=-1, skewY=45, rotation=30),
179            DecomposedTransform(scaleY=-1, skewX=45, rotation=30),
180            DecomposedTransform(scaleY=-1, skewY=45, rotation=30),
181            DecomposedTransform(scaleX=-1, skewX=45, rotation=-30),
182            DecomposedTransform(scaleX=-1, skewY=45, rotation=-30),
183            DecomposedTransform(scaleY=-1, skewX=45, rotation=-30),
184            DecomposedTransform(scaleY=-1, skewY=45, rotation=-30),
185            DecomposedTransform(scaleX=-2, skewX=45, rotation=30),
186            DecomposedTransform(scaleX=-2, skewY=45, rotation=30),
187            DecomposedTransform(scaleY=-2, skewX=45, rotation=30),
188            DecomposedTransform(scaleY=-2, skewY=45, rotation=30),
189            DecomposedTransform(scaleX=-2, skewX=45, rotation=-30),
190            DecomposedTransform(scaleX=-2, skewY=45, rotation=-30),
191            DecomposedTransform(scaleY=-2, skewX=45, rotation=-30),
192            DecomposedTransform(scaleY=-2, skewY=45, rotation=-30),
193        ],
194    )
195    def test_roundtrip(lst, decomposed):
196        assert decomposed.toTransform().toDecomposed().toTransform() == pytest.approx(
197            tuple(decomposed.toTransform())
198        ), decomposed
199