rotationmeshcubekeyeventvispy

How can I apply a transformation several times on a vispy mesh object?


I am trying to rotate a cube by pressing 'z' on my keyboard using vispy.

However, I would like that cube to rotate everytime I press 'z', which is not the case right now : it only rotates the first time I press it and then, when I press it again, nothing happens...

Here's the code.

from vispy import app, scene, geometry
import numpy as np

class MonCanvas(scene.SceneCanvas):
    def on_key_press(self,event):
        global cube1, cube2, c1data, c2data
        if event.text == 'a':
            c1data.set_face_colors([[0,0,1,1],[0,0,1,1],[0,1,0,1],[0,1,0,1],[1,1,1,1],[1,1,1,1],[1,1,0,1],[1,1,0,1],[1,0,0,1],[1,0,0,1],[1,0.5,0,1],[1,0.5,0,1]])
            cube1.mesh_data_changed()
        if event.text == 'z':
            rot1 = scene.transforms.MatrixTransform()
            rot1.rotate(90,(0,0,1))
            cube2.transform = rot1

fenetre = MonCanvas(title="FenĂȘtre principale", size=(2000,1000), keys="interactive")

view = fenetre.central_widget.add_view()
view.camera = 'turntable'
view.camera.distance = 10

c1data = geometry.MeshData(vertices = np.array([[0.5,0.5,-0.5],[0.5,-0.5,-0.5],[-0.5,0.5,-0.5],[-0.5,-0.5,-0.5],[0.5,0.5,0.5],[0.5,-0.5,0.5],[-0.5,0.5,0.5],[-0.5,-0.5,0.5]]), edges = np.array([[0,1],[0,2],[0,4],[3,1],[3,2],[3,7],[5,1],[5,4],[5,7],[6,2],[6,4],[6,7]]), faces = np.array([[1,2,3],[0,1,2],[5,6,7],[4,5,6],[0,1,5],[0,5,4],[2,3,7],[2,7,6],[1,3,7],[1,7,5],[0,2,6],[0,6,4]]))
c1data.set_face_colors([[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1],[0,0,1,1]])
cube1 = scene.visuals.Mesh(meshdata = c1data)
view.add(cube1)

c2data = geometry.MeshData(vertices = np.array([[0.5,0.5,0.5],[0.5,-0.5,0.5],[-0.5,0.5,0.5],[-0.5,-0.5,0.5],[0.5,0.5,1.5],[0.5,-0.5,1.5],[-0.5,0.5,1.5],[-0.5,-0.5,1.5]]), edges = np.array([[0,1],[0,2],[0,4],[3,1],[3,2],[3,7],[5,1],[5,4],[5,7],[6,2],[6,4],[6,7]]), faces = np.array([[1,2,3],[0,1,2],[5,6,7],[4,5,6],[0,1,5],[0,5,4],[2,3,7],[2,7,6],[1,3,7],[1,7,5],[0,2,6],[0,6,4]]))
c2data.set_face_colors([[0,0,1,1],[0,0,1,1],[0,1,0,1],[0,1,0,1],[1,1,1,1],[1,1,1,1],[1,1,0,1],[1,1,0,1],[1,0,0,1],[1,0,0,1],[1,0.5,0,1],[1,0.5,0,1]])
cube2 = scene.visuals.Mesh(meshdata = c2data)
view.add(cube2)

fenetre.show()
app.run()

Thanks in advance for the help !


Solution

  • The transform is applied to the coordinates of the MeshVisual inside the GPU. They do not modify the coordinates in-place inside the MeshVisual. So when you run:

                rot1 = scene.transforms.MatrixTransform()
                rot1.rotate(90,(0,0,1))
                cube2.transform = rot1
    

    You are telling vispy and the GPU to take the coordinates for cube2 and rotate them 90 degrees before using/drawing them. Put another way, you are telling it what transformation to apply, but you aren't performing that transformation. So, you need to take that 90 and add 90 to it every time. So your series of rotations would be the X, then after the first z they'd be X + 90, then X + 180, then X + 270, and so on. But there should be an easier way to do that...

    It looks like you can do cube2.transform.rotate(90) and that will modify the existing transform inplace. This has the added performance benefit of not creating a new transform object since doing that would require recompiling the shader code for every rotation which would be slower. So doing this works for me:

            if event.text == 'z':
                cube2.transform.rotate(90, (0, 0, 1))
    

    But you need to make sure after you create cube2 to set the initial transform to a MatrixTransform otherwise cube2.transform won't be a matrix transform that you can rotate:

    cube2 = scene.visuals.Mesh(meshdata = c2data)
    cube2.transform = scene.transforms.MatrixTransform()
    view.add(cube2)