[transform] Improve DecomposedTransform
And add tests. See thread starting at: https://github.com/fonttools/fonttools/pull/2958#issuecomment-1416859441
This commit is contained in:
parent
08d03a82b2
commit
4355d006ad
@ -407,6 +407,10 @@ def Scale(x, y=None):
|
|||||||
return Transform(x, 0, 0, y, 0, 0)
|
return Transform(x, 0, 0, y, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def _sign(v):
|
||||||
|
return +1 if v >= 0 else -1
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DecomposedTransform:
|
class DecomposedTransform:
|
||||||
"""The DecomposedTransform class implements a transformation with separate
|
"""The DecomposedTransform class implements a transformation with separate
|
||||||
@ -428,6 +432,12 @@ class DecomposedTransform:
|
|||||||
# Adapted from an answer on
|
# Adapted from an answer on
|
||||||
# https://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix
|
# https://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix
|
||||||
a, b, c, d, x, y = transform
|
a, b, c, d, x, y = transform
|
||||||
|
|
||||||
|
sx = _sign(a)
|
||||||
|
if sx < 0:
|
||||||
|
a *= sx
|
||||||
|
b *= sx
|
||||||
|
|
||||||
delta = a * d - b * c
|
delta = a * d - b * c
|
||||||
|
|
||||||
rotation = 0
|
rotation = 0
|
||||||
@ -437,12 +447,14 @@ class DecomposedTransform:
|
|||||||
# Apply the QR-like decomposition.
|
# Apply the QR-like decomposition.
|
||||||
if a != 0 or b != 0:
|
if a != 0 or b != 0:
|
||||||
r = math.sqrt(a * a + b * b)
|
r = math.sqrt(a * a + b * b)
|
||||||
rotation = math.acos(a / r) if b > 0 else -math.acos(a / r)
|
rotation = math.acos(a / r) if b >= 0 else -math.acos(a / r)
|
||||||
scaleX, scaleY = (r, delta / r)
|
scaleX, scaleY = (r, delta / r)
|
||||||
skewX, skewY = (math.atan((a * c + b * d) / (r * r)), 0)
|
skewX, skewY = (math.atan((a * c + b * d) / (r * r)), 0)
|
||||||
elif c != 0 or d != 0:
|
elif c != 0 or d != 0:
|
||||||
s = math.sqrt(c * c + d * d)
|
s = math.sqrt(c * c + d * d)
|
||||||
rotation = math.pi / 2 - (math.acos(-c / s) if d > 0 else -math.acos(c / s))
|
rotation = math.pi / 2 - (
|
||||||
|
math.acos(-c / s) if d >= 0 else -math.acos(c / s)
|
||||||
|
)
|
||||||
scaleX, scaleY = (delta / s, s)
|
scaleX, scaleY = (delta / s, s)
|
||||||
skewX, skewY = (0, math.atan((a * c + b * d) / (s * s)))
|
skewX, skewY = (0, math.atan((a * c + b * d) / (s * s)))
|
||||||
else:
|
else:
|
||||||
@ -453,9 +465,9 @@ class DecomposedTransform:
|
|||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
math.degrees(rotation),
|
math.degrees(rotation),
|
||||||
scaleX,
|
scaleX * sx,
|
||||||
scaleY,
|
scaleY,
|
||||||
math.degrees(skewX),
|
math.degrees(skewX) * sx,
|
||||||
math.degrees(skewY),
|
math.degrees(skewY),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -123,6 +123,19 @@ class TransformTest(object):
|
|||||||
assert d.translateX == 5
|
assert d.translateX == 5
|
||||||
assert d.translateY == 7
|
assert d.translateY == 7
|
||||||
|
|
||||||
|
def test_decompose(self):
|
||||||
|
t = Transform(-1, 0, 0, 1, 0, 0)
|
||||||
|
d = t.toDecomposed()
|
||||||
|
assert d.scaleX == -1
|
||||||
|
assert d.scaleY == 1
|
||||||
|
assert d.rotation == 0
|
||||||
|
|
||||||
|
t = Transform(1, 0, 0, -1, 0, 0)
|
||||||
|
d = t.toDecomposed()
|
||||||
|
assert d.scaleX == 1
|
||||||
|
assert d.scaleY == -1
|
||||||
|
assert d.rotation == 0
|
||||||
|
|
||||||
|
|
||||||
class DecomposedTransformTest(object):
|
class DecomposedTransformTest(object):
|
||||||
def test_identity(self):
|
def test_identity(self):
|
||||||
@ -141,3 +154,45 @@ class DecomposedTransformTest(object):
|
|||||||
def test_toTransform(self):
|
def test_toTransform(self):
|
||||||
t = DecomposedTransform(scaleX=2, scaleY=3)
|
t = DecomposedTransform(scaleX=2, scaleY=3)
|
||||||
assert t.toTransform() == (2, 0, 0, 3, 0, 0)
|
assert t.toTransform() == (2, 0, 0, 3, 0, 0)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"decomposed",
|
||||||
|
[
|
||||||
|
DecomposedTransform(scaleX=1, scaleY=0),
|
||||||
|
DecomposedTransform(scaleX=0, scaleY=1),
|
||||||
|
DecomposedTransform(scaleX=1, scaleY=0, rotation=30),
|
||||||
|
DecomposedTransform(scaleX=0, scaleY=1, rotation=30),
|
||||||
|
DecomposedTransform(scaleX=1, scaleY=1),
|
||||||
|
DecomposedTransform(scaleX=-1, scaleY=1),
|
||||||
|
DecomposedTransform(scaleX=1, scaleY=-1),
|
||||||
|
DecomposedTransform(scaleX=-1, scaleY=-1),
|
||||||
|
DecomposedTransform(rotation=90),
|
||||||
|
DecomposedTransform(rotation=-90),
|
||||||
|
DecomposedTransform(skewX=45),
|
||||||
|
DecomposedTransform(skewY=45),
|
||||||
|
DecomposedTransform(scaleX=-1, skewX=45),
|
||||||
|
DecomposedTransform(scaleX=-1, skewY=45),
|
||||||
|
DecomposedTransform(scaleY=-1, skewX=45),
|
||||||
|
DecomposedTransform(scaleY=-1, skewY=45),
|
||||||
|
DecomposedTransform(scaleX=-1, skewX=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleX=-1, skewY=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleY=-1, skewX=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleY=-1, skewY=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleX=-1, skewX=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleX=-1, skewY=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleY=-1, skewX=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleY=-1, skewY=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleX=-2, skewX=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleX=-2, skewY=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleY=-2, skewX=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleY=-2, skewY=45, rotation=30),
|
||||||
|
DecomposedTransform(scaleX=-2, skewX=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleX=-2, skewY=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleY=-2, skewX=45, rotation=-30),
|
||||||
|
DecomposedTransform(scaleY=-2, skewY=45, rotation=-30),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_roundtrip(lst, decomposed):
|
||||||
|
assert decomposed.toTransform().toDecomposed().toTransform() == pytest.approx(
|
||||||
|
tuple(decomposed.toTransform())
|
||||||
|
), decomposed
|
||||||
|
Loading…
x
Reference in New Issue
Block a user