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 rtGlobals=3
#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
    return 0


menu "Graph", dynamic
    ScrollToZoom#zoomMenu(), /Q, ScrollToZoom#toggleZoom()

static function /S zoomMenu()
    GetWindow kwTopWin hook(hScrollToZoom)
    return SelectString(strlen(s_value)>0, "", "!" + num2char(18)) + "Scroll-Zoom"


static function toggleZoom()
    GetWindow kwTopWin hook(hScrollToZoom)
    SetWindow kwTopWin hook(hScrollToZoom) = $SelectString(strlen(s_value)>0, "ScrollToZoom#hookScrollToZoom", "")

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
            expansion = 1 - rev*s.wheelDy * kZoomSpeed/500
        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])
            if (isLog)
                wAx = log(wAx)
                wAx = wAx[2] - (wAx[2] - wAx[p]) * expansion
                wAx = alog(wAx)
                wAx = wAx[2] - (wAx[2] - wAx[p]) * expansion
            WaveStats /Q/M=1 wAx
            if ( (V_numNaNs+V_numInfs) || wAx[1]==wAx[0] )
            if (wAx[1] > wAx[0])
                SetAxis /W=$s.WinName $axis, wAx[0], wAx[1]
                SetAxis /R/W=$s.WinName $axis, wAx[0], wAx[1]
    return 0


Edited to add /R flag for reversed axes. A subsequent autoscale will not switch the axis direction.

I just noticed that forum member Henry S released a project back in 2014 that functions more-or-less identically to this code snippet.

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!

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?

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




Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More