Source code for simvx.graphics.draw2d_transform
"""2D affine transform stack for Draw2D.
Provides push/pop transform composition used by all drawing primitives to
support rotated, scaled, and translated coordinate spaces.
"""
from __future__ import annotations
import math
[docs]
class Draw2DTransformMixin:
"""Mixin providing a 2D affine transform stack for Draw2D."""
# 2D affine transform stack: x' = a*x + b*y + tx, y' = c*x + d*y + ty
_xf: tuple = (1.0, 0.0, 0.0, 1.0, 0.0, 0.0) # identity (a, b, c, d, tx, ty)
_xf_stack: list = []
_has_xf: bool = False # fast-path: skip transform when identity
[docs]
@classmethod
def push_transform(cls, a, b, c, d, tx, ty):
"""Push a 2D affine transform, composing with current."""
cls._xf_stack.append(cls._xf)
oa, ob, oc, od, otx, oty = cls._xf
cls._xf = (
oa * a + ob * c,
oa * b + ob * d,
oc * a + od * c,
oc * b + od * d,
oa * tx + ob * ty + otx,
oc * tx + od * ty + oty,
)
cls._has_xf = True
[docs]
@classmethod
def pop_transform(cls):
"""Pop the last transform, restoring the previous one."""
if cls._xf_stack:
cls._xf = cls._xf_stack.pop()
cls._has_xf = cls._xf != (1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
[docs]
@classmethod
def push_identity(cls):
"""Push current transform and reset to identity (for screen-space drawing)."""
cls._xf_stack.append(cls._xf)
cls._xf = (1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
cls._has_xf = False
@classmethod
def _xf_pt(cls, x, y):
"""Transform a point. Returns unchanged when no transform is active."""
if not cls._has_xf:
return x, y
a, b, c, d, tx, ty = cls._xf
return a * x + b * y + tx, c * x + d * y + ty
@classmethod
def _xf_sc(cls):
"""Uniform scale factor from current transform."""
if not cls._has_xf:
return 1.0
a, b, c, d, _, _ = cls._xf
return math.sqrt(abs(a * d - b * c))