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))