Adapt to user zooming into axis

I have a graph with some data, attached to it a free axis and an axis hook function. The axis hook function should run some code whenever a user zooms in or out of an trace/image.

Fuction Setup()
    Make/O data = p
    Make/O largedata = p + 100
End

Function AxisHook(s)
    STRUCT WMAxisHookStruct &s
   
    print "called with", s
End

Window Graph0() : Graph
    PauseUpdate; Silent 1       // building window...
    Display /W=(95.25,263,612,634.25) data
    NewFreeAxis/O/L test
    ModifyFreeAxis/Z test,master= bottom,hook= AxisHook
    ModifyGraph nticks(test)=0
    ModifyGraph axThick(test)=0
    ModifyGraph lblPos(left)=47
    ModifyGraph freePos(test)=-50
    SetAxis left -7.63192488262911,144.768075117371
EndMacro

Now this works for my use case.

But when I add more data to it via

AppendToGraph largeData

my hook function get's also called. But I would like to distinguish the case of users zooming into the axis and code changing the axis limits. Or to rephrase I actually want to run the code *only* when the user zooms in/out interactively and not when doing it programmatically.

Is that possible? Am I doing it right?

Hmm, that is surprisingly difficult. There seems to be no way to tell from the axis hook what event called the hook function. I came up with a somewhat kludgy workaround which saves the latest axis scaling in the graph's user data and only calls code if the axis range has changed (which happens when zooming):

Function AxisHook(s)
    STRUCT WMAxisHookStruct &s
   
    String putRange, lastZoom = GetUserData(s.win,"","axisRange")
    sprintf putRange, "%f;%f;", s.min, s.max
    SetWindow $(s.win), userdata(axisRange)=putRange
   
    Variable zoomCall = 0
    if (strlen(lastZoom) > 0)
        Variable left = str2num(StringFromList(0,lastZoom))
        Variable right = str2num(StringFromList(1,lastZoom))
        if ( ((left - s.min) > 1e-6) || ((right - s.max) > 1e-6))
            zoomCall = 1   
        endif
    endif
   
    if (zoomCall)
        print "called with", s
    endif
End

 

Unfortunately, the axis range changes when you add a trace with different range, and also when the user changes the axis range.

Oh, that's right! So my example was not very useful. That's really an uphill battle, but one could use the same method to store a list of displayed traces on the graph and compare if something has changed between calls. I don't see how one can distinguish user's zooming in from any other event which changes the axis range.

You may need to have a button to turn the zoom feature (your hook function) on/off.

You may need to lock out any ability to manually add data to the graph and instead force the user to run a function instead (e.g. myappendtograph).

Doesn't Igor give an option to know what the last issued command was? Can that be tested in the hook function to distinguish a command-line input appendtograph and thereby ignore break from the hook?

Of these three, I might find the first the easiest to do even as it adds the overhead for the UI on the user.

Thanks all for your input. I've now found a solution. I've ditched the axis hook approach and went with a normal window hook. The code is basically

Function PA_TraceWindowHook(s)
    STRUCT WMWinHookStruct &s

    string traceGraph

    switch(s.eventcode)
        case 22: // mouse wheel
        case 6: // resize
            traceGraph = s.winName
            Execute/P/Q "PA_UpdateScaleBars(\"" + traceGraph + "\", 0)"
            break
        case 10: // menu
            if(!cmpstr(s.menuName, "Graph") && !cmpstr(s.menuItem, "Autoscale Axes"))
                traceGraph = s.winName
                Execute/P/Q "PA_UpdateScaleBars(\"" + traceGraph + "\", 1)"
            endif
            break
    endswitch

    return 0
End

where PA_UpdateScaleBars reacts to the user zooming into an axis via the mouse wheel, and the menu reset the scale bars on Ctrl+a. I had to use the operation queue here so that it is executed after the axis has been rescaled by IP.