Problem with correct 2-D zooming behavior

72 views
Skip to first unread message

Gregory Hermanson

unread,
Feb 26, 2020, 4:54:03 PM2/26/20
to vispy
I am trying to implement a simple zooming logic in Vispy, using the mousewheel and the x-coordinates of the mouse in the window. Goal: to zoom into the location where the mouse is pointing to, at any given time. The basic set-up works, but I get inconsistent jumps as soon as my zoom level increases and I change the x-location of the mouse.

I believe this is a simple question of re-normalizing the incremental zoom coordinates (in the on_mouse_wheel method), but I've failed to come up with a working solution after much effort - any help much appreciated!



import numpy as np
import math
from vispy import app, gloo, visuals
from vispy.visuals.transforms import STTransform


# Number of displayed lines
x_size
= 50


# window size
canvas_size
= (800, 600)


# launch app
app
.Application(backend_name='PyQt5')


# define texture
tex
= np.c_[(np.ones(shape=(1, x_size, 1)),np.zeros(shape=(1, x_size, 1)),np.zeros(shape=(1, x_size, 1)),np.random.uniform(size=(1, x_size, 1)))]


class Canvas(app.Canvas):
   
def __init__(self):
       
# housekeeping
        app
.Canvas.__init__(self, title='Use your wheel to zoom!', keys='interactive', vsync=True, autoswap=True)
        gloo
.set_viewport(0, 0, *self.physical_size)


       
# translate to full screen
       
self.heatmap = visuals.ImageVisual(data=tex, method='subdivide',interpolation='nearest')
        s
= (1 / (self.size[0]*self.heatmap.size[0] / (self.size[0]*2)), 100)
       
self.heatmap.transform = STTransform(scale=s, translate=(-1,-1))


       
self.scale = (1., 1.)
       
self.zoom_pos = 0.0


       
# track time
       
self._timer = app.Timer('auto', connect=self.on_timer, start=True)


       
# housekeeping
        gloo
.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha'))
       
self.show()


   
def on_mouse_wheel(self, event):
       
print(event.pos) #800, 600
        dx
= np.sign(event.delta[1]) * 0.05
        scale_x
, scale_y = self.scale
        scale_x_new
, scale_y_new = (
            scale_x
* math.exp(1.0 * dx),
            scale_y
* math.exp(1.0 * dx)
       
)


       
self.scale = (max(1, scale_x_new), max(1, scale_y_new))


       
# ????????????????????????????? #
       
# How to update this correctly? #
       
# ????????????????????????????? #
       
self.zoom_pos = self._normalize(event.pos)[0]




   
def on_resize(self, event):
        vp
= (0, 0, event.physical_size[0], event.physical_size[1])
       
self.context.set_viewport(*vp)




   
def on_timer(self, event):
        scale_x
, scale_y = self.scale
        s
= (scale_x / (self.size[0] * self.heatmap.size[0] / (self.size[0] * 2)), 100)
       
self.heatmap.transform = STTransform(scale=s, translate=(self.zoom_pos, -1))*STTransform(scale=None, translate=(-(self.zoom_pos + 1) / 2 * x_size,0))
       
self.update()


   
def on_draw(self, event):
        gloo
.clear(color='black', depth=True)
       
self.heatmap.draw()


   
def _normalize(self, x_y):
        x
, y = x_y
        w
, h = float(self.size[0]), float(self.size[1])
       
return x / (w / 2.) - 1., y / (h / 2.) - 1.




if __name__ == '__main__':
    c
= Canvas()
    app
.run()

David Hoese

unread,
Feb 26, 2020, 9:27:25 PM2/26/20
to vispy
I'm sure this conversation will end up on gitter given your message there, but I thought I'd put some of my thoughts here:

1. I wonder if there is a race condition type of thing going on between redrawing things and updating the zoom position. Like it is being redrawn, then the zoom is being updated, but not shown for a while.
2. You mentioned that the problems show up when you change X position. Is there any chance that your mouse wheel's delta is being reported oddly when the mouse is being moved (when the position changes)?

Just some random thoughts that came to mind after reading this.

Dave

Gregory Hermanson

unread,
Feb 27, 2020, 3:17:05 AM2/27/20
to vispy
Hi Dave,

I think the problem is the following:

- zoom_pos is always captured relative to the currently visible window, hence ignores the current zoom level
- Therefore, if a zoom has already been performed, a small change in zoom_pos translates to a big change (jump) when turning the mouse wheel
- zoom_pos should rather be captured relative to previous zoom levels



Re line plot zoom on Gitter:
Yes that's me - currently travelling with just my business laptop; I avoid using my personal communication / git accounts when using my work laptop (data + code leakage)

David Hoese

unread,
Feb 27, 2020, 9:11:25 AM2/27/20
to vispy
So do you think this is something wrong in VisPy causing the jumps? Or are you hoping for help with the logic to get this to be smooth?

Another thought after skimming your code again: you may want to look at modifying the STTransform of the heatmap instead of overwriting it every time. Overwriting the transform causes the shader code to be resent (recompiled) to the GPU. This could hurt performance when drawing more objects. There are also other methods that exist for the STTransform to work with zooming and translating: https://github.com/vispy/vispy/blob/master/vispy/visuals/transforms/linear.py#L192

Dave

Marius

unread,
Feb 27, 2020, 9:46:42 AM2/27/20
to vispy
Hi Gregory,

I have minimal experience with VisPy, someone else implemented a "3D graphic engine" for the opensource sw that I am working on (www.flatcam.org, beta branch https://bitbucket.org/jpcgt/flatcam/branch/Beta).
There is no issue with zooming although the files author made some patches to VisPy.

Yet, I observed that starting with PyQt > 5.12.1 there are issues at least in Windows and for MacOS (mouse right click is_dragging detection and also some weird transparency issues were reported), There may be other issues. All is OK if using PyQt <=5.12.1.

Perhaps this will help.

Best,
Marius
Reply all
Reply to author
Forward
0 new messages