Mousewheel Graph Window Zoom
In my web browser I can magnify the window contents by holding down the command/control key and scrolling with mousewheel or trackpad. Now I can do the same in a graph window :)
You can already use the mousewheel/trackpad to change the axis range if you hover over an axis. The code below lets you zoom both axes simultaneously. Holding down the shift key as well as command/ctrl accelerates the zoom factor. Expansion/contraction is centred at the mouse cursor position, so you can position your mouse and command-shift-scroll to zoom in rapidly on a point of interest.
When the 'global' symbol is defined, scroll-to-zoom is active for all newly created graph windows. Otherwise, it can be turned on or off for each graph window.
#pragma DefaultTab={3,20,4}
#pragma version=1.50
#pragma ModuleName=ScrollToZoom
#define global
// key codes
// 2: shift; 4: option/alt; 8: command/ctrl
// On Windows, alt key is reserved for graph drag.
static constant kZoomKey=8
static constant kFasterKey=2
static constant kZoomSpeed=70
static constant kReverse=0 // reverse the direction of expansion
#ifdef global
static function AfterWindowCreatedHook(string win, variable type)
if (type == 1)
SetWindow $win hook(hScrollToZoom)=ScrollToZoom#hookScrollToZoom
endif
return 0
end
#else
menu "Graph", dynamic
ScrollToZoom#zoomMenu(), /Q, ScrollToZoom#toggleZoom()
end
static function /S zoomMenu()
GetWindow kwTopWin hook(hScrollToZoom)
return SelectString(strlen(s_value)>0, "", "!" + num2char(18)) + "Scroll-Zoom"
end
#endif
static function toggleZoom()
GetWindow kwTopWin hook(hScrollToZoom)
SetWindow kwTopWin hook(hScrollToZoom) = $SelectString(strlen(s_value)>0, "ScrollToZoom#hookScrollToZoom", "")
end
static function hookScrollToZoom(STRUCT WMWinHookStruct &s)
if (s.eventCode == 22 && s.eventMod&kZoomKey) // mousewheel/touchpad + zoom key
string strAxes = AxisList(s.WinName), axis = "", type = ""
int i, numAxes, isHorizontal, isLog
Make /D/free/N=3 wAx // free wave to hold axis minimum, maximum, axis value for mouse location
numAxes = ItemsInList(strAxes)
int rev = 1 - 2*kReverse
variable expansion = 1 - rev*s.wheelDy * kZoomSpeed/5000
if (s.eventMod & kFasterKey)
if (kFasterKey==2 && s.wheelDy==0)
// this works when shift key also transforms wheel.dY to wheel.dX
s.wheelDy = s.wheelDx
endif
expansion = 1 - rev*s.wheelDy * kZoomSpeed/500
endif
for (i=0;i<numAxes;i++)
axis = StringFromList(i, strAxes)
type = StringByKey("AXTYPE", AxisInfo(s.WinName, axis))
isHorizontal = (cmpstr(type, "bottom")==0 || cmpstr(type,"top")==0 || cmpstr(type[0,2],"/B=")==0 || cmpstr(type[0,2],"/T=")==0)
isLog = (NumberByKey("log(x)", AxisInfo(s.WinName, axis),"="))
GetAxis /W=$s.WinName/Q $axis
wAx = {v_min, v_max, AxisValFromPixel(s.WinName, axis, isHorizontal ? s.mouseLoc.h : s.mouseLoc.v)}
if (WaveMax(wAx) == wAx[2] || WaveMin(wAx) == wAx[2])
continue
endif
if (isLog)
wAx = log(wAx)
wAx = wAx[2] - (wAx[2] - wAx[p]) * expansion
wAx = alog(wAx)
else
wAx = wAx[2] - (wAx[2] - wAx[p]) * expansion
endif
WaveStats /Q/M=1 wAx
if ( (V_numNaNs+V_numInfs) || wAx[1]==wAx[0] )
continue
endif
if (wAx[1] > wAx[0])
SetAxis /W=$s.WinName $axis, wAx[0], wAx[1]
else
SetAxis /R/W=$s.WinName $axis, wAx[0], wAx[1]
endif
endfor
endif
return 0
end
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
Edited snippet works with log axes.
February 17, 2021 at 12:30 am - Permalink
Edited to add /R flag for reversed axes. A subsequent autoscale will not switch the axis direction.
February 19, 2021 at 04:27 am - Permalink
I just noticed that forum member Henry S released a project back in 2014 that functions more-or-less identically to this code snippet.
https://www.wavemetrics.com/project/ScrollWheelZoom
I suspect that the 2014 project is not maintained any more. Nevertheless, I wanted to point out that Henry S got there first, and note that it's easy to overlook the resources available in this forum!
August 29, 2021 at 02:25 am - Permalink
Say, Tony- I'm guessing that this adds to the functionality you get currently when the mouse is over an axis and you use the mouse wheel?
August 30, 2021 at 09:24 am - Permalink
Yes, it zooms so that the point under the mouse stays put. The older project used a modifier key to prevent zooming, mine uses a modifier to allow. It feels quite 'natural'.
My version also handles log axes.
What I didn't do is to get a list of all axes and figure out which ones are relevant for the mouse cursor position...
Edit: v1.5 fixes this
August 31, 2021 at 12:39 am - Permalink
In reply to Yes, it zooms both axes … by tony
Hi Tony,
Thank you for posting this code! It works like a charm and it is extremely useful. However, for us, it would be really great if one could add-on additional functionalities such as: (i) correcting a linear slope and (ii) being able to extract (wave)points and the corresponding wavenames by clicking, perhaps while pressing the right mouse button, on a specific graph point. Maybe the former functionality could be implemented to work similarly to how the “alt” key is currently used for dragging. For the second feature the output may be directed to a either a separate, stand-alone window or to a user function. Could you please provide some pointers on how these features could be incorporated in your code? Alternatively, if updating your code is not feasible, would you mind providing some suggestions on how these functions could be developed?
Best,
Sebastian
March 13, 2023 at 09:21 am - Permalink
Hi Sebastian,
All the things you describe sound very doable, but I wouldn't attempt to tack any of them onto this code snippet. I suspect that there are some existing examples of code that does very similar things.
I have some code that provides a UI that will allow you change the slope of a data set interactively. It also allows other kinds of manipulations; I can share that with you if you're interested, but I prefer not to make it public.
For investigating wave names and points with mouseover interactivity, I would use a hook function together with the TraceFromPixel() function to identify the trace. Here is an example of code where a trace is labelled with a tag that disappears when the mouse moves off of the trace:
STRUCT WMWinHookStruct &s
variable hookResult = 0 // 0 if we do not handle event, 1 if we handle it.
switch(s.eventCode)
case 4: // mousemoved event
string foo=TraceFromPixel(s.mouseloc.h,s.mouseloc.v, "WINDOW:StandardsPanel#G0;DELTAX:4;DELTAY:4;")
if (strlen(foo))
string tname=StringByKey("TRACE", foo)
if (cmpstr(tname, "std_betacorr") && cmpstr(tname, "std_Ybcorr"))
Tag/K/W=StandardsPanel#G0 /N=idtag
break
endif
variable HitP=str2num(StringByKey("HITPOINT", foo))
DFREF LuHfdfr = GetLuHfDFREF()
wave /SDFR=LuHfdfr/T std_name
Tag/C/N=idtag/Z=1/B=(65535,65534,49151)/G=(43690,43690,43690)/D={1,20}/A=LT/X=-2.00/Y=-2.00/L=0 /W=StandardsPanel#G0 $tname, HitP,"\\K(0,0,0)\\Z10"+std_name[HitP]
else // not on point
Tag/K/W=StandardsPanel#G0 /N=idtag
endif
break
endswitch
return hookResult // If non-zero, we handled event and Igor will ignore it.
end
Maybe that provides a pathway to the kind of things you want to do? If you want to provide more details of what you have in mind I'm sure you can get some more specific pointers from the forum about how to get it done.
March 15, 2023 at 02:01 am - Permalink
Here is the code that allows you to 'correct' a linear slope in a completely arbitrary fashion. This is a dangerous activity, IMO. At least the manipulation is recorded in a wavenote. However, for spectroscopic applications at least, I strongly recommend fitting and subtracting a linear baseline rather than simply 'rotating' the data like this.
Edit: version 1.10 works for both waveform and X-Y traces
March 20, 2023 at 07:20 am - Permalink
See also
DisplayHelpTopic "Trace Menus"
for an example of how to extend the trace popup menu to extract a wave name. Invoking the menu also records the mouse position in global variables, so you can use TraceFromPixel to find the hitpoint.
March 20, 2023 at 07:44 am - Permalink
In reply to See also DisplayHelpTopic … by tony
Hi Tony,
Thank you for sharing your code and for the additional feedback! This works great and it is exactly what I envisioned.
Best,
Sebastian
March 20, 2023 at 08:34 am - Permalink
@sstoian
I found a newer version of the tilt&bend code, attached in the post above.
March 23, 2023 at 06:32 am - Permalink