如何在vtk环境中旋转或平移鼠标位置处的对象?

问题描述 投票:0回答:1

我想通过按鼠标左键来旋转或平移一个看起来像圆柱体的对象。

要旋转,我想单击尖端并在移动鼠标时使其围绕另一个尖端旋转(仍按下左按钮)。 要翻译,我想按“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 对象上也可以进行旋转?

这是截图

enter image description here

当我单击圆柱体尖端(蓝色)时,我进入

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())
python rotation mouse vtk
1个回答
0
投票

所做的更改:

  1. 翻译精度:

    • 使用
      vtkCoordinate
      SetCoordinateSystemToDisplay()
      确保坐标位于正确的系统中,并将其转换为世界坐标以进行准确翻译。
  2. 平滑旋转

    • 即使鼠标离开对象,也可以通过更新对象的变换矩阵来确保旋转顺利进行。
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())

说明:

  • 鼠标移动:

    • 检查是否按下平移键(“t”)并通过将显示坐标转换为世界坐标来应用平移。
    • 对于旋转,它计算旋转矩阵并将其应用于对象。
  • MouseMoveOK 和 MouseMoveNot:

    • 处理启用和禁用鼠标移动检测并更新对象的颜色以获得视觉反馈。
© www.soinside.com 2019 - 2024. All rights reserved.