I would like to rotate an object that look like a cylinder by pressing the left button of the mouse on its tip and make it rotate around the other tip when I move the mouse (the left button still pressed). I already succeed in making a translation of the object when I use the mouse wheel on it, but I cannot manage to understand how I can perfom a rotation.
The issue I have is how to convert the mouse possition (in 2d) to a rotation (in 3D) of the object.
here is a screenshot
When I click on the cylinder tip (the blue one), I enter in the MouseMove(self, obj, event)
function. I don’t know how to apply a rotation on the Aelectrode
object with repect to the mouse position self.GetInteractor().GetEventPosition()
and the previuous mouse position self.prevpos
Here the full code I have for now:
from PyQt6.QtGui import *
from PyQt6.QtCore import *
from PyQt6.QtWidgets import *
import numpy as np
import sys
import vtk
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
class Mesh_sEEGElectrodeView(QMainWindow):
def __init__(self,parent=None):
super(Mesh_sEEGElectrodeView, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.mainHBOX_param_scene = QVBoxLayout()
self.MV_plot = VTK_sEEGElectrodeView(self)
self.mainHBOX_param_scene.addWidget(self.MV_plot)
self.centralWidget.setLayout(self.mainHBOX_param_scene)
self.ElectrodeList = []
self.ElectrodeList.append(Electrode())
self.Display_parcellisation()
def Display_parcellisation(self):
self.MV_plot.draw_Mesh(self.ElectrodeList)
class Electrode():
def __init__(self, NumOfSensors=15, Diameter=0.8, Resolution=36):
self.NOS = NumOfSensors
self.Diameter = Diameter
self.Resolution = Resolution
def Create(self):
SensorLength = 2
mm = 1
self.cylActors = []
# design tube holding electrodes
tube = vtk.vtkCylinderSource()
tube.SetHeight(((self.NOS * 2 + (self.NOS - 1) * 1.5) - 0.1) * mm)
tube.SetCenter(0, (self.NOS * 2 + (self.NOS - 1) * 1.5) * mm / 2, 0)
tube.SetRadius((self.Diameter - 0.05) * mm / 2)
tube.SetResolution(self.Resolution)
tubePoly = tube.GetOutput()
# tubePoly.Update()
tube.Update()
TubeMT = vtk.vtkPolyDataMapper()
TubeMT.SetInputData(tubePoly)
TubeMT.Update()
# TubeMT.GlobalImmediateModeRenderingOn()
TubeA = vtk.vtkLODActor()
TubeA.VisibilityOn()
TubeA.SetMapper(TubeMT)
TubeA.GetProperty().SetColor(1, 0, 0)
self.cylActors.append(TubeA)
# create the Electrodes
for i in range(self.NOS):
# create cylinder
cyl = vtk.vtkCylinderSource()
cyl.SetHeight(SensorLength * mm)
cyl.SetCenter(0, (1 + i * (2 + 1.5)) * mm, 0)
cyl.SetRadius(self.Diameter / 2 * mm)
cyl.SetResolution(self.Resolution)
cylPoly = cyl.GetOutput()
cyl.Update()
# cylPoly.Update()
# create mappers and actors for the sensors
cMT = vtk.vtkPolyDataMapper()
cMT.SetInputData(cylPoly)
# cMT.GlobalImmediateModeRenderingOn()
cA = vtk.vtkLODActor()
cA.VisibilityOn()
cA.SetMapper(cMT)
if i == 0:
cA.GetProperty().SetColor(0, 1, 0)
else:
cA.GetProperty().SetColor(0, 0, 1)
self.cylActors.append(cA)
return self.cylActors
class VTK_sEEGElectrodeView(QMainWindow):
def __init__(self, parent=None):
super(VTK_sEEGElectrodeView, self).__init__(parent)
self.parent=parent
self.frame = QFrame()
self.vl = QVBoxLayout()
self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
self.vl.addWidget(self.vtkWidget)
self.ren = vtk.vtkRenderer()
self.ren.SetBackground(.1, .1, .1)
# self.ren.SetUseDepthPeeling(1)
# self.ren.SetMaximumNumberOfPeels(100)
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
# style = vtk.vtkInteractorStyleTrackballCamera()
self.style1 = MouseInteractorHighLightActor_sEEGElectrode(self)
self.style1.SetDefaultRenderer(self.ren)
self.iren.SetInteractorStyle(self.style1)
self.ren.ResetCamera()
self.frame.setLayout(self.vl)
self.setCentralWidget(self.frame)
self.show()
self.iren.Initialize()
self.iren.Start()
def draw_Mesh(self,ElectrodeList,smoothing =0):
self.ren.RemoveAllViewProps()
self.ElectrodeList = ElectrodeList
self.Electrode_a = []
self.Electrode_aAssemblys = []
for i in range(len(self.ElectrodeList)):
self.Electrode_a.append(self.ElectrodeList[i].Create())
actAssembly = vtk.vtkAssembly()
for j in range(self.ElectrodeList[i].NOS + 1):
actAssembly.AddPart(self.Electrode_a[i][j])
actAssembly.id = i
self.Electrode_aAssemblys.append(actAssembly)
self.ren.AddActor(self.Electrode_aAssemblys[i])
for i in range(10):
source = vtk.vtkSphereSource()
source.SetCenter(np.random.randint(-50,50,1),np.random.randint(-50,50,1),np.random.randint(-50,50,1))
source.SetRadius(3.0)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
self.ren.ResetCamera()
self.iren.ReInitialize()
self.iren.GetRenderWindow().Render()
class MouseInteractorHighLightActor_sEEGElectrode(vtk.vtkInteractorStyleTrackballCamera):
def __init__(self, parent=None):
self.parent = parent
self.AddObserver("MouseWheelForwardEvent", self.MouseWheelBackward)
self.AddObserver("MouseWheelBackwardEvent", self.MouseWheelBackward)
self.AddObserver('LeftButtonPressEvent',self.MouseMoveOK)
self.AddObserver('LeftButtonReleaseEvent',self.MouseMoveNot)
self.AddObserver("MouseMoveEvent",self.MouseMove)
self.DetectMouseMove = 0
self.prevpos= [0,0]
def MouseMove(self, obj, event):
if self.DetectMouseMove:
self.OnMouseMove()
clickPos = self.GetInteractor().GetEventPosition()
pickerActor = vtk.vtkPropPicker()
pickerActor.PickProp(clickPos[0], clickPos[1], self.GetDefaultRenderer())
# get the new
NewPickedActor = pickerActor.GetAssembly()
if NewPickedActor:
if NewPickedActor in self.parent.Electrode_aAssemblys:
elec = NewPickedActor.id
Aelectrode = self.parent.Electrode_aAssemblys[elec]
electrode = self.parent.ElectrodeList[elec]
Aelectrode.SetScale([1, 1, 1])
bas = np.asarray(electrode.cylActors[1].GetCenter())
haut = np.asarray(electrode.cylActors[-1].GetCenter())
self.GetInteractor().GetRenderWindow().Render()
self.prevpos = clickPos
else:
self.OnMouseMove()
def MouseMoveOK(self, obj, event):
clickPos = self.GetInteractor().GetEventPosition()
self.prevpos = clickPos
pickerActor = vtk.vtkPropPicker()
pickerActor.PickProp(clickPos[0], clickPos[1], self.GetDefaultRenderer())
# get the new
NewPickedActor = pickerActor.GetAssembly()
if NewPickedActor:
if NewPickedActor in self.parent.Electrode_aAssemblys:
self.DetectMouseMove = 1
else:
self.OnLeftButtonDown()
def MouseMoveNot(self, obj, event):
self.DetectMouseMove = 0
self.OnLeftButtonUp()
def MouseWheelBackward(self, obj, event):
clickPos = self.GetInteractor().GetEventPosition()
pickerActor = vtk.vtkPropPicker()
pickerActor.PickProp(clickPos[0], clickPos[1], self.GetDefaultRenderer())
NewPickedActor = pickerActor.GetAssembly()
if NewPickedActor:
if NewPickedActor in self.parent.Electrode_aAssemblys:
elec = NewPickedActor.id
Aelectrode = self.parent.Electrode_aAssemblys[elec]
electrode = self.parent.ElectrodeList[elec]
a = np.asarray(electrode.cylActors[1].GetCenter()) - np.asarray(electrode.cylActors[-1].GetCenter())
a = a / np.linalg.norm(a)
position = Aelectrode.GetPosition()
if event == 'MouseWheelBackwardEvent':
Aelectrode.SetPosition(position[0] - a[0], position[1] - a[1], position[2] - a[2])
else:
Aelectrode.SetPosition(position[0] + a[0], position[1] + a[1], position[2] + a[2])
self.GetInteractor().GetRenderWindow().Render()
else:
if event == 'MouseWheelBackwardEvent':
self.OnMouseWheelBackward()
else:
self.OnMouseWheelForward()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Mesh_sEEGElectrodeView()
window.show()
sys.exit(app.exec())