Я пытаюсь ограничить вращение boxwidget взаимодействием, чтобы позволить пользователю просто вращать прямоугольник вокруг оси вверх.
Поэтому я попробовал подход, чтобы добавить наблюдателя в " InteractionEvent ", и используйте функцию обратного вызова, чтобы повернуть вращение вокруг оси x и z.
После реализации я столкнулся с двумя проблемами:
-) Изменения при преобразовании объекта - не оказали видимого эффекта в окне (возможно, я просто работаю с копией 3D -объект?)
-) Расчет, который я сделал, изменил кватернион - но не так, как предполагалось. Было упомянуто просто свести на нет знак x и z и сохранить степень одинаковой. В результате измененного поворота я надеялся увидеть, что значения x и z становятся равными нулю ... Я предполагаю, что я неправильно понял кватернионы или что я допустил ошибку при вызовах функции vtk
Old Orientation [11.79730158 0.87192947 0.29194216 0.39307604]
Will rotate object with the following quaternion: [11.79730158 -0.87192947 -0. -0.39307604]
New Orientation [ 3.47624906 -0.17562846 0.98392544 0.03233235]
Different Access: Orientation [ 3.47624906 -0.17562846 0.98392544 0.03233235]
Результаты выдаются с помощью следующего кода:
import numpy as np
import vtk
import math
from math import atan2, sqrt, pi
from vtk.util import numpy_support
##########################################################################
def transform(T, points):
assert(points.shape[0] >= 3)
assert(T.shape == (4, 4))
points_hom = np.vstack((points[0:3], np.ones((1, points.shape[1]))))
points_hom = np.matmul(T, points_hom)
points_hom = points_hom[0:3, :] / points_hom[3, :]
points = np.vstack((points_hom, points[3:, :]))
return points
def LeftButtonPressEvent(obj, event):
t = vtk.vtkTransform()
obj.GetTransform(t)
q = np.zeros(4)
t.GetOrientationWXYZ(q)
print(f"Old Orientation {q}")
q[2] = 0
q[1:] = -q[1:]
#angle = 2.0 * atan2(np.linalg.norm(q[1:]), q[0]/180.0*pi)*180.0/pi
print(f"Will rotate object with the following quaternion: {q}")
t.RotateWXYZ(q[0], q[1:])
obj.GetProp3D().SetUserTransform( t )
t.GetOrientationWXYZ(q)
print(f"New Orientation {q}")
obj_transform = obj.GetProp3D().GetUserTransform()
obj_transform.GetOrientationWXYZ(q)
print(f"Different Access: Orientation {q}\n\n\n")
##########################################################################
def init_vtk_pointcloud(pts, point_size=2.0, points_per_row=True):
if not points_per_row:
pts = pts.T
assert (pts.shape[1] == 3 or
pts.shape[1] == 4 or
pts.shape[1] == 6)
if pts.shape[1] == 3:
color = np.ones((pts.shape[0], 3), np.uint8) * 255
elif pts.shape[1] == 4:
color = (pts[:, 3] * np.ones((3, 1)) * 255).astype(np.uint8).T
elif pts.shape[1] == 6 or pts.shape[1] == 7:
color = pts[:, 3:6]
else:
assert False, "Dimension of points is not 3 (without color), " \
"4 (with intensity) or 6/7 (with color and optionally intensity)"
vtk_point_cloud = VtkPointCloud(point_size=point_size)
vtk_point_cloud.set_points(pts[:, :3], color)
return vtk_point_cloud
##########################################################################
def init_rendering(axes_scaling, background_color, camera, point_size, other_actors,
points_per_row, pts, window_name, window_size):
vtk_point_cloud = init_vtk_pointcloud(pts, point_size, points_per_row)
# Renderer
renderer = vtk.vtkRenderer()
renderer.SetActiveCamera(camera)
renderer.SetBackground(*background_color)
# Render Window
render_window = vtk.vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetWindowName(window_name)
render_window.SetSize(*window_size)
# Add actor
renderer.AddActor(vtk_point_cloud.vtkActor)
return render_window, vtk_point_cloud
##########################################################################
def show_pointcloud(pts, window_name='Debug', points_per_row=True, background_color=[.0, .3, .4], camera=None,
axes_scaling=1.0, point_size=1.0, other_actors=[], window_size=(1280, 720), finalize=True):
renderWindow, vtk_point_cloud = init_rendering(axes_scaling, background_color, camera, point_size,
other_actors, points_per_row, pts, window_name, window_size)
# Interactor
renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetInteractorStyle(
PointSizeInteractorStyle(renderWindowInteractor, vtk_point_cloud))
renderWindowInteractor.SetRenderWindow(renderWindow)
boxActor = vtk.vtkActor()
boxWidget = vtk.vtkBoxWidget()
boxWidget.SetInteractor(renderWindowInteractor)#renderer interactor
boxWidget.SetProp3D(boxActor)#actor here
boxWidget.SetPlaceFactor(1.0)
boxWidget.PlaceWidget()
#ToDo: first interaction does not work except user scrolls beforehand... why?
boxWidget.On()
boxWidget.AddObserver("InteractionEvent", LeftButtonPressEvent)
renderWindow.Render()
renderWindowInteractor.Start()
if finalize:
renderWindow.Finalize()
##########################################################################
def create_cylinder(radius=1.0, height=1.0, pos=[0.0, 0.0, 0.0], approx_samples=1000):
perimeter = 2 * math.pi * radius
base_area = radius**2 * math.pi
area = perimeter * height + 2*base_area
# points per areal unit
pts_per_au = approx_samples / area
no_base_square_samples = int(pts_per_au*4*radius**2)
base = np.random.rand(no_base_square_samples, 2) * 2 * radius - radius
radii = np.linalg.norm(base, axis=1)
mask = radii < radius
base = base[mask, :]
max_dim = max(perimeter, height)
surface_samples = int(pts_per_au * (max_dim**2))
surface = np.random.rand(surface_samples, 2) * max_dim
mask = (surface[:, 0] < perimeter) & (surface[:, 1] < height)
surface = surface[mask, :]
surface = np.vstack((radius*np.sin(surface[:, 0]/radius), radius*np.cos(surface[:, 0]/radius),
surface[:, 1] - height/2.0)).T
top = np.hstack((base, np.ones((base.shape[0], 1)) * height/2.0))
bottom = top * (1.0, 1.0, -1.0)
pts = np.vstack((top, bottom, surface)) + pos
return pts
##########################################################################
class VtkPointCloud:
def add_point(self, point):
if self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints:
pointId = self.vtkPoints.InsertNextPoint(point[:])
self.vtkDepth.InsertNextValue(point[2])
self.vtkCells.InsertNextCell(1)
self.vtkCells.InsertCellPoint(pointId)
else:
r = np.random.randint(0, self.maxNumPoints)
self.vtkPoints.SetPoint(r, point[:])
self.vtkCells.Modified()
self.vtkPoints.Modified()
self.vtkDepth.Modified()
##########################################################################
def __init__(self, zMin=-10.0, zMax=10.0, maxNumPoints=1e6, point_size=2):
self.maxNumPoints = maxNumPoints
self.vtkPolyData = vtk.vtkPolyData()
self.clear_points()
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(self.vtkPolyData)
mapper.SetColorModeToDefault()
mapper.SetScalarRange(zMin, zMax)
mapper.SetScalarVisibility(1)
self.vtkActor = vtk.vtkActor()
self.vtkActor.SetMapper(mapper)
self.vtkActor.GetProperty().SetPointSize(point_size)
##########################################################################
def set_points(self, points, color=None):
if color is None:
if points.shape[1] == 3:
color = np.ones((points.shape[0], 3), np.uint8) * 255
elif points.shape[1] == 4:
color = value2hsvcolormap(points[:, 3])
# color = (points[:, 3] * np.ones((3, 1)) * 255).astype(np.uint8).T
elif points.shape[1] == 6 or points.shape[1] == 7:
color = points[:, 3:6]
color = np.ascontiguousarray(color)
points = np.ascontiguousarray(points[:, :3])
vtk_cell_idc = np.zeros((points.shape[0] * 2,), dtype=np.int64)
vtk_cell_idc[0::2] = 1
vtk_cell_idc[1::2] = np.arange(points.shape[0])
vtk_point_data = numpy_support.numpy_to_vtk(num_array=points, deep=True, array_type=vtk.VTK_FLOAT)
self.vtkPoints.SetData(vtk_point_data)
if color is not None:
vtk_color_data = numpy_support.numpy_to_vtk(num_array=color, deep=True, array_type=vtk.VTK_UNSIGNED_CHAR)
vtk_color_data.SetName('Color')
self.vtkPolyData.GetPointData().SetScalars(vtk_color_data)
self.vtkPolyData.GetPointData().SetActiveScalars('Color')
vtk_cell_data = numpy_support.numpy_to_vtk(num_array=vtk_cell_idc, deep=True, array_type=vtk.VTK_ID_TYPE)
self.vtkCells.SetCells(points.shape[0], vtk_cell_data)
self.vtkPoints.Modified()
self.vtkCells.Modified()
self.vtkPolyData.Modified()
##########################################################################
def clear_points(self):
self.vtkPoints = vtk.vtkPoints()
self.vtkCells = vtk.vtkCellArray()
self.vtkPolyData.SetPoints(self.vtkPoints)
self.vtkPolyData.SetVerts(self.vtkCells)
##########################################################################
class PointSizeInteractorStyle(vtk.vtkInteractorStyleTrackballCamera):
def __init__(self, parent, point_cloud=None, max_pt_size=20):
self.AddObserver("MouseWheelForwardEvent", self.mouse_wheel_forward_event)
self.AddObserver("MouseWheelBackwardEvent", self.mouse_wheel_backward_event)
self.AddObserver("LeftButtonPressEvent", self.LeftButtonPressEvent)
self._parent = parent
self._point_cloud = point_cloud
self._max_pt_size = max_pt_size
##########################################################################
def mouse_wheel_forward_event(self, obj, event):
if self._parent.GetAltKey():
new_point_size = self._point_cloud_actor.GetProperty().GetPointSize() + 1
new_point_size = self._max_pt_size if new_point_size > self._max_pt_size else new_point_size
self._point_cloud.vtkActor.GetProperty().SetPointSize(new_point_size)
self._parent.GetRenderWindow().Render()
else:
self.OnMouseWheelForward()
return
def LeftButtonPressEvent(self, obj, event):
print("Global interactor")
self.OnLeftButtonDown()
##########################################################################
def mouse_wheel_backward_event(self, obj, event):
if self._parent.GetAltKey():
new_point_size = self._point_cloud_actor.GetProperty().GetPointSize() - 1
new_point_size = 1 if new_point_size < 1 else new_point_size
self._point_cloud.vtkActor.GetProperty().SetPointSize(new_point_size)
self._parent.GetRenderWindow().Render()
else:
self.OnMouseWheelBackward()
return
##########################################################################
class PointCloudInteractorStyle(PointSizeInteractorStyle):
def __init__(self, parent, image_actor, point_cloud, caption_renderer, pc_gen=None, max_pt_size=20):
# self.AddObserver("KeyPressEvent", self.key_press_event)
self._parent = parent
self._image_actor = image_actor
self._pc_gen = pc_gen
self._caption_renderer = caption_renderer
self._caption_renderer.RemoveAllViewProps()
point_cloud.set_points(pts)
super().__init__(parent, point_cloud)
#########################################################################
def main():
point_cloud_cube = create_cylinder()
# point_cloud_cube = point_cloud_cube + np.random.rand(*point_cloud_cube.shape) * 0.02
R, _ = np.linalg.qr(np.random.rand(3, 3))
#point_cloud_cube = transformation.rotate(R, point_cloud_cube.T).T
point_cloud = np.vstack((point_cloud_cube))
color = np.ones((point_cloud.shape[0], 3), dtype=point_cloud.dtype) * 255
camera = vtk.vtkCamera()
camera.SetViewUp(0, -1, 0)
camera.SetPosition(2, -1, -4)
camera.SetFocalPoint(0, 0, 0)
point_cloud = np.hstack((point_cloud[:, :3], color))
show_pointcloud(point_cloud, 'Test', camera=camera, axes_scaling=0.5)
if __name__ == '__main__':
main()