pythonfunctionargumentssvgwrite

How to (re)define function with complex argument


I use the svgwrite library in Python and, because svg has y-axis going downwards while I find it more convenient to have it upwards, I defined a function YInvert which does the trick for me. However, I have to use this function every time I use a function from svgwrite, e.g.:

dwg.line((xI,YInvert(yI)), (xII,YInvert(yII)), stroke='blue', stroke_width=0.1)

However, this is uncomfortable. How could I redefine the function dwg.line (or define some new function LineNew) to automatically include the YInvert? I want the argument to be in the same form, by this I mean I could use:

dwg.line((xI,yI), (xII,yII), stroke='blue', stroke_width=0.1)

or

LineNew((xI,yI), (xII,yII), stroke='blue', stroke_width=0.1)

I also use for example

dwg.circle(center=(10,YInvert(10)), r=0.2, fill='black')

so I am looking for something that would apply for a wide variety of arguments, just adding the YInvert function at the right place(s).


Solution

  • Here is a decorator approach. The coordinate detection is not very smart, you'd probably want to hone that a bit:

    from functools import wraps
    
    def YInvert(Y): # just for demo
        return -Y
    
    def invert_axis(f):
        @wraps(f)
        def g(*args, **kwds):
            args = list(args)
            for j, a in enumerate(args):
                if isinstance(a, tuple) and len(a) == 2:
                    try:
                        args[j] = (a[0], YInvert(a[1]))
                    except:
                        pass
            for k, v in kwds.items():
                if isinstance(v, tuple) and len(v) == 2:
                    try:
                        kwds[k] = (v[0], YInvert(v[1]))
                    except:
                        pass
            return f(*args, **kwds)
        return g
    

    Demo:

    @invert_axis
    def f(a, b, c, d=3):
        print(a, b, c, d)
    
    f((1, 2), 3, (4, 5), d=(1, 2))
    # (1, -2) 3 (4, -5) (1, -2)
    

    This can also be applied to functions already defined like library functions:

    f2 = invert_axis(f2)