Compare commits
5 Commits
serializat
...
andrea-geo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6396bdfebf | ||
|
|
96ab3b0930 | ||
|
|
5c04d00d4c | ||
|
|
72e69cfca5 | ||
|
|
59a9e829fc |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@ src/Python/uLib/.nfs*
|
|||||||
test_props.xml
|
test_props.xml
|
||||||
test_props2.xml
|
test_props2.xml
|
||||||
test_boost.cpp
|
test_boost.cpp
|
||||||
|
.claude/settings.json
|
||||||
|
|||||||
@@ -111,17 +111,17 @@ void vtkContainerBox::SyncFromVtk() {
|
|||||||
|
|
||||||
// VTK -> Model: Extract new world TRS from proxy, which matches the model's TRS center
|
// VTK -> Model: Extract new world TRS from proxy, which matches the model's TRS center
|
||||||
vtkMatrix4x4* rootMat = root->GetUserMatrix();
|
vtkMatrix4x4* rootMat = root->GetUserMatrix();
|
||||||
if (rootMat) {
|
// if (rootMat) {
|
||||||
std::cout << "[vtkContainerBox::SyncFromVtk] Read Proxy UserMatrix:" << std::endl;
|
// std::cout << "[vtkContainerBox::SyncFromVtk] Read Proxy UserMatrix:" << std::endl;
|
||||||
rootMat->Print(std::cout);
|
// rootMat->Print(std::cout);
|
||||||
}
|
// }
|
||||||
|
|
||||||
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
|
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
|
||||||
|
|
||||||
// Synchronize TRS property members from the updated local matrix
|
// Synchronize TRS property members from the updated local matrix
|
||||||
m_Content->FromMatrix(vtkWorld);
|
m_Content->FromMatrix(vtkWorld);
|
||||||
|
|
||||||
std::cout << "[vtkContainerBox::SyncFromVtk] New Model WorldMatrix:" << std::endl << m_Content->GetWorldMatrix() << std::endl;
|
// std::cout << "[vtkContainerBox::SyncFromVtk] New Model WorldMatrix:" << std::endl << m_Content->GetWorldMatrix() << std::endl;
|
||||||
|
|
||||||
// Since we modified the model, notify observers, but block the loop back to VTK
|
// Since we modified the model, notify observers, but block the loop back to VTK
|
||||||
// ConnectionBlock blocker(d->m_UpdateSignal);
|
// ConnectionBlock blocker(d->m_UpdateSignal);
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include <vtkPiecewiseFunction.h>
|
#include <vtkPiecewiseFunction.h>
|
||||||
#include <vtkSmartVolumeMapper.h>
|
#include <vtkSmartVolumeMapper.h>
|
||||||
#include <vtkVolumeProperty.h>
|
#include <vtkVolumeProperty.h>
|
||||||
|
#include <vtkMatrix4x4.h>
|
||||||
|
|
||||||
#include <vtkActor.h>
|
#include <vtkActor.h>
|
||||||
#include <vtkPolyDataMapper.h>
|
#include <vtkPolyDataMapper.h>
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
#include <Math/VoxImage.h>
|
#include <Math/VoxImage.h>
|
||||||
|
|
||||||
#include "Vtk/Math/vtkVoxImage.h"
|
#include "Vtk/Math/vtkVoxImage.h"
|
||||||
|
#include "Vtk/Math/vtkDense.h"
|
||||||
|
|
||||||
#include <vtkAutoInit.h>
|
#include <vtkAutoInit.h>
|
||||||
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
|
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
|
||||||
@@ -128,9 +130,15 @@ vtkVoxImage::vtkVoxImage(Content &content)
|
|||||||
m_Image(vtkImageData::New()), m_Outline(vtkCubeSource::New()),
|
m_Image(vtkImageData::New()), m_Outline(vtkCubeSource::New()),
|
||||||
m_OutlineActor(vtkActor::New()),
|
m_OutlineActor(vtkActor::New()),
|
||||||
m_Reader(NULL), m_Writer(NULL), writer_factor(1.E6),
|
m_Reader(NULL), m_Writer(NULL), writer_factor(1.E6),
|
||||||
m_Window(40/1.E6), m_Level(20/1.E6), m_ShadingPreset(0) {
|
m_Window(1.0), m_Level(0.5), m_ShadingPreset(0) {
|
||||||
|
// Transfer functions
|
||||||
|
m_ColorFun = vtkColorTransferFunction::New();
|
||||||
|
m_OpacityFun = vtkPiecewiseFunction::New();
|
||||||
|
m_UpdateConnection = Object::connect(&m_Content, &uLib::Object::Updated, this, &vtkVoxImage::Update);
|
||||||
|
|
||||||
GetContent();
|
GetContent();
|
||||||
InstallPipe();
|
InstallPipe();
|
||||||
|
RescaleShaderRange();
|
||||||
ULIB_ACTIVATE_DISPLAY_PROPERTIES;
|
ULIB_ACTIVATE_DISPLAY_PROPERTIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +148,8 @@ vtkVoxImage::~vtkVoxImage() {
|
|||||||
m_Asm->Delete();
|
m_Asm->Delete();
|
||||||
m_Outline->Delete();
|
m_Outline->Delete();
|
||||||
m_OutlineActor->Delete();
|
m_OutlineActor->Delete();
|
||||||
|
m_ColorFun->Delete();
|
||||||
|
m_OpacityFun->Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
vtkImageData *vtkVoxImage::GetImageData() {
|
vtkImageData *vtkVoxImage::GetImageData() {
|
||||||
@@ -181,6 +191,7 @@ void vtkVoxImage::ReadFromVKTFile(const char *fname) {
|
|||||||
|
|
||||||
m_Image->DeepCopy(vtkscale->GetOutput());
|
m_Image->DeepCopy(vtkscale->GetOutput());
|
||||||
SetContent();
|
SetContent();
|
||||||
|
RescaleShaderRange();
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Error: file does not contain structured points\n";
|
std::cerr << "Error: file does not contain structured points\n";
|
||||||
}
|
}
|
||||||
@@ -200,115 +211,134 @@ void vtkVoxImage::ReadFromXMLFile(const char *fname) {
|
|||||||
|
|
||||||
m_Image->DeepCopy(vtkscale->GetOutput());
|
m_Image->DeepCopy(vtkscale->GetOutput());
|
||||||
SetContent();
|
SetContent();
|
||||||
|
RescaleShaderRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
void vtkVoxImage::setShadingPreset(int blendType) {
|
void vtkVoxImage::setShadingPreset(int blendType) {
|
||||||
m_ShadingPreset = blendType;
|
m_ShadingPreset = blendType;
|
||||||
vtkSmartVolumeMapper *mapper = (vtkSmartVolumeMapper *)m_Actor->GetMapper();
|
vtkSmartVolumeMapper *mapper = (vtkSmartVolumeMapper *)m_Actor->GetMapper();
|
||||||
|
if (!mapper) return;
|
||||||
vtkVolumeProperty *property = m_Actor->GetProperty();
|
vtkVolumeProperty *property = m_Actor->GetProperty();
|
||||||
|
|
||||||
static vtkColorTransferFunction *colorFun = vtkColorTransferFunction::New();
|
|
||||||
static vtkPiecewiseFunction *opacityFun = vtkPiecewiseFunction::New();
|
|
||||||
|
|
||||||
float window = m_Window;
|
float window = m_Window;
|
||||||
float level = m_Level;
|
float level = m_Level;
|
||||||
|
|
||||||
property->SetColor(colorFun);
|
property->SetColor(m_ColorFun);
|
||||||
property->SetScalarOpacity(opacityFun);
|
property->SetScalarOpacity(m_OpacityFun);
|
||||||
property->SetInterpolationTypeToLinear();
|
property->SetInterpolationTypeToLinear();
|
||||||
|
|
||||||
if (blendType != 6) {
|
m_ColorFun->RemoveAllPoints();
|
||||||
colorFun->RemoveAllPoints();
|
m_OpacityFun->RemoveAllPoints();
|
||||||
opacityFun->RemoveAllPoints();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (blendType) {
|
switch (blendType) {
|
||||||
case 0:
|
case 0: // MIP
|
||||||
colorFun->AddRGBSegment(0.0, 1.0, 1.0, 1.0, 255.0, 1.0, 1.0, 1.0);
|
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window,
|
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||||
1.0);
|
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||||
mapper->SetBlendModeToMaximumIntensity();
|
mapper->SetBlendModeToMaximumIntensity();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1: // Composite
|
||||||
colorFun->AddRGBSegment(level - 0.5 * window, 0.0, 0.0, 0.0,
|
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||||
level + 0.5 * window, 1.0, 1.0, 1.0);
|
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window,
|
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||||
1.0);
|
|
||||||
mapper->SetBlendModeToComposite();
|
mapper->SetBlendModeToComposite();
|
||||||
property->ShadeOff();
|
property->ShadeOff();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2: // Composite Shaded
|
||||||
colorFun->AddRGBSegment(0.0, 1.0, 1.0, 1.0, 255.0, 1.0, 1.0, 1.0);
|
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window,
|
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||||
1.0);
|
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||||
mapper->SetBlendModeToComposite();
|
mapper->SetBlendModeToComposite();
|
||||||
property->ShadeOn();
|
property->ShadeOn();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3: // Rainbow MIP
|
||||||
colorFun->AddRGBPoint(-3024, 0, 0, 0, 0.5, 0.0);
|
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 1);
|
||||||
colorFun->AddRGBPoint(-1000, .62, .36, .18, 0.5, 0.0);
|
m_ColorFun->AddRGBPoint(level, 0, 1, 0);
|
||||||
colorFun->AddRGBPoint(-500, .88, .60, .29, 0.33, 0.45);
|
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 0);
|
||||||
colorFun->AddRGBPoint(3071, .83, .66, 1, 0.5, 0.0);
|
m_ColorFun->AddRGBPoint(level + window, 1, 0, 0);
|
||||||
opacityFun->AddPoint(-3024, 0, 0.5, 0.0);
|
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||||
opacityFun->AddPoint(-1000, 0, 0.5, 0.0);
|
|
||||||
opacityFun->AddPoint(-500, 1.0, 0.33, 0.45);
|
|
||||||
opacityFun->AddPoint(3071, 1.0, 0.5, 0.0);
|
|
||||||
mapper->SetBlendModeToComposite();
|
|
||||||
property->ShadeOn();
|
|
||||||
property->SetAmbient(0.1);
|
|
||||||
property->SetDiffuse(0.9);
|
|
||||||
property->SetSpecular(0.2);
|
|
||||||
property->SetSpecularPower(10.0);
|
|
||||||
property->SetScalarOpacityUnitDistance(0.8919);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
colorFun->AddRGBPoint(0.0, 0, 0, 1); // Blue
|
|
||||||
colorFun->AddRGBPoint(level, 0, 1, 0); // Green
|
|
||||||
colorFun->AddRGBPoint(level + 0.5*window, 1, 1, 0); // Yellow
|
|
||||||
colorFun->AddRGBPoint(level + window, 1, 0, 0); // Red
|
|
||||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
|
||||||
mapper->SetBlendModeToMaximumIntensity();
|
mapper->SetBlendModeToMaximumIntensity();
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 4: // Additive
|
||||||
colorFun->AddRGBSegment(0.0, 1.0, 1.0, 1.0, 255.0, 1.0, 1.0, 1.0);
|
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||||
|
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||||
mapper->SetBlendModeToAdditive();
|
mapper->SetBlendModeToAdditive();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
vtkGenericWarningMacro("Unknown blend type.");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vtkVoxImage::RescaleShaderRange() {
|
||||||
|
double range[2];
|
||||||
|
m_Image->GetScalarRange(range);
|
||||||
|
m_Level = (range[0] + range[1]) / 2.0;
|
||||||
|
m_Window = range[1] - range[0];
|
||||||
|
if (m_Window <= 1e-9)
|
||||||
|
m_Window = 1.0;
|
||||||
|
setShadingPreset(m_ShadingPreset);
|
||||||
|
}
|
||||||
|
|
||||||
void vtkVoxImage::SetRepresentation(Representation mode) {
|
void vtkVoxImage::SetRepresentation(Representation mode) {
|
||||||
|
Puppet::SetRepresentation(mode); // Ensure base class data state is updated
|
||||||
|
|
||||||
if (mode == Wireframe) {
|
if (mode == Wireframe) {
|
||||||
m_Actor->SetVisibility(0);
|
m_Actor->SetVisibility(0);
|
||||||
m_OutlineActor->SetVisibility(1);
|
m_OutlineActor->SetVisibility(1);
|
||||||
} else if (mode == Surface) {
|
m_OutlineActor->GetProperty()->SetRepresentationToWireframe();
|
||||||
|
} else if (mode == Volume) {
|
||||||
m_Actor->SetVisibility(1);
|
m_Actor->SetVisibility(1);
|
||||||
m_OutlineActor->SetVisibility(1); // Keep outline visible as boundary
|
m_OutlineActor->SetVisibility(1);
|
||||||
|
m_OutlineActor->GetProperty()->SetRepresentationToWireframe();
|
||||||
} else {
|
} else {
|
||||||
Puppet::SetRepresentation(mode);
|
// Other representations (Points, Surface, etc) are handled by basic Puppet
|
||||||
|
// behavior which affects the m_Asm parts.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vtkVoxImage::serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version) {
|
void vtkVoxImage::serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version) {
|
||||||
// Call base class if it has display properties
|
// Call base class to show Transform and Appearance properties
|
||||||
Puppet::serialize_display(ar, version);
|
// Puppet::serialize_display(ar, version);
|
||||||
|
|
||||||
// Use the member variables if they are available
|
// Use the member variables for volume rendering parameters
|
||||||
ar & boost::serialization::make_hrp("Window", m_Window);
|
ar & boost::serialization::make_hrp("Window", m_Window);
|
||||||
ar & boost::serialization::make_hrp("Level", m_Level);
|
ar & boost::serialization::make_hrp("Level", m_Level);
|
||||||
ar & boost::serialization::make_hrp_enum("Shading", m_ShadingPreset, {"MIP", "Composite", "Composite Shaded", "MIP Bone", "MIP Hot", "Additive"});
|
ar & boost::serialization::make_hrp_enum("Shading", m_ShadingPreset,
|
||||||
|
{"MIP", "Composite", "Composite Shaded", "MIP Bone", "MIP Hot", "Additive"});
|
||||||
|
}
|
||||||
|
|
||||||
|
void vtkVoxImage::SyncFromVtk() {
|
||||||
|
if (auto *root = this->GetProxyProp()) {
|
||||||
|
vtkMatrix4x4 *rootMat = root->GetUserMatrix();
|
||||||
|
if (rootMat) {
|
||||||
|
Matrix4f vtkLocal = VtkToMatrix4f(rootMat);
|
||||||
|
// Synchronize TRS from VTK, compensating for local volume offset
|
||||||
|
m_Content.FromMatrix(vtkLocal); // * m_Content.GetLocalMatrix().inverse());
|
||||||
|
m_Content.Updated();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vtkVoxImage::Update() {
|
void vtkVoxImage::Update() {
|
||||||
|
if (auto *root = vtkProp3D::SafeDownCast(this->GetProp())) {
|
||||||
|
vtkNew<vtkMatrix4x4> m;
|
||||||
|
Matrix4fToVtk(m_Content.GetMatrix(), m); // * m_Content.GetLocalMatrix(), m);
|
||||||
|
root->SetUserMatrix(m);
|
||||||
|
root->Modified();
|
||||||
|
// std::cout << "[vtkVoxImage::Update] Set Proxy UserMatrix:" << std::endl;
|
||||||
|
// std::cout << m_Content.GetMatrix() << std::endl;
|
||||||
|
}
|
||||||
setShadingPreset(m_ShadingPreset);
|
setShadingPreset(m_ShadingPreset);
|
||||||
m_Actor->Update();
|
m_Actor->Update();
|
||||||
m_Outline->SetBounds(m_Image->GetBounds());
|
m_Outline->SetBounds(m_Image->GetBounds());
|
||||||
m_Outline->Update();
|
m_Outline->Update();
|
||||||
|
|
||||||
|
ConnectionBlock blocker(m_UpdateConnection);
|
||||||
|
this->Puppet::Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void vtkVoxImage::InstallPipe() {
|
void vtkVoxImage::InstallPipe() {
|
||||||
vtkSmartPointer<vtkSmartVolumeMapper> mapper =
|
vtkSmartPointer<vtkSmartVolumeMapper> mapper =
|
||||||
vtkSmartPointer<vtkSmartVolumeMapper>::New();
|
vtkSmartPointer<vtkSmartVolumeMapper>::New();
|
||||||
@@ -320,7 +350,7 @@ void vtkVoxImage::InstallPipe() {
|
|||||||
mapper->Update();
|
mapper->Update();
|
||||||
|
|
||||||
m_Actor->SetMapper(mapper);
|
m_Actor->SetMapper(mapper);
|
||||||
this->setShadingPreset(0);
|
this->setShadingPreset(m_ShadingPreset);
|
||||||
mapper->Update();
|
mapper->Update();
|
||||||
|
|
||||||
m_Outline->SetBounds(m_Image->GetBounds());
|
m_Outline->SetBounds(m_Image->GetBounds());
|
||||||
@@ -337,7 +367,7 @@ void vtkVoxImage::InstallPipe() {
|
|||||||
this->SetProp(m_Asm);
|
this->SetProp(m_Asm);
|
||||||
|
|
||||||
// Default look
|
// Default look
|
||||||
this->SetRepresentation(Surface);
|
this->SetRepresentation(Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
|
|
||||||
class vtkImageData;
|
class vtkImageData;
|
||||||
class vtkActor;
|
class vtkActor;
|
||||||
|
class vtkColorTransferFunction;
|
||||||
|
class vtkPiecewiseFunction;
|
||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
namespace Vtk {
|
namespace Vtk {
|
||||||
@@ -55,6 +57,8 @@ public:
|
|||||||
|
|
||||||
void SetContent();
|
void SetContent();
|
||||||
|
|
||||||
|
vtkProp3D *GetProp() override { return m_Asm; }
|
||||||
|
|
||||||
vtkImageData *GetImageData();
|
vtkImageData *GetImageData();
|
||||||
|
|
||||||
void SaveToXMLFile(const char *fname);
|
void SaveToXMLFile(const char *fname);
|
||||||
@@ -65,8 +69,10 @@ public:
|
|||||||
|
|
||||||
void setShadingPreset(int blendType = 2);
|
void setShadingPreset(int blendType = 2);
|
||||||
void SetRepresentation(Representation mode);
|
void SetRepresentation(Representation mode);
|
||||||
|
void RescaleShaderRange();
|
||||||
|
|
||||||
void Update() override;
|
void Update() override;
|
||||||
|
void SyncFromVtk() override;
|
||||||
void serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version = 0) override;
|
void serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version = 0) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -88,6 +94,11 @@ private:
|
|||||||
float m_Window;
|
float m_Window;
|
||||||
float m_Level;
|
float m_Level;
|
||||||
int m_ShadingPreset;
|
int m_ShadingPreset;
|
||||||
|
|
||||||
|
Connection m_UpdateConnection;
|
||||||
|
|
||||||
|
class vtkColorTransferFunction *m_ColorFun;
|
||||||
|
class vtkPiecewiseFunction *m_OpacityFun;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ set(TESTS
|
|||||||
vtkHandlerWidget
|
vtkHandlerWidget
|
||||||
PuppetPropertyTest
|
PuppetPropertyTest
|
||||||
PuppetParentingTest
|
PuppetParentingTest
|
||||||
|
vtkQViewportTest
|
||||||
# vtkVoxImageTest
|
# vtkVoxImageTest
|
||||||
# vtkTriangleMeshTest
|
# vtkTriangleMeshTest
|
||||||
)
|
)
|
||||||
|
|||||||
87
src/Vtk/testing/vtkQViewportTest.cpp
Normal file
87
src/Vtk/testing/vtkQViewportTest.cpp
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 <QApplication>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <Vtk/vtkQViewport.h>
|
||||||
|
|
||||||
|
#include <vtkSmartPointer.h>
|
||||||
|
#include <vtkCubeSource.h>
|
||||||
|
#include <vtkPolyDataMapper.h>
|
||||||
|
#include <vtkActor.h>
|
||||||
|
#include <vtkProperty.h>
|
||||||
|
#include <vtkRenderer.h>
|
||||||
|
|
||||||
|
#include "testing-prototype.h"
|
||||||
|
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
// Force X11 on Linux to avoid Wayland connection issues in headless/X11 environments
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BEGIN_TESTING(vtk QViewport Test);
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
app.processEvents();
|
||||||
|
|
||||||
|
Vtk::QViewport viewport;
|
||||||
|
viewport.resize(800, 600);
|
||||||
|
viewport.show();
|
||||||
|
|
||||||
|
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
|
||||||
|
cube->SetXLength(10);
|
||||||
|
cube->SetYLength(10);
|
||||||
|
cube->SetZLength(10);
|
||||||
|
cube->Update();
|
||||||
|
|
||||||
|
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||||
|
mapper->SetInputConnection(cube->GetOutputPort());
|
||||||
|
|
||||||
|
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
|
||||||
|
actor->SetMapper(mapper);
|
||||||
|
actor->GetProperty()->SetColor(1, 0, 0);
|
||||||
|
|
||||||
|
viewport.addProp(actor);
|
||||||
|
viewport.Render();
|
||||||
|
|
||||||
|
ASSERT_NOT_NULL(viewport.GetRenderWindow());
|
||||||
|
ASSERT_NOT_NULL(viewport.GetRenderer());
|
||||||
|
|
||||||
|
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {
|
||||||
|
// Run application for a while to see the result
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
END_TESTING;
|
||||||
|
}
|
||||||
@@ -134,11 +134,11 @@ public:
|
|||||||
|
|
||||||
vtkActor *actor = vtkActor::SafeDownCast(p);
|
vtkActor *actor = vtkActor::SafeDownCast(p);
|
||||||
if (actor) {
|
if (actor) {
|
||||||
if (m_Representation != -1) {
|
if (m_Representation != -1 && m_Representation != Puppet::Volume) {
|
||||||
if (m_Representation == Puppet::SurfaceWithEdges) {
|
if (m_Representation == Puppet::SurfaceWithEdges) {
|
||||||
actor->GetProperty()->SetRepresentation(VTK_SURFACE);
|
actor->GetProperty()->SetRepresentation(VTK_SURFACE);
|
||||||
actor->GetProperty()->SetEdgeVisibility(1);
|
actor->GetProperty()->SetEdgeVisibility(1);
|
||||||
} else {
|
} else if (m_Representation != Puppet::Outline && m_Representation != Puppet::Slice) {
|
||||||
actor->GetProperty()->SetRepresentation(m_Representation);
|
actor->GetProperty()->SetRepresentation(m_Representation);
|
||||||
actor->GetProperty()->SetEdgeVisibility(0);
|
actor->GetProperty()->SetEdgeVisibility(0);
|
||||||
}
|
}
|
||||||
@@ -628,6 +628,8 @@ struct AppearanceProxy {
|
|||||||
ar & boost::serialization::make_hrp("Visibility", pd->m_Visibility);
|
ar & boost::serialization::make_hrp("Visibility", pd->m_Visibility);
|
||||||
ar & boost::serialization::make_hrp("Pickable", pd->m_Selectable);
|
ar & boost::serialization::make_hrp("Pickable", pd->m_Selectable);
|
||||||
ar & boost::serialization::make_hrp("Dragable", pd->m_Dragable);
|
ar & boost::serialization::make_hrp("Dragable", pd->m_Dragable);
|
||||||
|
ar & boost::serialization::make_hrp("ShowBoundingBox", pd->m_ShowBoundingBox);
|
||||||
|
ar & boost::serialization::make_hrp("ShowScaleMeasures", pd->m_ShowScaleMeasures);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ struct ViewerData {
|
|||||||
vtkRenderWindow *m_RenderWindow;
|
vtkRenderWindow *m_RenderWindow;
|
||||||
vtkSmartPointer<vtkRenderWindowInteractor> m_Interactor;
|
vtkSmartPointer<vtkRenderWindowInteractor> m_Interactor;
|
||||||
vtkSmartPointer<vtkButtonWidget> m_GridButton;
|
vtkSmartPointer<vtkButtonWidget> m_GridButton;
|
||||||
|
vtkSmartPointer<vtkButtonWidget> m_ProjButton;
|
||||||
|
|
||||||
ViewerData() : m_RenderWindow(vtkRenderWindow::New()) {}
|
ViewerData() : m_RenderWindow(vtkRenderWindow::New()) {}
|
||||||
~ViewerData() {
|
~ViewerData() {
|
||||||
@@ -97,6 +98,11 @@ Viewer::~Viewer() {
|
|||||||
dv->m_GridButton->SetInteractor(nullptr);
|
dv->m_GridButton->SetInteractor(nullptr);
|
||||||
dv->m_GridButton = 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()) {
|
if (this->GetRenderWindow()) {
|
||||||
this->GetRenderWindow()->RemoveAllObservers();
|
this->GetRenderWindow()->RemoveAllObservers();
|
||||||
}
|
}
|
||||||
@@ -123,6 +129,7 @@ void Viewer::InstallPipe() {
|
|||||||
// Setup native grid button
|
// Setup native grid button
|
||||||
if (!std::getenv("CTEST_PROJECT_NAME")) {
|
if (!std::getenv("CTEST_PROJECT_NAME")) {
|
||||||
SetupGridButton();
|
SetupGridButton();
|
||||||
|
SetupProjButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUT we want to override the style with our custom NoSpin version
|
// BUT we want to override the style with our custom NoSpin version
|
||||||
@@ -238,6 +245,88 @@ void Viewer::UpdateGridButtonPosition() {
|
|||||||
rep->PlaceWidget(bds);
|
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() {
|
void Viewer::Start() {
|
||||||
if (std::getenv("CTEST_PROJECT_NAME")) return;
|
if (std::getenv("CTEST_PROJECT_NAME")) return;
|
||||||
dv->m_RenderWindow->GetInteractor()->Start();
|
dv->m_RenderWindow->GetInteractor()->Start();
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ private:
|
|||||||
void SetupGridButton();
|
void SetupGridButton();
|
||||||
void UpdateGridButtonPosition();
|
void UpdateGridButtonPosition();
|
||||||
|
|
||||||
|
void SetupProjButton();
|
||||||
|
void UpdateProjButtonPosition();
|
||||||
|
|
||||||
struct ViewerData *dv;
|
struct ViewerData *dv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -118,18 +118,17 @@ void vtkObjectsContext::SyncFromVtk() {
|
|||||||
Puppet* vtkObjectsContext::CreatePuppet(uLib::Object* obj) {
|
Puppet* vtkObjectsContext::CreatePuppet(uLib::Object* obj) {
|
||||||
if (!obj) return nullptr;
|
if (!obj) return nullptr;
|
||||||
|
|
||||||
if (auto* box = dynamic_cast<uLib::ContainerBox*>(obj)) {
|
if (auto* vox = dynamic_cast<uLib::Abstract::VoxImage*>(obj)) {
|
||||||
|
return new vtkVoxImage(*vox);
|
||||||
|
} else if (auto* box = dynamic_cast<uLib::ContainerBox*>(obj)) {
|
||||||
return new vtkContainerBox(box);
|
return new vtkContainerBox(box);
|
||||||
} else if (auto* chamber = dynamic_cast<uLib::DetectorChamber*>(obj)) {
|
} else if (auto* chamber = dynamic_cast<uLib::DetectorChamber*>(obj)) {
|
||||||
return new vtkDetectorChamber(chamber);
|
return new vtkDetectorChamber(chamber);
|
||||||
} else if (auto* cylinder = dynamic_cast<uLib::Cylinder*>(obj)) {
|
} else if (auto* cylinder = dynamic_cast<uLib::Cylinder*>(obj)) {
|
||||||
return new vtkCylinder(cylinder);
|
return new vtkCylinder(cylinder);
|
||||||
} else if (auto* vox = dynamic_cast<uLib::Abstract::VoxImage*>(obj)) {
|
|
||||||
return new vtkVoxImage(*vox);
|
|
||||||
} else if (auto* assembly = dynamic_cast<uLib::Assembly*>(obj)) {
|
} else if (auto* assembly = dynamic_cast<uLib::Assembly*>(obj)) {
|
||||||
return new Assembly(assembly);
|
return new Assembly(assembly);
|
||||||
}
|
} else if (auto* box = dynamic_cast<uLib::Geant::BoxSolid*>(obj)) {
|
||||||
else if (auto* box = dynamic_cast<uLib::Geant::BoxSolid*>(obj)) {
|
|
||||||
return new vtkBoxSolid(box);
|
return new vtkBoxSolid(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ QViewport::QViewport(QWidget* parent)
|
|||||||
, Viewport()
|
, Viewport()
|
||||||
, m_VtkWidget(nullptr)
|
, m_VtkWidget(nullptr)
|
||||||
, m_GridButton(nullptr)
|
, m_GridButton(nullptr)
|
||||||
|
, m_ProjButton(nullptr)
|
||||||
{
|
{
|
||||||
// Build the layout – zero margins so VTK fills the entire widget
|
// Build the layout – zero margins so VTK fills the entire widget
|
||||||
auto* layout = new QVBoxLayout(this);
|
auto* layout = new QVBoxLayout(this);
|
||||||
@@ -58,6 +59,36 @@ QViewport::QViewport(QWidget* parent)
|
|||||||
m_GridButton->setChecked(true); // Grid is on by default
|
m_GridButton->setChecked(true); // Grid is on by default
|
||||||
connect(m_GridButton, &QPushButton::clicked, this, &QViewport::onGridButtonClicked);
|
connect(m_GridButton, &QPushButton::clicked, this, &QViewport::onGridButtonClicked);
|
||||||
|
|
||||||
|
// Projection Toggle Button (below grid button)
|
||||||
|
m_ProjButton = new QPushButton(m_VtkWidget);
|
||||||
|
m_ProjButton->setText("P");
|
||||||
|
m_ProjButton->setFixedSize(40, 40);
|
||||||
|
m_ProjButton->setToolTip("Toggle Perspective / Orthographic");
|
||||||
|
m_ProjButton->setStyleSheet(
|
||||||
|
"QPushButton {"
|
||||||
|
" border-radius: 20px;"
|
||||||
|
" background-color: rgba(40, 40, 40, 180);"
|
||||||
|
" color: white;"
|
||||||
|
" font-size: 22px;"
|
||||||
|
" border: 1.5px solid rgba(255, 255, 255, 60);"
|
||||||
|
"}"
|
||||||
|
"QPushButton:hover {"
|
||||||
|
" background-color: rgba(70, 70, 70, 200);"
|
||||||
|
" border: 1.5px solid rgba(255, 255, 255, 100);"
|
||||||
|
"}"
|
||||||
|
"QPushButton:checked {"
|
||||||
|
" background-color: rgba(0, 120, 215, 200);"
|
||||||
|
" color: white;"
|
||||||
|
" border: 1.5px solid rgba(255, 255, 255, 120);"
|
||||||
|
"}"
|
||||||
|
"QPushButton:pressed {"
|
||||||
|
" background-color: rgba(0, 90, 160, 220);"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
m_ProjButton->setCheckable(true);
|
||||||
|
m_ProjButton->setChecked(false); // Perspective by default
|
||||||
|
connect(m_ProjButton, &QPushButton::clicked, this, &QViewport::onProjButtonClicked);
|
||||||
|
|
||||||
// After the Qt widget exists but before the first paint,
|
// After the Qt widget exists but before the first paint,
|
||||||
// attach the renderer and configure the pipeline.
|
// attach the renderer and configure the pipeline.
|
||||||
SetupPipeline();
|
SetupPipeline();
|
||||||
@@ -81,7 +112,6 @@ void QViewport::SetupPipeline()
|
|||||||
|
|
||||||
void QViewport::Render()
|
void QViewport::Render()
|
||||||
{
|
{
|
||||||
UpdateGrid();
|
|
||||||
if (m_VtkWidget && m_VtkWidget->renderWindow())
|
if (m_VtkWidget && m_VtkWidget->renderWindow())
|
||||||
m_VtkWidget->renderWindow()->Render();
|
m_VtkWidget->renderWindow()->Render();
|
||||||
}
|
}
|
||||||
@@ -101,6 +131,11 @@ void QViewport::onGridButtonClicked()
|
|||||||
SetGridVisible(m_GridButton->isChecked());
|
SetGridVisible(m_GridButton->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QViewport::onProjButtonClicked()
|
||||||
|
{
|
||||||
|
SetParallelProjection(m_ProjButton->isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
void QViewport::OnSelectionChanged(Puppet* p)
|
void QViewport::OnSelectionChanged(Puppet* p)
|
||||||
{
|
{
|
||||||
emit puppetSelected(p);
|
emit puppetSelected(p);
|
||||||
@@ -116,6 +151,11 @@ void QViewport::resizeEvent(QResizeEvent* event)
|
|||||||
int y = 160;
|
int y = 160;
|
||||||
m_GridButton->move(x, y);
|
m_GridButton->move(x, y);
|
||||||
}
|
}
|
||||||
|
if (m_ProjButton) {
|
||||||
|
int x = width() - m_ProjButton->width() - 10;
|
||||||
|
int y = 210;
|
||||||
|
m_ProjButton->move(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
|
|||||||
@@ -51,12 +51,14 @@ protected:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onGridButtonClicked();
|
void onGridButtonClicked();
|
||||||
|
void onProjButtonClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupPipeline();
|
void SetupPipeline();
|
||||||
|
|
||||||
QVTKOpenGLNativeWidget* m_VtkWidget;
|
QVTKOpenGLNativeWidget* m_VtkWidget;
|
||||||
QPushButton* m_GridButton;
|
QPushButton* m_GridButton;
|
||||||
|
QPushButton* m_ProjButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
|
|||||||
@@ -537,6 +537,21 @@ bool Viewport::GetGridVisible() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Viewport::SetParallelProjection(bool parallel)
|
||||||
|
{
|
||||||
|
if (pv->m_Renderer && pv->m_Renderer->GetActiveCamera()) {
|
||||||
|
pv->m_Renderer->GetActiveCamera()->SetParallelProjection(parallel ? 1 : 0);
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Viewport::GetParallelProjection() const
|
||||||
|
{
|
||||||
|
if (pv->m_Renderer && pv->m_Renderer->GetActiveCamera())
|
||||||
|
return pv->m_Renderer->GetActiveCamera()->GetParallelProjection() != 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Viewport::SetGridAxis(Axis axis)
|
void Viewport::SetGridAxis(Axis axis)
|
||||||
{
|
{
|
||||||
m_GridAxis = axis;
|
m_GridAxis = axis;
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ public:
|
|||||||
void SetGridAxis(Axis axis);
|
void SetGridAxis(Axis axis);
|
||||||
Axis GetGridAxis() const { return m_GridAxis; }
|
Axis GetGridAxis() const { return m_GridAxis; }
|
||||||
|
|
||||||
|
// Projection control
|
||||||
|
void SetParallelProjection(bool parallel);
|
||||||
|
bool GetParallelProjection() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetupPipeline(vtkRenderWindowInteractor* iren);
|
void SetupPipeline(vtkRenderWindowInteractor* iren);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user