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.