add vtkHandlerWidget
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
set( TESTS
|
set( TESTS
|
||||||
vtkViewerTest
|
vtkViewerTest
|
||||||
vtkContainerBoxTest
|
vtkContainerBoxTest
|
||||||
|
vtkHandlerWidget
|
||||||
# vtkVoxImageTest
|
# vtkVoxImageTest
|
||||||
# vtkTriangleMeshTest
|
# vtkTriangleMeshTest
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
include $(top_srcdir)/Common.am
|
|
||||||
include ../Vtk.am
|
|
||||||
|
|
||||||
#AM_DEFAULT_SOURCE_EXT = .cpp
|
|
||||||
|
|
||||||
# if HAVE_CHECK
|
|
||||||
TESTS = \
|
|
||||||
vtkViewerTest \
|
|
||||||
vtkContainerBoxTest \
|
|
||||||
vtkMuonScatter \
|
|
||||||
vtkStructuredGridTest \
|
|
||||||
vtkVoxRaytracerTest \
|
|
||||||
vtkVoxImageTest
|
|
||||||
# vtkTriangleMeshTest
|
|
||||||
|
|
||||||
all: $(TESTS)
|
|
||||||
|
|
||||||
LDADD = $(top_srcdir)/libmutom-${PACKAGE_VERSION}.la $(AM_LIBS_ALL)
|
|
||||||
check_PROGRAMS = $(TESTS)
|
|
||||||
|
|
||||||
116
src/Vtk/testing/vtkHandlerWidget.cpp
Normal file
116
src/Vtk/testing/vtkHandlerWidget.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3.0 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
#include "Math/ContainerBox.h"
|
||||||
|
#include "Vtk/uLibVtkViewer.h"
|
||||||
|
#include "Vtk/vtkContainerBox.h"
|
||||||
|
#include "Vtk/vtkHandlerWidget.h"
|
||||||
|
#include "testing-prototype.h"
|
||||||
|
|
||||||
|
#include <vtkProp3D.h>
|
||||||
|
#include <vtkPropAssembly.h>
|
||||||
|
#include <vtkPropCollection.h>
|
||||||
|
#include <vtkSmartPointer.h>
|
||||||
|
#include <vtkCallbackCommand.h>
|
||||||
|
#include <vtkRenderWindowInteractor.h>
|
||||||
|
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
BEGIN_TESTING(vtkHandlerWidget with ContainerBox);
|
||||||
|
|
||||||
|
// 1. Create a ContainerBox (Math object)
|
||||||
|
ContainerBox box;
|
||||||
|
box.Scale(Vector3f(2.0, 3.0, 4.0));
|
||||||
|
box.SetPosition(Vector3f(1.0, 1.0, 1.0));
|
||||||
|
|
||||||
|
// 2. Wrap it in a Vtk::vtkContainerBox (Vtk Puppet)
|
||||||
|
Vtk::vtkContainerBox v_box(&box);
|
||||||
|
v_box.SetRepresentation(Vtk::Puppet::Surface);
|
||||||
|
v_box.SetOpacity(0.5);
|
||||||
|
|
||||||
|
// 3. Setup the Viewer
|
||||||
|
Vtk::Viewer viewer;
|
||||||
|
viewer.AddPuppet(v_box);
|
||||||
|
|
||||||
|
// 4. Create and setup the vtkHandlerWidget
|
||||||
|
vtkSmartPointer<Vtk::vtkHandlerWidget> handler =
|
||||||
|
vtkSmartPointer<Vtk::vtkHandlerWidget>::New();
|
||||||
|
|
||||||
|
handler->SetInteractor(viewer.GetInteractor());
|
||||||
|
|
||||||
|
// Get the prop from the puppet and cast it to vtkProp3D
|
||||||
|
vtkProp *v_prop = v_box.GetProp();
|
||||||
|
vtkProp3D *prop = vtkProp3D::SafeDownCast(v_prop);
|
||||||
|
if (!prop) {
|
||||||
|
// If it's a PropAssembly, try to get the first part (the cube)
|
||||||
|
::vtkPropAssembly *assembly = ::vtkPropAssembly::SafeDownCast(v_prop);
|
||||||
|
if (assembly && assembly->GetParts()->GetNumberOfItems() > 0) {
|
||||||
|
assembly->GetParts()->InitTraversal();
|
||||||
|
prop = vtkProp3D::SafeDownCast(assembly->GetParts()->GetNextProp());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST1(prop != nullptr);
|
||||||
|
|
||||||
|
handler->SetProp3D(prop);
|
||||||
|
handler->PlaceWidget();
|
||||||
|
handler->EnabledOn();
|
||||||
|
|
||||||
|
// 5. Add a key callback to switch modes
|
||||||
|
auto key_callback = vtkSmartPointer<vtkCallbackCommand>::New();
|
||||||
|
key_callback->SetCallback([](vtkObject *caller, unsigned long, void *clientData, void *) {
|
||||||
|
auto interactor = static_cast<vtkRenderWindowInteractor *>(caller);
|
||||||
|
auto h = static_cast<Vtk::vtkHandlerWidget *>(clientData);
|
||||||
|
std::string key = interactor->GetKeySym();
|
||||||
|
if (key == "g") {
|
||||||
|
std::cout << "Switching to GLOBAL frame" << std::endl;
|
||||||
|
h->SetReferenceFrame(Vtk::vtkHandlerWidget::GLOBAL);
|
||||||
|
} else if (key == "l") {
|
||||||
|
std::cout << "Switching to LOCAL frame" << std::endl;
|
||||||
|
h->SetReferenceFrame(Vtk::vtkHandlerWidget::LOCAL);
|
||||||
|
} else if (key == "c") {
|
||||||
|
std::cout << "Switching to CENTER frame" << std::endl;
|
||||||
|
h->SetReferenceFrame(Vtk::vtkHandlerWidget::CENTER);
|
||||||
|
} else if (key == "k") {
|
||||||
|
std::cout << "Switching to CENTER_LOCAL frame" << std::endl;
|
||||||
|
h->SetReferenceFrame(Vtk::vtkHandlerWidget::CENTER_LOCAL);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
key_callback->SetClientData(handler.GetPointer());
|
||||||
|
viewer.GetInteractor()->AddObserver(vtkCommand::CharEvent, key_callback);
|
||||||
|
|
||||||
|
// 6. Start interaction if not in a continuous integration environment
|
||||||
|
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {
|
||||||
|
std::cout << "Interactive test: use the gizmos to transform the ContainerBox"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "Keys: [g] GLOBAL, [l] LOCAL, [c] CENTER, [k] CENTER_LOCAL" << std::endl;
|
||||||
|
viewer.Start();
|
||||||
|
} else {
|
||||||
|
std::cout << "Non-interactive test: widget initialized successfully"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
END_TESTING;
|
||||||
|
}
|
||||||
@@ -29,16 +29,19 @@
|
|||||||
#include <vtkArrowSource.h>
|
#include <vtkArrowSource.h>
|
||||||
#include <vtkCallbackCommand.h>
|
#include <vtkCallbackCommand.h>
|
||||||
#include <vtkCamera.h>
|
#include <vtkCamera.h>
|
||||||
|
#include <vtkCellPicker.h>
|
||||||
#include <vtkConeSource.h>
|
#include <vtkConeSource.h>
|
||||||
#include <vtkCubeSource.h>
|
#include <vtkCubeSource.h>
|
||||||
#include <vtkInteractorObserver.h>
|
#include <vtkInteractorObserver.h>
|
||||||
#include <vtkMath.h>
|
#include <vtkMath.h>
|
||||||
#include <vtkMatrix4x4.h>
|
#include <vtkMatrix4x4.h>
|
||||||
#include <vtkObjectFactory.h>
|
#include <vtkObjectFactory.h>
|
||||||
|
#include <vtkPlane.h>
|
||||||
#include <vtkPolyDataMapper.h>
|
#include <vtkPolyDataMapper.h>
|
||||||
#include <vtkProp3D.h>
|
#include <vtkProp3D.h>
|
||||||
#include <vtkPropPicker.h>
|
#include <vtkPropPicker.h>
|
||||||
#include <vtkProperty.h>
|
#include <vtkProperty.h>
|
||||||
|
#include <vtkRegularPolygonSource.h>
|
||||||
#include <vtkRenderWindow.h>
|
#include <vtkRenderWindow.h>
|
||||||
#include <vtkRenderWindowInteractor.h>
|
#include <vtkRenderWindowInteractor.h>
|
||||||
#include <vtkRenderer.h>
|
#include <vtkRenderer.h>
|
||||||
@@ -52,10 +55,19 @@ vtkStandardNewMacro(vtkHandlerWidget);
|
|||||||
|
|
||||||
vtkHandlerWidget::vtkHandlerWidget() {
|
vtkHandlerWidget::vtkHandlerWidget() {
|
||||||
this->Interaction = IDLE;
|
this->Interaction = IDLE;
|
||||||
this->m_Picker = vtkSmartPointer<::vtkPropPicker>::New();
|
this->m_Picker = vtkSmartPointer<::vtkCellPicker>::New();
|
||||||
|
this->m_Picker->SetTolerance(0.01); // Increased tolerance for thin gizmos
|
||||||
this->m_InitialTransform = vtkSmartPointer<::vtkTransform>::New();
|
this->m_InitialTransform = vtkSmartPointer<::vtkTransform>::New();
|
||||||
this->EventCallbackCommand->SetCallback(vtkHandlerWidget::ProcessEvents);
|
this->EventCallbackCommand->SetCallback(vtkHandlerWidget::ProcessEvents);
|
||||||
this->EventCallbackCommand->SetClientData(this);
|
this->EventCallbackCommand->SetClientData(this);
|
||||||
|
this->m_Frame = LOCAL;
|
||||||
|
this->m_HighlightedProp = nullptr;
|
||||||
|
this->m_ClipPlane = vtkSmartPointer<::vtkPlane>::New();
|
||||||
|
this->m_OverlayRenderer = vtkSmartPointer<::vtkRenderer>::New();
|
||||||
|
this->m_OverlayRenderer->SetLayer(1);
|
||||||
|
this->m_OverlayRenderer->EraseOff();
|
||||||
|
this->m_OverlayRenderer->InteractiveOff();
|
||||||
|
this->Priority = 50.0; // Higher priority to beat camera style
|
||||||
this->CreateGizmos();
|
this->CreateGizmos();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,23 +97,39 @@ void vtkHandlerWidget::SetEnabled(int enabling) {
|
|||||||
|
|
||||||
// Add observers
|
// Add observers
|
||||||
::vtkRenderWindowInteractor *i = this->Interactor;
|
::vtkRenderWindowInteractor *i = this->Interactor;
|
||||||
|
this->SetPriority(this->Priority);
|
||||||
i->AddObserver(::vtkCommand::LeftButtonPressEvent,
|
i->AddObserver(::vtkCommand::LeftButtonPressEvent,
|
||||||
this->EventCallbackCommand, this->Priority);
|
this->EventCallbackCommand, this->Priority);
|
||||||
i->AddObserver(::vtkCommand::LeftButtonReleaseEvent,
|
i->AddObserver(::vtkCommand::LeftButtonReleaseEvent,
|
||||||
this->EventCallbackCommand, this->Priority);
|
this->EventCallbackCommand, this->Priority);
|
||||||
i->AddObserver(::vtkCommand::MouseMoveEvent, this->EventCallbackCommand,
|
i->AddObserver(::vtkCommand::MouseMoveEvent, this->EventCallbackCommand,
|
||||||
this->Priority);
|
this->Priority);
|
||||||
|
i->AddObserver(::vtkCommand::RenderEvent, this->EventCallbackCommand,
|
||||||
|
this->Priority);
|
||||||
|
|
||||||
this->UpdateGizmoPosition();
|
this->UpdateGizmoPosition();
|
||||||
this->CurrentRenderer->AddActor(m_AxesX);
|
|
||||||
this->CurrentRenderer->AddActor(m_AxesY);
|
// Manage Layers
|
||||||
this->CurrentRenderer->AddActor(m_AxesZ);
|
auto win = this->Interactor->GetRenderWindow();
|
||||||
this->CurrentRenderer->AddActor(m_RotX);
|
if (win->GetNumberOfLayers() < 2) {
|
||||||
this->CurrentRenderer->AddActor(m_RotY);
|
win->SetNumberOfLayers(2);
|
||||||
this->CurrentRenderer->AddActor(m_RotZ);
|
}
|
||||||
this->CurrentRenderer->AddActor(m_ScaleX);
|
|
||||||
this->CurrentRenderer->AddActor(m_ScaleY);
|
// Sync Viewport and Camera
|
||||||
this->CurrentRenderer->AddActor(m_ScaleZ);
|
this->m_OverlayRenderer->SetViewport(this->CurrentRenderer->GetViewport());
|
||||||
|
this->m_OverlayRenderer->SetActiveCamera(this->CurrentRenderer->GetActiveCamera());
|
||||||
|
win->AddRenderer(this->m_OverlayRenderer);
|
||||||
|
|
||||||
|
this->m_OverlayRenderer->AddActor(m_AxesX);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_AxesY);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_AxesZ);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_RotX);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_RotY);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_RotZ);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_RotCam);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_ScaleX);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_ScaleY);
|
||||||
|
this->m_OverlayRenderer->AddActor(m_ScaleZ);
|
||||||
|
|
||||||
this->InvokeEvent(::vtkCommand::EnableEvent, nullptr);
|
this->InvokeEvent(::vtkCommand::EnableEvent, nullptr);
|
||||||
} else {
|
} else {
|
||||||
@@ -109,18 +137,12 @@ void vtkHandlerWidget::SetEnabled(int enabling) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this->Enabled = 0;
|
this->Enabled = 0;
|
||||||
|
this->Highlight(nullptr);
|
||||||
this->Interactor->RemoveObserver(this->EventCallbackCommand);
|
this->Interactor->RemoveObserver(this->EventCallbackCommand);
|
||||||
if (this->CurrentRenderer) {
|
if (this->Interactor->GetRenderWindow()) {
|
||||||
this->CurrentRenderer->RemoveActor(m_AxesX);
|
this->Interactor->GetRenderWindow()->RemoveRenderer(this->m_OverlayRenderer);
|
||||||
this->CurrentRenderer->RemoveActor(m_AxesY);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_AxesZ);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_RotX);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_RotY);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_RotZ);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_ScaleX);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_ScaleY);
|
|
||||||
this->CurrentRenderer->RemoveActor(m_ScaleZ);
|
|
||||||
}
|
}
|
||||||
|
this->m_OverlayRenderer->RemoveAllViewProps();
|
||||||
this->InvokeEvent(::vtkCommand::DisableEvent, nullptr);
|
this->InvokeEvent(::vtkCommand::DisableEvent, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,12 +159,22 @@ void vtkHandlerWidget::ProcessEvents(::vtkObject *caller, unsigned long event,
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
case ::vtkCommand::LeftButtonPressEvent:
|
case ::vtkCommand::LeftButtonPressEvent:
|
||||||
self->OnLeftButtonDown();
|
self->OnLeftButtonDown();
|
||||||
|
if (self->Interaction != ::uLib::Vtk::vtkHandlerWidget::IDLE)
|
||||||
|
self->EventCallbackCommand->SetAbortFlag(1);
|
||||||
break;
|
break;
|
||||||
case ::vtkCommand::LeftButtonReleaseEvent:
|
case ::vtkCommand::LeftButtonReleaseEvent:
|
||||||
self->OnLeftButtonUp();
|
self->OnLeftButtonUp();
|
||||||
|
if (self->EventCallbackCommand->GetAbortFlag()) {
|
||||||
|
// Don't let release bleed if press was captured
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ::vtkCommand::MouseMoveEvent:
|
case ::vtkCommand::MouseMoveEvent:
|
||||||
self->OnMouseMove();
|
self->OnMouseMove();
|
||||||
|
if (self->Interaction != ::uLib::Vtk::vtkHandlerWidget::IDLE)
|
||||||
|
self->EventCallbackCommand->SetAbortFlag(1);
|
||||||
|
break;
|
||||||
|
case ::vtkCommand::RenderEvent:
|
||||||
|
self->UpdateGizmoPosition();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,8 +187,9 @@ void vtkHandlerWidget::OnLeftButtonDown() {
|
|||||||
this->CurrentRenderer = this->Interactor->FindPokedRenderer(X, Y);
|
this->CurrentRenderer = this->Interactor->FindPokedRenderer(X, Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_Picker->Pick(X, Y, 0.0, this->CurrentRenderer);
|
this->m_Picker->Pick(X, Y, 0.0, this->m_OverlayRenderer);
|
||||||
::vtkProp *prop = this->m_Picker->GetViewProp();
|
::vtkProp *prop = this->m_Picker->GetViewProp();
|
||||||
|
this->m_Picker->GetPickPosition(this->m_StartPickPosition);
|
||||||
|
|
||||||
if (!prop)
|
if (!prop)
|
||||||
return;
|
return;
|
||||||
@@ -180,6 +213,8 @@ void vtkHandlerWidget::OnLeftButtonDown() {
|
|||||||
this->Interaction = SCALE_Y;
|
this->Interaction = SCALE_Y;
|
||||||
else if (prop == m_ScaleZ)
|
else if (prop == m_ScaleZ)
|
||||||
this->Interaction = SCALE_Z;
|
this->Interaction = SCALE_Z;
|
||||||
|
else if (prop == m_RotCam)
|
||||||
|
this->Interaction = ROT_CAM;
|
||||||
|
|
||||||
if (this->Interaction != IDLE) {
|
if (this->Interaction != IDLE) {
|
||||||
this->StartEventPosition[0] = X;
|
this->StartEventPosition[0] = X;
|
||||||
@@ -198,62 +233,186 @@ void vtkHandlerWidget::OnLeftButtonUp() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this->Interaction = IDLE;
|
this->Interaction = IDLE;
|
||||||
|
this->EventCallbackCommand->SetAbortFlag(1);
|
||||||
this->InvokeEvent(::vtkCommand::EndInteractionEvent, nullptr);
|
this->InvokeEvent(::vtkCommand::EndInteractionEvent, nullptr);
|
||||||
this->Interactor->Render();
|
this->Interactor->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void vtkHandlerWidget::OnMouseMove() {
|
void vtkHandlerWidget::OnMouseMove() {
|
||||||
if (this->Interaction == IDLE || !this->Prop3D)
|
if (!this->Prop3D || !this->CurrentRenderer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int X = this->Interactor->GetEventPosition()[0];
|
int X = this->Interactor->GetEventPosition()[0];
|
||||||
int Y = this->Interactor->GetEventPosition()[1];
|
int Y = this->Interactor->GetEventPosition()[1];
|
||||||
|
|
||||||
|
if (this->Interaction == IDLE) {
|
||||||
|
this->m_Picker->Pick(X, Y, 0.0, this->m_OverlayRenderer);
|
||||||
|
::vtkProp *prop = this->m_Picker->GetViewProp();
|
||||||
|
this->Highlight(prop);
|
||||||
|
this->UpdateGizmoPosition(); // Ensure camera adjustments happen
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
double dx = X - this->StartEventPosition[0];
|
double dx = X - this->StartEventPosition[0];
|
||||||
double dy = Y - this->StartEventPosition[1];
|
double dy = Y - this->StartEventPosition[1];
|
||||||
|
|
||||||
vtkSmartPointer<::vtkTransform> t = vtkSmartPointer<::vtkTransform>::New();
|
// Get current gizmo properties from its actors
|
||||||
t->PostMultiply();
|
vtkMatrix4x4 *gizmo_mat = m_AxesX->GetUserMatrix();
|
||||||
t->SetMatrix(this->m_InitialTransform->GetMatrix());
|
if (!gizmo_mat)
|
||||||
|
return;
|
||||||
|
|
||||||
double factor = 0.01;
|
double gpos[3] = {gizmo_mat->GetElement(0, 3), gizmo_mat->GetElement(1, 3),
|
||||||
|
gizmo_mat->GetElement(2, 3)};
|
||||||
|
|
||||||
|
// Normalized gizmo axes
|
||||||
|
double gx[3] = {gizmo_mat->GetElement(0, 0), gizmo_mat->GetElement(1, 0),
|
||||||
|
gizmo_mat->GetElement(2, 0)};
|
||||||
|
double gy[3] = {gizmo_mat->GetElement(0, 1), gizmo_mat->GetElement(1, 1),
|
||||||
|
gizmo_mat->GetElement(2, 1)};
|
||||||
|
double gz[3] = {gizmo_mat->GetElement(0, 2), gizmo_mat->GetElement(1, 2),
|
||||||
|
gizmo_mat->GetElement(2, 2)};
|
||||||
|
|
||||||
|
auto get_motion_magnitude = [&](double axis[3], double origin[3]) {
|
||||||
|
double p1[3] = {origin[0], origin[1], origin[2]};
|
||||||
|
double p2[3] = {origin[0] + axis[0], origin[1] + axis[1],
|
||||||
|
origin[2] + axis[2]};
|
||||||
|
|
||||||
|
double d1[3], d2[3];
|
||||||
|
this->ComputeWorldToDisplay(this->CurrentRenderer, p1[0], p1[1], p1[2], d1);
|
||||||
|
this->ComputeWorldToDisplay(this->CurrentRenderer, p2[0], p2[1], p2[2], d2);
|
||||||
|
|
||||||
|
double v[2] = {d2[0] - d1[0], d2[1] - d1[1]};
|
||||||
|
double v_mag_sq = v[0] * v[0] + v[1] * v[1];
|
||||||
|
if (v_mag_sq < 1.0)
|
||||||
|
return 0.0;
|
||||||
|
return (dx * v[0] + dy * v[1]) / v_mag_sq;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto get_rotation_magnitude = [&](double axis[3]) {
|
||||||
|
// Tangent at pick point
|
||||||
|
double v_pick[3] = {this->m_StartPickPosition[0] - gpos[0],
|
||||||
|
this->m_StartPickPosition[1] - gpos[1],
|
||||||
|
this->m_StartPickPosition[2] - gpos[2]};
|
||||||
|
double tangent[3];
|
||||||
|
vtkMath::Cross(axis, v_pick, tangent);
|
||||||
|
vtkMath::Normalize(tangent);
|
||||||
|
|
||||||
|
double p1[3] = {this->m_StartPickPosition[0], this->m_StartPickPosition[1], this->m_StartPickPosition[2]};
|
||||||
|
double p2[3] = {p1[0] + tangent[0], p1[1] + tangent[1], p1[2] + tangent[2]};
|
||||||
|
|
||||||
|
double d1[3], d2[3];
|
||||||
|
this->ComputeWorldToDisplay(this->CurrentRenderer, p1[0], p1[1], p1[2], d1);
|
||||||
|
this->ComputeWorldToDisplay(this->CurrentRenderer, p2[0], p2[1], p2[2], d2);
|
||||||
|
|
||||||
|
double v[2] = {d2[0] - d1[0], d2[1] - d1[1]};
|
||||||
|
double v_mag_sq = v[0] * v[0] + v[1] * v[1];
|
||||||
|
if (v_mag_sq < 1.0) return 0.0;
|
||||||
|
|
||||||
|
// Return pixels along tangent (mapped to degrees)
|
||||||
|
return (dx * v[0] + dy * v[1]) / sqrt(v_mag_sq);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a transform that represents the operation in Gizmo-local space
|
||||||
|
vtkNew<vtkTransform> delta;
|
||||||
|
delta->PostMultiply();
|
||||||
|
delta->Translate(-gpos[0], -gpos[1], -gpos[2]);
|
||||||
|
|
||||||
|
// Orientation of the gizmo
|
||||||
|
vtkNew<vtkMatrix4x4> orient;
|
||||||
|
orient->Identity();
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
orient->SetElement(i, 0, gx[i]);
|
||||||
|
orient->SetElement(i, 1, gy[i]);
|
||||||
|
orient->SetElement(i, 2, gz[i]);
|
||||||
|
}
|
||||||
|
vtkNew<vtkMatrix4x4> orient_inv;
|
||||||
|
vtkMatrix4x4::Invert(orient, orient_inv);
|
||||||
|
delta->Concatenate(orient_inv);
|
||||||
|
|
||||||
|
// Now the coordinate system is at gizmo center, aligned with its axes.
|
||||||
|
double mag = 0;
|
||||||
switch (this->Interaction) {
|
switch (this->Interaction) {
|
||||||
case TRANS_X:
|
case TRANS_X:
|
||||||
t->Translate(dx * factor, 0, 0);
|
mag = get_motion_magnitude(gx, gpos);
|
||||||
|
delta->Translate(mag, 0, 0);
|
||||||
break;
|
break;
|
||||||
case TRANS_Y:
|
case TRANS_Y:
|
||||||
t->Translate(0, dy * factor, 0);
|
mag = get_motion_magnitude(gy, gpos);
|
||||||
|
delta->Translate(0, mag, 0);
|
||||||
break;
|
break;
|
||||||
case TRANS_Z:
|
case TRANS_Z:
|
||||||
t->Translate(0, 0, dy * factor);
|
mag = get_motion_magnitude(gz, gpos);
|
||||||
|
delta->Translate(0, 0, mag);
|
||||||
break;
|
break;
|
||||||
case ROT_X:
|
case ROT_X:
|
||||||
t->RotateX(dy);
|
mag = get_rotation_magnitude(gx);
|
||||||
|
delta->RotateX(mag);
|
||||||
break;
|
break;
|
||||||
case ROT_Y:
|
case ROT_Y:
|
||||||
t->RotateY(dx);
|
mag = get_rotation_magnitude(gy);
|
||||||
|
delta->RotateY(mag);
|
||||||
break;
|
break;
|
||||||
case ROT_Z:
|
case ROT_Z:
|
||||||
t->RotateZ(dx);
|
mag = get_rotation_magnitude(gz);
|
||||||
|
delta->RotateZ(mag);
|
||||||
break;
|
break;
|
||||||
case SCALE_X:
|
case SCALE_X:
|
||||||
t->Scale(std::max(0.1, 1.0 + dx * factor), 1.0, 1.0);
|
mag = get_motion_magnitude(gx, gpos);
|
||||||
|
delta->Scale(std::max(0.1, 1.0 + mag), 1.0, 1.0);
|
||||||
break;
|
break;
|
||||||
case SCALE_Y:
|
case SCALE_Y:
|
||||||
t->Scale(1.0, std::max(0.1, 1.0 + dy * factor), 1.0);
|
mag = get_motion_magnitude(gy, gpos);
|
||||||
|
delta->Scale(1.0, std::max(0.1, 1.0 + mag), 1.0);
|
||||||
break;
|
break;
|
||||||
case SCALE_Z:
|
case SCALE_Z:
|
||||||
t->Scale(1.0, 1.0, std::max(0.1, 1.0 + dy * factor));
|
mag = get_motion_magnitude(gz, gpos);
|
||||||
|
delta->Scale(1.0, 1.0, std::max(0.1, 1.0 + mag));
|
||||||
|
break;
|
||||||
|
case ROT_CAM: {
|
||||||
|
// Rotate around camera-viewer axis
|
||||||
|
double camPos[3];
|
||||||
|
this->CurrentRenderer->GetActiveCamera()->GetPosition(camPos);
|
||||||
|
double dir[3] = {camPos[0] - gpos[0], camPos[1] - gpos[1],
|
||||||
|
camPos[2] - gpos[2]};
|
||||||
|
vtkMath::Normalize(dir);
|
||||||
|
|
||||||
|
// Orientation of the gizmo is currently orient
|
||||||
|
// But delta is in gizmo-local.
|
||||||
|
// In gizmo-local, the camera direction is:
|
||||||
|
double dir4[4] = {dir[0], dir[1], dir[2], 0.0};
|
||||||
|
double dir_local4[4];
|
||||||
|
orient_inv->MultiplyPoint(dir4, dir_local4);
|
||||||
|
double axis_local[3] = {dir_local4[0], dir_local4[1], dir_local4[2]};
|
||||||
|
mag = get_rotation_magnitude(dir); // Tangent calculated in world space
|
||||||
|
delta->RotateWXYZ(mag, axis_local[0], axis_local[1], axis_local[2]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->Prop3D->SetUserMatrix(t->GetMatrix());
|
// Back to world space
|
||||||
|
delta->Concatenate(orient);
|
||||||
|
delta->Translate(gpos[0], gpos[1], gpos[2]);
|
||||||
|
|
||||||
|
// Apply delta on top of the initial object state
|
||||||
|
vtkNew<vtkTransform> final_t;
|
||||||
|
final_t->PostMultiply();
|
||||||
|
final_t->SetMatrix(this->m_InitialTransform->GetMatrix());
|
||||||
|
final_t->Concatenate(delta);
|
||||||
|
|
||||||
|
this->Prop3D->SetUserMatrix(final_t->GetMatrix());
|
||||||
this->UpdateGizmoPosition();
|
this->UpdateGizmoPosition();
|
||||||
|
|
||||||
this->InvokeEvent(::vtkCommand::InteractionEvent, nullptr);
|
this->InvokeEvent(::vtkCommand::InteractionEvent, nullptr);
|
||||||
this->Interactor->Render();
|
this->Interactor->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vtkHandlerWidget::SetReferenceFrame(ReferenceFrame frame) {
|
||||||
|
this->m_Frame = frame;
|
||||||
|
this->UpdateGizmoPosition();
|
||||||
|
if (this->Interactor)
|
||||||
|
this->Interactor->Render();
|
||||||
|
}
|
||||||
|
|
||||||
void vtkHandlerWidget::PlaceWidget(double bounds[6]) {
|
void vtkHandlerWidget::PlaceWidget(double bounds[6]) {
|
||||||
(void)bounds;
|
(void)bounds;
|
||||||
this->UpdateGizmoPosition();
|
this->UpdateGizmoPosition();
|
||||||
@@ -277,47 +436,50 @@ void vtkHandlerWidget::GetTransform(::vtkTransform *t) {
|
|||||||
void vtkHandlerWidget::CreateGizmos() {
|
void vtkHandlerWidget::CreateGizmos() {
|
||||||
auto create_arrow = [](double dir[3], double color[3]) {
|
auto create_arrow = [](double dir[3], double color[3]) {
|
||||||
auto arrow = vtkSmartPointer<::vtkArrowSource>::New();
|
auto arrow = vtkSmartPointer<::vtkArrowSource>::New();
|
||||||
|
arrow->SetTipLength(0.2);
|
||||||
|
arrow->SetTipRadius(0.06);
|
||||||
|
arrow->SetShaftRadius(0.015);
|
||||||
|
|
||||||
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
||||||
mapper->SetInputConnection(arrow->GetOutputPort());
|
mapper->SetInputConnection(arrow->GetOutputPort());
|
||||||
auto actor = vtkSmartPointer<::vtkActor>::New();
|
auto actor = vtkSmartPointer<::vtkActor>::New();
|
||||||
actor->SetMapper(mapper);
|
actor->SetMapper(mapper);
|
||||||
actor->GetProperty()->SetColor(color);
|
actor->GetProperty()->SetColor(color);
|
||||||
|
actor->GetProperty()->SetLighting(0); // Saturated colors, no shadows
|
||||||
|
|
||||||
auto t = vtkSmartPointer<::vtkTransform>::New();
|
|
||||||
if (dir[1] > 0)
|
if (dir[1] > 0)
|
||||||
t->RotateZ(90);
|
actor->RotateZ(90);
|
||||||
else if (dir[2] > 0)
|
else if (dir[2] > 0)
|
||||||
t->RotateY(-90);
|
actor->RotateY(-90);
|
||||||
actor->SetUserTransform(t);
|
|
||||||
return actor;
|
return actor;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto create_ring = [](int axis, double color[3]) {
|
auto create_ring = [&](int axis, double color[3]) {
|
||||||
auto arc = vtkSmartPointer<::vtkArcSource>::New();
|
auto circle = vtkSmartPointer<::vtkRegularPolygonSource>::New();
|
||||||
arc->SetCenter(0, 0, 0);
|
circle->SetNumberOfSides(64);
|
||||||
arc->SetResolution(64);
|
circle->SetRadius(1.0);
|
||||||
if (axis == 0) {
|
circle->SetCenter(0, 0, 0);
|
||||||
arc->SetPoint1(0, 1, 0);
|
circle->GeneratePolygonOff();
|
||||||
arc->SetPoint2(0, -1, 0);
|
circle->GeneratePolylineOn();
|
||||||
} else if (axis == 1) {
|
|
||||||
arc->SetPoint1(1, 0, 0);
|
if (axis == 0) circle->SetNormal(1, 0, 0);
|
||||||
arc->SetPoint2(-1, 0, 0);
|
else if (axis == 1) circle->SetNormal(0, 1, 0);
|
||||||
} else if (axis == 2) {
|
else if (axis == 2) circle->SetNormal(0, 0, 1);
|
||||||
arc->SetPoint1(1, 0, 0);
|
|
||||||
arc->SetPoint2(-1, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
||||||
mapper->SetInputConnection(arc->GetOutputPort());
|
mapper->SetInputConnection(circle->GetOutputPort());
|
||||||
|
mapper->AddClippingPlane(this->m_ClipPlane);
|
||||||
|
|
||||||
auto actor = vtkSmartPointer<::vtkActor>::New();
|
auto actor = vtkSmartPointer<::vtkActor>::New();
|
||||||
actor->SetMapper(mapper);
|
actor->SetMapper(mapper);
|
||||||
actor->GetProperty()->SetColor(color);
|
actor->GetProperty()->SetColor(color);
|
||||||
actor->GetProperty()->SetLineWidth(3);
|
actor->GetProperty()->SetLineWidth(3);
|
||||||
|
actor->GetProperty()->SetLighting(0);
|
||||||
return actor;
|
return actor;
|
||||||
};
|
};
|
||||||
|
|
||||||
double red[] = {0.8, 0.1, 0.1}, green[] = {0.1, 0.8, 0.1},
|
double red[] = {1.0, 0.0, 0.0}, green[] = {0.0, 1.0, 0.0},
|
||||||
blue[] = {0.1, 0.1, 0.8};
|
blue[] = {0.0, 0.0, 1.0}, white[] = {1.0, 1.0, 1.0};
|
||||||
|
|
||||||
double x[] = {1, 0, 0}, y[] = {0, 1, 0}, z[] = {0, 0, 1};
|
double x[] = {1, 0, 0}, y[] = {0, 1, 0}, z[] = {0, 0, 1};
|
||||||
m_AxesX = create_arrow(x, red);
|
m_AxesX = create_arrow(x, red);
|
||||||
@@ -328,17 +490,34 @@ void vtkHandlerWidget::CreateGizmos() {
|
|||||||
m_RotY = create_ring(1, green);
|
m_RotY = create_ring(1, green);
|
||||||
m_RotZ = create_ring(2, blue);
|
m_RotZ = create_ring(2, blue);
|
||||||
|
|
||||||
|
m_RotCam = vtkSmartPointer<::vtkActor>::New();
|
||||||
|
{
|
||||||
|
auto circle = vtkSmartPointer<::vtkRegularPolygonSource>::New();
|
||||||
|
circle->SetNumberOfSides(64);
|
||||||
|
circle->SetRadius(1.3); // Slightly larger
|
||||||
|
circle->SetCenter(0, 0, 0);
|
||||||
|
circle->GeneratePolygonOff();
|
||||||
|
circle->GeneratePolylineOn();
|
||||||
|
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
||||||
|
mapper->SetInputConnection(circle->GetOutputPort());
|
||||||
|
m_RotCam->SetMapper(mapper);
|
||||||
|
m_RotCam->GetProperty()->SetColor(white);
|
||||||
|
m_RotCam->GetProperty()->SetLineWidth(2);
|
||||||
|
m_RotCam->GetProperty()->SetLighting(0);
|
||||||
|
}
|
||||||
|
|
||||||
auto create_cube = [](double pos[3], double color[3]) {
|
auto create_cube = [](double pos[3], double color[3]) {
|
||||||
auto cube = vtkSmartPointer<::vtkCubeSource>::New();
|
auto cube = vtkSmartPointer<::vtkCubeSource>::New();
|
||||||
cube->SetCenter(pos[0], pos[1], pos[2]);
|
cube->SetCenter(pos[0], pos[1], pos[2]);
|
||||||
cube->SetXLength(0.12);
|
cube->SetXLength(0.08);
|
||||||
cube->SetYLength(0.12);
|
cube->SetYLength(0.08);
|
||||||
cube->SetZLength(0.12);
|
cube->SetZLength(0.08);
|
||||||
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
auto mapper = vtkSmartPointer<::vtkPolyDataMapper>::New();
|
||||||
mapper->SetInputConnection(cube->GetOutputPort());
|
mapper->SetInputConnection(cube->GetOutputPort());
|
||||||
auto actor = vtkSmartPointer<::vtkActor>::New();
|
auto actor = vtkSmartPointer<::vtkActor>::New();
|
||||||
actor->SetMapper(mapper);
|
actor->SetMapper(mapper);
|
||||||
actor->GetProperty()->SetColor(color);
|
actor->GetProperty()->SetColor(color);
|
||||||
|
actor->GetProperty()->SetLighting(0);
|
||||||
return actor;
|
return actor;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -346,21 +525,173 @@ void vtkHandlerWidget::CreateGizmos() {
|
|||||||
m_ScaleX = create_cube(px, red);
|
m_ScaleX = create_cube(px, red);
|
||||||
m_ScaleY = create_cube(py, green);
|
m_ScaleY = create_cube(py, green);
|
||||||
m_ScaleZ = create_cube(pz, blue);
|
m_ScaleZ = create_cube(pz, blue);
|
||||||
|
|
||||||
|
// Configure picker to only see gizmo actors (Pick-Through)
|
||||||
|
m_Picker->InitializePickList();
|
||||||
|
m_Picker->AddPickList(m_AxesX);
|
||||||
|
m_Picker->AddPickList(m_AxesY);
|
||||||
|
m_Picker->AddPickList(m_AxesZ);
|
||||||
|
m_Picker->AddPickList(m_RotX);
|
||||||
|
m_Picker->AddPickList(m_RotY);
|
||||||
|
m_Picker->AddPickList(m_RotZ);
|
||||||
|
m_Picker->AddPickList(m_RotCam);
|
||||||
|
m_Picker->AddPickList(m_ScaleX);
|
||||||
|
m_Picker->AddPickList(m_ScaleY);
|
||||||
|
m_Picker->AddPickList(m_ScaleZ);
|
||||||
|
m_Picker->PickFromListOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
void vtkHandlerWidget::UpdateGizmoPosition() {
|
void vtkHandlerWidget::UpdateGizmoPosition() {
|
||||||
if (!this->Prop3D)
|
if (!this->Prop3D)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
vtkNew<vtkMatrix4x4> mat_gizmo;
|
||||||
|
mat_gizmo->Identity();
|
||||||
|
|
||||||
|
double center[3];
|
||||||
|
double bounds[6];
|
||||||
|
this->Prop3D->GetBounds(bounds);
|
||||||
|
center[0] = (bounds[0] + bounds[1]) / 2.0;
|
||||||
|
center[1] = (bounds[2] + bounds[3]) / 2.0;
|
||||||
|
center[2] = (bounds[4] + bounds[5]) / 2.0;
|
||||||
|
|
||||||
|
double pos[3];
|
||||||
|
this->Prop3D->GetPosition(pos);
|
||||||
|
|
||||||
|
if (m_Frame == LOCAL) {
|
||||||
::vtkMatrix4x4 *mat = this->Prop3D->GetMatrix();
|
::vtkMatrix4x4 *mat = this->Prop3D->GetMatrix();
|
||||||
m_AxesX->SetUserMatrix(mat);
|
mat_gizmo->DeepCopy(mat);
|
||||||
m_AxesY->SetUserMatrix(mat);
|
// Remove scaling
|
||||||
m_AxesZ->SetUserMatrix(mat);
|
for (int j = 0; j < 3; ++j) {
|
||||||
m_RotX->SetUserMatrix(mat);
|
double v[3] = {mat->GetElement(0, j), mat->GetElement(1, j),
|
||||||
m_RotY->SetUserMatrix(mat);
|
mat->GetElement(2, j)};
|
||||||
m_RotZ->SetUserMatrix(mat);
|
double len = vtkMath::Norm(v);
|
||||||
m_ScaleX->SetUserMatrix(mat);
|
if (len > 1e-6) {
|
||||||
m_ScaleY->SetUserMatrix(mat);
|
mat_gizmo->SetElement(0, j, v[0] / len);
|
||||||
m_ScaleZ->SetUserMatrix(mat);
|
mat_gizmo->SetElement(1, j, v[1] / len);
|
||||||
|
mat_gizmo->SetElement(2, j, v[2] / len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_Frame == CENTER_LOCAL) {
|
||||||
|
::vtkMatrix4x4 *mat = this->Prop3D->GetMatrix();
|
||||||
|
mat_gizmo->DeepCopy(mat);
|
||||||
|
// Remove scaling
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
double v[3] = {mat->GetElement(0, j), mat->GetElement(1, j),
|
||||||
|
mat->GetElement(2, j)};
|
||||||
|
double len = vtkMath::Norm(v);
|
||||||
|
if (len > 1e-6) {
|
||||||
|
mat_gizmo->SetElement(0, j, v[0] / len);
|
||||||
|
mat_gizmo->SetElement(1, j, v[1] / len);
|
||||||
|
mat_gizmo->SetElement(2, j, v[2] / len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set position to center
|
||||||
|
mat_gizmo->SetElement(0, 3, center[0]);
|
||||||
|
mat_gizmo->SetElement(1, 3, center[1]);
|
||||||
|
mat_gizmo->SetElement(2, 3, center[2]);
|
||||||
|
} else if (m_Frame == GLOBAL) {
|
||||||
|
mat_gizmo->Identity();
|
||||||
|
mat_gizmo->SetElement(0, 3, pos[0]);
|
||||||
|
mat_gizmo->SetElement(1, 3, pos[1]);
|
||||||
|
mat_gizmo->SetElement(2, 3, pos[2]);
|
||||||
|
} else if (m_Frame == CENTER) {
|
||||||
|
mat_gizmo->Identity();
|
||||||
|
mat_gizmo->SetElement(0, 3, center[0]);
|
||||||
|
mat_gizmo->SetElement(1, 3, center[1]);
|
||||||
|
mat_gizmo->SetElement(2, 3, center[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_AxesX->SetUserMatrix(mat_gizmo);
|
||||||
|
m_AxesY->SetUserMatrix(mat_gizmo);
|
||||||
|
m_AxesZ->SetUserMatrix(mat_gizmo);
|
||||||
|
m_RotX->SetUserMatrix(mat_gizmo);
|
||||||
|
m_RotY->SetUserMatrix(mat_gizmo);
|
||||||
|
m_RotZ->SetUserMatrix(mat_gizmo);
|
||||||
|
m_ScaleX->SetUserMatrix(mat_gizmo);
|
||||||
|
m_ScaleY->SetUserMatrix(mat_gizmo);
|
||||||
|
m_ScaleZ->SetUserMatrix(mat_gizmo);
|
||||||
|
|
||||||
|
// Sync Overlay Renderer with Main Renderer
|
||||||
|
if (this->CurrentRenderer && this->m_OverlayRenderer) {
|
||||||
|
this->m_OverlayRenderer->SetViewport(this->CurrentRenderer->GetViewport());
|
||||||
|
this->m_OverlayRenderer->SetAspect(this->CurrentRenderer->GetAspect());
|
||||||
|
this->m_OverlayRenderer->ComputeAspect();
|
||||||
|
|
||||||
|
if (this->m_OverlayRenderer->GetActiveCamera() != this->CurrentRenderer->GetActiveCamera()) {
|
||||||
|
this->m_OverlayRenderer->SetActiveCamera(this->CurrentRenderer->GetActiveCamera());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera ring always faces camera
|
||||||
|
if (this->CurrentRenderer) {
|
||||||
|
double camPos[3];
|
||||||
|
this->CurrentRenderer->GetActiveCamera()->GetPosition(camPos);
|
||||||
|
double dir[3] = {camPos[0] - mat_gizmo->GetElement(0, 3),
|
||||||
|
camPos[1] - mat_gizmo->GetElement(1, 3),
|
||||||
|
camPos[2] - mat_gizmo->GetElement(2, 3)};
|
||||||
|
vtkMath::Normalize(dir);
|
||||||
|
|
||||||
|
// Orient RotCam actor to face 'dir'
|
||||||
|
vtkNew<vtkTransform> tcam;
|
||||||
|
tcam->PostMultiply();
|
||||||
|
// Default circle is in XY plane (Normal Z: 0,0,1)
|
||||||
|
double z[3] = {0, 0, 1};
|
||||||
|
double cross[3];
|
||||||
|
vtkMath::Cross(z, dir, cross);
|
||||||
|
double cross_mag = vtkMath::Norm(cross);
|
||||||
|
if (cross_mag > 1e-6) {
|
||||||
|
double angle = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(z, dir)));
|
||||||
|
tcam->RotateWXYZ(angle, cross[0], cross[1], cross[2]);
|
||||||
|
} else if (vtkMath::Dot(z, dir) < 0) {
|
||||||
|
tcam->RotateX(180);
|
||||||
|
}
|
||||||
|
tcam->Translate(mat_gizmo->GetElement(0, 3), mat_gizmo->GetElement(1, 3),
|
||||||
|
mat_gizmo->GetElement(2, 3));
|
||||||
|
m_RotCam->SetUserMatrix(tcam->GetMatrix());
|
||||||
|
|
||||||
|
// Update clipping plane for axes rings
|
||||||
|
this->m_ClipPlane->SetOrigin(mat_gizmo->GetElement(0, 3),
|
||||||
|
mat_gizmo->GetElement(1, 3),
|
||||||
|
mat_gizmo->GetElement(2, 3));
|
||||||
|
this->m_ClipPlane->SetNormal(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vtkHandlerWidget::Highlight(::vtkProp *prop) {
|
||||||
|
if (this->m_HighlightedProp == prop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Restore previous
|
||||||
|
if (this->m_HighlightedProp) {
|
||||||
|
::vtkActor *actor = ::vtkActor::SafeDownCast(this->m_HighlightedProp);
|
||||||
|
if (actor) {
|
||||||
|
actor->GetProperty()->SetColor(m_OriginalColor);
|
||||||
|
actor->GetProperty()->SetLineWidth(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->m_HighlightedProp = nullptr;
|
||||||
|
|
||||||
|
// Highlight new if it belongs to us
|
||||||
|
if (prop == m_AxesX || prop == m_AxesY || prop == m_AxesZ || prop == m_RotX ||
|
||||||
|
prop == m_RotY || prop == m_RotZ || prop == m_RotCam || prop == m_ScaleX ||
|
||||||
|
prop == m_ScaleY || prop == m_ScaleZ) {
|
||||||
|
this->m_HighlightedProp = prop;
|
||||||
|
::vtkActor *actor = ::vtkActor::SafeDownCast(prop);
|
||||||
|
if (actor) {
|
||||||
|
actor->GetProperty()->GetColor(m_OriginalColor);
|
||||||
|
double h[3] = {m_OriginalColor[0] + 0.2, m_OriginalColor[1] + 0.2,
|
||||||
|
m_OriginalColor[2] + 0.2};
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
h[i] = std::min(1.0, h[i]);
|
||||||
|
actor->GetProperty()->SetColor(h);
|
||||||
|
actor->GetProperty()->SetLineWidth(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->Interactor)
|
||||||
|
this->Interactor->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
|
|||||||
@@ -35,9 +35,10 @@
|
|||||||
// Forward declarations of VTK classes in global namespace
|
// Forward declarations of VTK classes in global namespace
|
||||||
class vtkActor;
|
class vtkActor;
|
||||||
class vtkCallbackCommand;
|
class vtkCallbackCommand;
|
||||||
class vtkPropPicker;
|
class vtkCellPicker;
|
||||||
class vtkTransform;
|
class vtkTransform;
|
||||||
class vtkObject;
|
class vtkObject;
|
||||||
|
class vtkPlane;
|
||||||
class vtkRenderWindowInteractor;
|
class vtkRenderWindowInteractor;
|
||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
@@ -73,9 +74,22 @@ public:
|
|||||||
ROT_Z,
|
ROT_Z,
|
||||||
SCALE_X,
|
SCALE_X,
|
||||||
SCALE_Y,
|
SCALE_Y,
|
||||||
SCALE_Z
|
SCALE_Z,
|
||||||
|
ROT_CAM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ReferenceFrame {
|
||||||
|
GLOBAL = 0,
|
||||||
|
LOCAL,
|
||||||
|
CENTER,
|
||||||
|
CENTER_LOCAL,
|
||||||
|
NORMAL, // Not implemented
|
||||||
|
PARENT // Not implemented
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetReferenceFrame(ReferenceFrame frame);
|
||||||
|
ReferenceFrame GetReferenceFrame() const { return this->m_Frame; }
|
||||||
|
|
||||||
using ::vtk3DWidget::PlaceWidget;
|
using ::vtk3DWidget::PlaceWidget;
|
||||||
virtual void PlaceWidget(double bounds[6]) override;
|
virtual void PlaceWidget(double bounds[6]) override;
|
||||||
virtual void PlaceWidget() override;
|
virtual void PlaceWidget() override;
|
||||||
@@ -87,17 +101,27 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void CreateGizmos();
|
void CreateGizmos();
|
||||||
void UpdateGizmoPosition();
|
void UpdateGizmoPosition();
|
||||||
|
void Highlight(::vtkProp *prop);
|
||||||
|
|
||||||
|
vtkSmartPointer<::vtkRenderer> m_OverlayRenderer;
|
||||||
|
ReferenceFrame m_Frame;
|
||||||
|
|
||||||
int Interaction;
|
int Interaction;
|
||||||
|
::vtkProp *m_HighlightedProp;
|
||||||
|
double m_OriginalColor[3];
|
||||||
|
|
||||||
// Visual components //
|
// Visual components //
|
||||||
vtkSmartPointer<::vtkActor> m_AxesX, m_AxesY, m_AxesZ; // Arrows
|
vtkSmartPointer<::vtkActor> m_AxesX, m_AxesY, m_AxesZ; // Arrows
|
||||||
vtkSmartPointer<::vtkActor> m_RotX, m_RotY, m_RotZ; // Rings
|
vtkSmartPointer<::vtkActor> m_RotX, m_RotY, m_RotZ; // Rings
|
||||||
|
vtkSmartPointer<::vtkActor> m_RotCam; // Camera ring
|
||||||
vtkSmartPointer<::vtkActor> m_ScaleX, m_ScaleY, m_ScaleZ; // Cubes
|
vtkSmartPointer<::vtkActor> m_ScaleX, m_ScaleY, m_ScaleZ; // Cubes
|
||||||
|
|
||||||
vtkSmartPointer<::vtkPropPicker> m_Picker;
|
vtkSmartPointer<::vtkPlane> m_ClipPlane;
|
||||||
|
|
||||||
|
vtkSmartPointer<::vtkCellPicker> m_Picker;
|
||||||
|
|
||||||
double StartEventPosition[2];
|
double StartEventPosition[2];
|
||||||
|
double m_StartPickPosition[3];
|
||||||
vtkSmartPointer<::vtkTransform> m_InitialTransform;
|
vtkSmartPointer<::vtkTransform> m_InitialTransform;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user