343 lines
11 KiB
C++
343 lines
11 KiB
C++
/*//////////////////////////////////////////////////////////////////////////////
|
|
// 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.
|
|
|
|
//////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <cstdlib>
|
|
#include <vtkRenderWindowInteractor.h>
|
|
#include <vtkRendererCollection.h>
|
|
#include <vtkSmartPointer.h>
|
|
|
|
#include <vtkAxesActor.h>
|
|
#include <vtkCamera.h>
|
|
#include <vtkCameraOrientationWidget.h>
|
|
#include <vtkInteractorStyleTrackballCamera.h>
|
|
#include <vtkObjectFactory.h>
|
|
#include <vtkTextProperty.h>
|
|
#include <vtkRenderWindow.h>
|
|
#include <vtkButtonWidget.h>
|
|
#include <vtkTexturedButtonRepresentation2D.h>
|
|
#include <vtkImageData.h>
|
|
#include <vtkImageCanvasSource2D.h>
|
|
#include <vtkCallbackCommand.h>
|
|
#include <vtkCoordinate.h>
|
|
#include <vtkRenderer.h>
|
|
|
|
#include "uLibVtkViewer.h"
|
|
|
|
// Custom interactor style: disables spin/inertia so the scene only
|
|
// rotates while the mouse is actively being moved with the button held.
|
|
class vtkInteractorStyleNoSpin : public vtkInteractorStyleTrackballCamera {
|
|
public:
|
|
static vtkInteractorStyleNoSpin *New();
|
|
vtkTypeMacro(vtkInteractorStyleNoSpin, vtkInteractorStyleTrackballCamera);
|
|
|
|
// Override: when the left button is released, immediately stop any
|
|
// ongoing motion (rotation/spin) so no momentum is carried over.
|
|
void OnLeftButtonUp() override {
|
|
this->StopState();
|
|
vtkInteractorStyleTrackballCamera::OnLeftButtonUp();
|
|
}
|
|
};
|
|
vtkStandardNewMacro(vtkInteractorStyleNoSpin);
|
|
|
|
namespace uLib {
|
|
namespace Vtk {
|
|
|
|
struct ViewerData {
|
|
vtkRenderWindow *m_RenderWindow;
|
|
vtkSmartPointer<vtkRenderWindowInteractor> m_Interactor;
|
|
vtkSmartPointer<vtkButtonWidget> m_GridButton;
|
|
vtkSmartPointer<vtkButtonWidget> m_ProjButton;
|
|
|
|
ViewerData() : m_RenderWindow(vtkRenderWindow::New()) {}
|
|
~ViewerData() {
|
|
if (m_Interactor) {
|
|
m_Interactor->SetRenderWindow(nullptr);
|
|
}
|
|
m_RenderWindow->Delete();
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///// VTK VIEWER //////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
Viewer::Viewer()
|
|
: Viewport(), dv(new ViewerData()) {
|
|
InstallPipe();
|
|
}
|
|
|
|
Viewer::~Viewer() {
|
|
this->DisableHandler();
|
|
if (dv->m_GridButton) {
|
|
dv->m_GridButton->Off();
|
|
dv->m_GridButton->SetInteractor(nullptr);
|
|
dv->m_GridButton = nullptr;
|
|
}
|
|
if (dv->m_ProjButton) {
|
|
dv->m_ProjButton->Off();
|
|
dv->m_ProjButton->SetInteractor(nullptr);
|
|
dv->m_ProjButton = nullptr;
|
|
}
|
|
if (this->GetRenderWindow()) {
|
|
this->GetRenderWindow()->RemoveAllObservers();
|
|
}
|
|
if (this->GetInteractor()) {
|
|
this->GetInteractor()->RemoveAllObservers();
|
|
}
|
|
UninstallPipe();
|
|
delete dv;
|
|
}
|
|
|
|
void Viewer::InstallPipe() {
|
|
dv->m_RenderWindow->AddRenderer(this->GetRenderer());
|
|
dv->m_RenderWindow->SetSize(600,600);
|
|
if (std::getenv("CTEST_PROJECT_NAME")) {
|
|
dv->m_RenderWindow->SetOffScreenRendering(1);
|
|
}
|
|
|
|
dv->m_Interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
|
|
dv->m_Interactor->SetRenderWindow(dv->m_RenderWindow);
|
|
|
|
// Common setup
|
|
Viewport::SetupPipeline(dv->m_Interactor);
|
|
|
|
// Setup native grid button
|
|
if (!std::getenv("CTEST_PROJECT_NAME")) {
|
|
SetupGridButton();
|
|
SetupProjButton();
|
|
}
|
|
|
|
// BUT we want to override the style with our custom NoSpin version
|
|
vtkSmartPointer<vtkInteractorStyleNoSpin> style =
|
|
vtkSmartPointer<vtkInteractorStyleNoSpin>::New();
|
|
dv->m_Interactor->SetInteractorStyle(style);
|
|
|
|
// Must be rendered here in Vtk-6.0 or seg-fault //
|
|
if (!std::getenv("CTEST_PROJECT_NAME")) {
|
|
dv->m_RenderWindow->Render();
|
|
}
|
|
}
|
|
|
|
void Viewer::UninstallPipe() {
|
|
}
|
|
|
|
void Viewer::Render() {
|
|
if (dv->m_RenderWindow)
|
|
dv->m_RenderWindow->Render();
|
|
}
|
|
|
|
vtkCameraOrientationWidget *
|
|
Viewer::MakeCameraOrientationWidget(vtkRenderWindowInteractor *interactor,
|
|
vtkRenderer *renderer) {
|
|
vtkSmartPointer<vtkCameraOrientationWidget> widget =
|
|
vtkSmartPointer<vtkCameraOrientationWidget>::New();
|
|
widget->SetParentRenderer(renderer);
|
|
widget->SetInteractor(interactor);
|
|
widget->On();
|
|
return widget;
|
|
}
|
|
|
|
void Viewer::SetupGridButton() {
|
|
if (!dv->m_RenderWindow || !dv->m_RenderWindow->GetInteractor()) return;
|
|
|
|
// Create procedural textures for the button using canvas
|
|
vtkNew<vtkImageCanvasSource2D> canvas;
|
|
canvas->SetScalarTypeToUnsignedChar();
|
|
canvas->SetNumberOfScalarComponents(4);
|
|
canvas->SetExtent(0, 63, 0, 63, 0, 0);
|
|
|
|
// State 0: OFF (Gray circle, transparent background)
|
|
canvas->SetDrawColor(0, 0, 0, 0);
|
|
canvas->FillBox(0, 63, 0, 63);
|
|
canvas->SetDrawColor(120, 120, 120, 255);
|
|
canvas->DrawCircle(32, 32, 25);
|
|
canvas->Update();
|
|
|
|
vtkNew<vtkImageData> imgOff;
|
|
imgOff->DeepCopy(canvas->GetOutput());
|
|
|
|
// State 1: ON (White circle, transparent background)
|
|
canvas->SetDrawColor(0, 0, 0, 0);
|
|
canvas->FillBox(0, 63, 0, 63);
|
|
canvas->SetDrawColor(255, 255, 255, 255);
|
|
canvas->DrawCircle(32, 32, 25);
|
|
canvas->Update();
|
|
|
|
vtkNew<vtkImageData> imgOn;
|
|
imgOn->DeepCopy(canvas->GetOutput());
|
|
|
|
vtkNew<vtkTexturedButtonRepresentation2D> rep;
|
|
rep->SetNumberOfStates(2);
|
|
rep->SetButtonTexture(0, imgOff);
|
|
rep->SetButtonTexture(1, imgOn);
|
|
|
|
dv->m_GridButton = vtkSmartPointer<vtkButtonWidget>::New();
|
|
dv->m_GridButton->SetInteractor(dv->m_RenderWindow->GetInteractor());
|
|
dv->m_GridButton->SetRepresentation(rep);
|
|
|
|
// Position it initially
|
|
UpdateGridButtonPosition();
|
|
|
|
// Callback for resize (ModifiedEvent on RenderWindow)
|
|
vtkNew<vtkCallbackCommand> resizeCallback;
|
|
resizeCallback->SetClientData(this);
|
|
resizeCallback->SetCallback([](vtkObject*, unsigned long, void* clientdata, void*){
|
|
auto* v = static_cast<Viewer*>(clientdata);
|
|
v->UpdateGridButtonPosition();
|
|
});
|
|
dv->m_RenderWindow->AddObserver(vtkCommand::ModifiedEvent, resizeCallback);
|
|
|
|
// Callback for state change
|
|
vtkNew<vtkCallbackCommand> stateCallback;
|
|
stateCallback->SetClientData(this);
|
|
stateCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){
|
|
auto* btn = vtkButtonWidget::SafeDownCast(caller);
|
|
auto* v = static_cast<Viewer*>(clientdata);
|
|
auto* r = vtkTexturedButtonRepresentation2D::SafeDownCast(btn->GetRepresentation());
|
|
v->SetGridVisible(r->GetState() == 1);
|
|
});
|
|
|
|
dv->m_GridButton->AddObserver(vtkCommand::StateChangedEvent, stateCallback);
|
|
dv->m_GridButton->On();
|
|
|
|
// Set initial state
|
|
rep->SetState(GetGridVisible() ? 1 : 0);
|
|
}
|
|
|
|
void Viewer::UpdateGridButtonPosition() {
|
|
if (!dv->m_GridButton || !dv->m_RenderWindow) return;
|
|
auto* rep = vtkTexturedButtonRepresentation2D::SafeDownCast(dv->m_GridButton->GetRepresentation());
|
|
if (!rep) return;
|
|
|
|
int *sz = dv->m_RenderWindow->GetSize();
|
|
if (sz[0] == 0 || sz[1] == 0) return; // Window not yet sized or hidden
|
|
|
|
int margin_rigth = 23;
|
|
int margin_top = 170;
|
|
int btnSz = 100; // Button size in display coordinates
|
|
double bds[6] = { (double)sz[0] - btnSz - margin_rigth, (double)sz[0] - margin_rigth,
|
|
(double)sz[1] - margin_top - btnSz/2.0, (double)sz[1] - margin_top + btnSz/2.0, 0, 0 };
|
|
rep->PlaceWidget(bds);
|
|
}
|
|
|
|
void Viewer::SetupProjButton() {
|
|
if (!dv->m_RenderWindow || !dv->m_RenderWindow->GetInteractor()) return;
|
|
|
|
vtkNew<vtkImageCanvasSource2D> canvas;
|
|
canvas->SetScalarTypeToUnsignedChar();
|
|
canvas->SetNumberOfScalarComponents(4);
|
|
canvas->SetExtent(0, 63, 0, 63, 0, 0);
|
|
|
|
// State 0: Perspective (gray trapezoid-like lines)
|
|
canvas->SetDrawColor(0, 0, 0, 0);
|
|
canvas->FillBox(0, 63, 0, 63);
|
|
canvas->SetDrawColor(120, 120, 120, 255);
|
|
canvas->DrawSegment(16, 16, 48, 16);
|
|
canvas->DrawSegment(48, 16, 56, 48);
|
|
canvas->DrawSegment(56, 48, 8, 48);
|
|
canvas->DrawSegment(8, 48, 16, 16);
|
|
canvas->Update();
|
|
|
|
vtkNew<vtkImageData> imgPersp;
|
|
imgPersp->DeepCopy(canvas->GetOutput());
|
|
|
|
// State 1: Orthographic (white rectangle)
|
|
canvas->SetDrawColor(0, 0, 0, 0);
|
|
canvas->FillBox(0, 63, 0, 63);
|
|
canvas->SetDrawColor(255, 255, 255, 255);
|
|
canvas->DrawSegment(12, 16, 52, 16);
|
|
canvas->DrawSegment(52, 16, 52, 48);
|
|
canvas->DrawSegment(52, 48, 12, 48);
|
|
canvas->DrawSegment(12, 48, 12, 16);
|
|
canvas->Update();
|
|
|
|
vtkNew<vtkImageData> imgOrtho;
|
|
imgOrtho->DeepCopy(canvas->GetOutput());
|
|
|
|
vtkNew<vtkTexturedButtonRepresentation2D> rep;
|
|
rep->SetNumberOfStates(2);
|
|
rep->SetButtonTexture(0, imgPersp);
|
|
rep->SetButtonTexture(1, imgOrtho);
|
|
|
|
dv->m_ProjButton = vtkSmartPointer<vtkButtonWidget>::New();
|
|
dv->m_ProjButton->SetInteractor(dv->m_RenderWindow->GetInteractor());
|
|
dv->m_ProjButton->SetRepresentation(rep);
|
|
|
|
UpdateProjButtonPosition();
|
|
|
|
vtkNew<vtkCallbackCommand> resizeCallback;
|
|
resizeCallback->SetClientData(this);
|
|
resizeCallback->SetCallback([](vtkObject*, unsigned long, void* clientdata, void*){
|
|
static_cast<Viewer*>(clientdata)->UpdateProjButtonPosition();
|
|
});
|
|
dv->m_RenderWindow->AddObserver(vtkCommand::ModifiedEvent, resizeCallback);
|
|
|
|
vtkNew<vtkCallbackCommand> stateCallback;
|
|
stateCallback->SetClientData(this);
|
|
stateCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){
|
|
auto* btn = vtkButtonWidget::SafeDownCast(caller);
|
|
auto* v = static_cast<Viewer*>(clientdata);
|
|
auto* r = vtkTexturedButtonRepresentation2D::SafeDownCast(btn->GetRepresentation());
|
|
v->SetParallelProjection(r->GetState() == 1);
|
|
});
|
|
dv->m_ProjButton->AddObserver(vtkCommand::StateChangedEvent, stateCallback);
|
|
dv->m_ProjButton->On();
|
|
|
|
rep->SetState(GetParallelProjection() ? 1 : 0);
|
|
}
|
|
|
|
void Viewer::UpdateProjButtonPosition() {
|
|
if (!dv->m_ProjButton || !dv->m_RenderWindow) return;
|
|
auto* rep = vtkTexturedButtonRepresentation2D::SafeDownCast(dv->m_ProjButton->GetRepresentation());
|
|
if (!rep) return;
|
|
|
|
int *sz = dv->m_RenderWindow->GetSize();
|
|
if (sz[0] == 0 || sz[1] == 0) return;
|
|
|
|
int margin_right = 23;
|
|
int margin_top = 220; // below the grid button (170 + 50)
|
|
int btnSz = 100;
|
|
double bds[6] = { (double)sz[0] - btnSz - margin_right, (double)sz[0] - margin_right,
|
|
(double)sz[1] - margin_top - btnSz/2.0, (double)sz[1] - margin_top + btnSz/2.0, 0, 0 };
|
|
rep->PlaceWidget(bds);
|
|
}
|
|
|
|
void Viewer::Start() {
|
|
if (std::getenv("CTEST_PROJECT_NAME")) return;
|
|
dv->m_RenderWindow->GetInteractor()->Start();
|
|
}
|
|
|
|
vtkRenderWindow *Viewer::GetRenderWindow() { return dv->m_RenderWindow; }
|
|
|
|
vtkRenderWindowInteractor *Viewer::GetInteractor() {
|
|
return dv->m_RenderWindow->GetInteractor();
|
|
}
|
|
|
|
} // namespace Vtk
|
|
} // namespace uLib
|