@@ -2660,6 +2660,65 @@ def get_matrix(self):
26602660 self ._inverted = None
26612661 self ._invalid = 0
26622662 return self ._mtx
2663+ class IndirectTransform (Transform ):
2664+ """
2665+ A transform that wraps a callable and resolves it lazily at draw time.
2666+
2667+ This is useful when the actual transform depends on something not
2668+ known until draw time, like another artist's bounding box.
2669+
2670+ Parameters
2671+ ----------
2672+ func : callable
2673+ A callable that takes no arguments and returns a `.Transform`
2674+ or `.BboxBase`.
2675+
2676+ Examples
2677+ --------
2678+ ::
2679+
2680+ tr = IndirectTransform(
2681+ lambda: BboxTransformTo(some_artist.get_window_extent())
2682+ )
2683+ """
2684+
2685+ input_dims = 2
2686+ output_dims = 2
2687+
2688+ def __init__ (self , func , ** kwargs ):
2689+ if not callable (func ):
2690+ raise TypeError (
2691+ f"func must be callable, not { type (func ).__name__ !r} "
2692+ )
2693+ super ().__init__ (** kwargs )
2694+ self ._func = func
2695+
2696+ def _resolve (self ):
2697+ """Call the wrapped function and return a proper Transform."""
2698+ result = self ._func ()
2699+ if isinstance (result , BboxBase ):
2700+ return BboxTransformTo (result )
2701+ elif isinstance (result , Transform ):
2702+ return result
2703+ raise TypeError (
2704+ f"func must return a Transform or BboxBase, "
2705+ f"not { type (result ).__name__ !r} "
2706+ )
2707+
2708+ def get_affine (self ):
2709+ return self ._resolve ().get_affine ()
2710+
2711+ def frozen (self ):
2712+ return self ._resolve ().frozen ()
2713+
2714+ def inverted (self ):
2715+ return self ._resolve ().inverted ()
2716+
2717+ def transform_affine (self , points ):
2718+ return self ._resolve ().transform_affine (points )
2719+
2720+ def transform_non_affine (self , points ):
2721+ return self ._resolve ().transform_non_affine (points )
26632722
26642723
26652724class BboxTransformFrom (Affine2DBase ):
0 commit comments