我想通过按鼠标左键来旋转或平移一个看起来像圆柱体的对象。
要旋转,我想单击尖端并在移动鼠标时使其围绕另一个尖端旋转(仍按下左按钮)。 要翻译,我想按“t”键并根据相机的计划移动对象。
旋转可以工作,但不平滑,因为一旦鼠标离开对象,方向就不再更新。 翻译似乎有效,但位置与鼠标的位置不完全对应。
当按下鼠标左键时,我保存我点击的对象。 当我按下某个键时,我还会保存该键以知道按下“t”键进行翻译。
在
MouseMove
函数中,
如果 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])
如何将对象直接定位在鼠标下方?.
我遇到的第二个问题是当鼠标离开对象时旋转停止。有没有办法即使鼠标不在 vtk assembly 对象上也可以进行旋转?
这是截图
当我单击圆柱体尖端(蓝色)时,我进入
MouseMove(self, obj, event)
功能。我不知道如何相对于鼠标位置 Aelectrode
和上一个鼠标位置 self.GetInteractor().GetEventPosition()
在
self.prevpos
对象上应用旋转
这是我现在的完整代码:
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())
翻译精度:
vtkCoordinate
和 SetCoordinateSystemToDisplay()
确保坐标位于正确的系统中,并将其转换为世界坐标以进行准确翻译。平滑旋转:
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:
clickPos = self.GetInteractor().GetEventPosition()
pickerActor = vtk.vtkPropPicker()
pickerActor.PickProp(clickPos[0], clickPos[1], self.GetDefaultRenderer())
NewPickedActor = pickerActor.GetAssembly()
if self.key == 't':
elec = self.elec
Aelectrode = self.parent.Electrode_aAssemblys[elec]
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToDisplay()
coordinate.SetValue(self.prevpos[0], self.prevpos[1], 0)
prevviewCoord = np.asarray(coordinate.GetComputedWorldValue(self.parent.ren))
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToDisplay()
coordinate.SetValue(clickPos[0], clickPos[1], 0)
click3DviewCoord = np.asarray(coordinate.GetComputedWorldValue(self.parent.ren))
translation_vector = (click3DviewCoord - prevviewCoord)
position = Aelectrode.GetPosition()
Aelectrode.SetPosition(position[0] + translation_vector[0], position[1] + translation_vector[1], position[2] + translation_vector[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)
km
at = 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
def MouseMoveOK(self, obj, event):
clickPos = self.GetInteractor().GetEventPosition()
pickerActor = vtk.vtkPropPicker()
pickerActor.PickProp(clickPos[0], clickPos[1], self.GetDefaultRenderer())
self.prevpos = clickPos
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())
鼠标移动:
MouseMoveOK 和 MouseMoveNot: