Reputation: 4967
I would like to rotate or translate an object that look like a cylinder by pressing the left button of the mouse on it.
to rotate, i would like to click on the tip and make it rotate around the other tip when I move the mouse (the left button still pressed). To translate I would like to press the 't' key and move the object according to the plan of the camera.
The rotation works but it is not smooth because as soon as the mouse leave the object, the orientation is not updated anymore. the translation seems to work but the position do not correspond exactly to the position of the mouse.
When the mouse left button is pressed, I save the object I click on. When I pressed a key, i also save the key to know the 't' key is pressed for translating.
In the MouseMove
function,
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 self.key == 't':
elec = self.elec
Aelectrode = self.parent.Electrode_aAssemblys[elec]
electrode = self.parent.ElectrodeList[elec]
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToView()
coordinate.SetValue(self.prevpos[0], self.prevpos[1], 0.5)
prevviewCoord = np.asarray(coordinate.GetComputedWorldValue(self.parent.ren))
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToView()
coordinate.SetValue(clickPos[0], clickPos[1], 0.5)
click3DviewCoord = np.asarray(coordinate.GetComputedWorldValue(self.parent.ren))
a = (click3DviewCoord - prevviewCoord) / 300
position = Aelectrode.GetPosition()
Aelectrode.SetPosition(position[0] + a[0], position[1] + a[1], position[2] + a[2])
How can I position the object directly under the mouse?.
The second problem I have is that the rotation stops when the mouse leave the object. Is there a way to make the rotation even if the mouse is not on the vtkassembly 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)
cyl.Update()
cMT = vtk.vtkPolyDataMapper()
cMT.SetInputData(cyl.GetOutput())
cA = vtk.vtkActor()
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
n = [0, 50, 0]
actAssembly.SetOrientation(np.degrees(np.arctan(n[2] / (n[1] + 0.000001))), 0,
np.degrees(np.arctan(n[0] / (n[1] + 0.000001))))
actAssembly.SetPickable(True)
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()
self.ren.Render()
class MouseInteractorHighLightActor_sEEGElectrode(vtk.vtkInteractorStyleTrackballCamera):
def __init__(self, parent=None):
self.parent = parent
self.AddObserver("KeyPressEvent", self.keyPressEvent)
self.AddObserver("KeyReleaseEvent", self.KeyReleaseEvent)
self.AddObserver('LeftButtonPressEvent', self.MouseMoveOK)
self.AddObserver('LeftButtonReleaseEvent', self.MouseMoveNot)
self.AddObserver("MouseMoveEvent", self.MouseMove)
self.DetectMouseMove = 0
self.prevpos = [0, 0]
self.elec = 0
self.key = None
def keyPressEvent(self, obj, event):
self.key = obj.GetInteractor().GetKeySym()
def KeyReleaseEvent(self, obj, event):
self.key = None
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 self.key == 't':
elec = self.elec
Aelectrode = self.parent.Electrode_aAssemblys[elec]
electrode = self.parent.ElectrodeList[elec]
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToView()
coordinate.SetValue(self.prevpos[0], self.prevpos[1], 0.5)
prevviewCoord = np.asarray(coordinate.GetComputedWorldValue(self.parent.ren))
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToView()
coordinate.SetValue(clickPos[0], clickPos[1], 0.5)
click3DviewCoord = np.asarray(coordinate.GetComputedWorldValue(self.parent.ren))
a = (click3DviewCoord - prevviewCoord) / 300
position = Aelectrode.GetPosition()
Aelectrode.SetPosition(position[0] + a[0], position[1] + a[1], position[2] + a[2])
else:
if NewPickedActor:
if NewPickedActor in self.parent.Electrode_aAssemblys:
elec = self.elec
Aelectrode = self.parent.Electrode_aAssemblys[elec]
dest = np.asarray(pickerActor.GetPickPosition())
Origine = np.asarray(Aelectrode.GetPosition())
org = np.asarray(Aelectrode.GetParts().GetLastProp3D().GetCenter())
dest_E_corrd = dest - Origine
def rotation_matrix_from_vectors(vec1, vec2):
a, b = (vec1 / np.linalg.norm(vec1)).reshape(3), (vec2 / np.linalg.norm(vec2)).reshape(3)
v = np.cross(a, b)
c = np.dot(a, b)
s = np.linalg.norm(v)
kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])
rotation_matrix = np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))
return rotation_matrix
RotMat = rotation_matrix_from_vectors(org, dest_E_corrd)
vec1_rot = RotMat.dot(org)
vec1_rot = np.zeros((4, 4))
vec1_rot[:3, :3] = RotMat
vec1_rot[3, 3] = 1
vec1_rot[:3, -1] = Origine
rot = vtk.vtkMatrix4x4()
for i in range(0, 4):
for j in range(0, 4):
rot.SetElement(i, j, vec1_rot[i, j])
Aelectrode.PokeMatrix(rot)
self.GetInteractor().GetRenderWindow().Render()
self.prevpos = clickPos
else:
self.OnMouseMove()
def MouseMoveOK(self, obj, event):
clickPos = self.GetInteractor().GetEventPosition()
pickerActor = vtk.vtkPropPicker()
pickerActor.PickProp(clickPos[0], clickPos[1], self.GetDefaultRenderer())
self.prevpos = clickPos
print(self.prevpos)
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
self.elec = NewPickedActor.id
self.parent.ElectrodeList[self.elec].cylActors[-1].GetProperty().SetColor(1, 1, 0)
else:
self.OnLeftButtonDown()
def MouseMoveNot(self, obj, event):
self.DetectMouseMove = 0
self.parent.ElectrodeList[self.elec].cylActors[-1].GetProperty().SetColor(0, 0, 1)
self.OnLeftButtonUp()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Mesh_sEEGElectrodeView()
window.show()
sys.exit(app.exec())
Upvotes: 0
Views: 158