Compare commits
8 Commits
59a9e829fc
...
andrea-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e09a614fa5 | ||
|
|
7f6323403d | ||
|
|
a53b3051de | ||
|
|
c53570192f | ||
|
|
6396bdfebf | ||
|
|
96ab3b0930 | ||
|
|
5c04d00d4c | ||
|
|
72e69cfca5 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
build/
|
build*/
|
||||||
.cache/
|
.cache/
|
||||||
|
CMakeUserPresets.json
|
||||||
build_warnings*.log
|
build_warnings*.log
|
||||||
final_build.log
|
final_build.log
|
||||||
cmake_configure.log
|
cmake_configure.log
|
||||||
@@ -16,3 +17,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
|
||||||
|
|||||||
14
CLAUDE.md
14
CLAUDE.md
@@ -12,7 +12,7 @@ export MAMBA_ROOT_PREFIX="/home/share/micromamba"
|
|||||||
eval "$(/home/share/micromamba/bin/micromamba shell hook --shell bash)"
|
eval "$(/home/share/micromamba/bin/micromamba shell hook --shell bash)"
|
||||||
micromamba activate mutom
|
micromamba activate mutom
|
||||||
|
|
||||||
# Configure (from repo root, using Conan preset)
|
# Configure (from repo root, using Conan preset — uses Ninja + ccache)
|
||||||
cmake --preset conan-release
|
cmake --preset conan-release
|
||||||
|
|
||||||
# Build everything
|
# Build everything
|
||||||
@@ -40,6 +40,18 @@ conan install . --output-folder=build --build=missing
|
|||||||
cmake --preset conan-release
|
cmake --preset conan-release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Build acceleration (already configured)
|
||||||
|
- **Ninja** generator — used automatically via the conan default profile (`~/.conan2/profiles/default`)
|
||||||
|
- **ccache** — enabled via `CMAKE_CXX_COMPILER_LAUNCHER=ccache`; cached rebuilds are nearly instant (~0.3s vs ~25s cold)
|
||||||
|
- **Clang 22 + lld** profile available (`~/.conan2/profiles/fast`) but blocked by template overload ambiguities in `src/Core/Archives.h` that need fixing for full compatibility
|
||||||
|
|
||||||
|
To reconfigure with the fast profile once Archives.h is fixed:
|
||||||
|
```bash
|
||||||
|
conan install . --output-folder=build --build=missing --profile=fast
|
||||||
|
cmake -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build build -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
**uLib** is a C++ framework for Cosmic Muon Tomography (CMT), structured as layered shared libraries:
|
**uLib** is a C++ framework for Cosmic Muon Tomography (CMT), structured as layered shared libraries:
|
||||||
|
|||||||
@@ -169,6 +169,26 @@ if(Geant4_FOUND)
|
|||||||
add_compile_definitions(HAVE_GEANT4)
|
add_compile_definitions(HAVE_GEANT4)
|
||||||
set(HAVE_GEANT4 1)
|
set(HAVE_GEANT4 1)
|
||||||
|
|
||||||
|
# Workaround: Geant4's G4EXPATShim creates EXPAT::EXPAT (uppercase) with
|
||||||
|
# IMPORTED_LOCATION "${EXPAT_LIBRARY}", but EXPAT_LIBRARY is empty when using
|
||||||
|
# conda's config-mode expat package (which installs as expat::expat lowercase).
|
||||||
|
# Resolve the actual library path from expat::expat or via find_library.
|
||||||
|
if(TARGET EXPAT::EXPAT)
|
||||||
|
get_target_property(_expat_loc EXPAT::EXPAT IMPORTED_LOCATION)
|
||||||
|
if(NOT _expat_loc OR _expat_loc MATCHES "NOTFOUND|^$")
|
||||||
|
if(TARGET expat::expat)
|
||||||
|
get_target_property(_expat_loc expat::expat IMPORTED_LOCATION_NOCONFIG)
|
||||||
|
endif()
|
||||||
|
if(NOT _expat_loc OR _expat_loc MATCHES "NOTFOUND|^$")
|
||||||
|
find_library(_expat_loc NAMES expat)
|
||||||
|
endif()
|
||||||
|
if(_expat_loc)
|
||||||
|
set_target_properties(EXPAT::EXPAT PROPERTIES IMPORTED_LOCATION "${_expat_loc}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
unset(_expat_loc)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Sanitize Geant4 targets to remove Qt5 dependencies that conflict with VTK/Qt6
|
# Sanitize Geant4 targets to remove Qt5 dependencies that conflict with VTK/Qt6
|
||||||
if(TARGET Geant4::G4interfaces)
|
if(TARGET Geant4::G4interfaces)
|
||||||
set_target_properties(Geant4::G4interfaces PROPERTIES
|
set_target_properties(Geant4::G4interfaces PROPERTIES
|
||||||
|
|||||||
@@ -12,6 +12,22 @@
|
|||||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
|
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "fast",
|
||||||
|
"displayName": "Fast build: Ninja + clang + ccache",
|
||||||
|
"description": "Uses Ninja generator, clang/lld compiler, and ccache",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release",
|
||||||
|
"CMAKE_C_COMPILER": "clang",
|
||||||
|
"CMAKE_CXX_COMPILER": "clang++",
|
||||||
|
"CMAKE_EXE_LINKER_FLAGS": "-fuse-ld=lld",
|
||||||
|
"CMAKE_SHARED_LINKER_FLAGS": "-fuse-ld=lld",
|
||||||
|
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
|
||||||
|
"CMAKE_C_COMPILER_LAUNCHER": "ccache"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mutom",
|
"name": "mutom",
|
||||||
"description": "",
|
"description": "",
|
||||||
@@ -19,4 +35,4 @@
|
|||||||
"inherits": []
|
"inherits": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 4,
|
|
||||||
"vendor": {
|
|
||||||
"conan": {}
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"build/CMakePresets.json"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
36
README.md
36
README.md
@@ -41,7 +41,11 @@ conda activate mutom
|
|||||||
|
|
||||||
### Configure and Build
|
### Configure and Build
|
||||||
|
|
||||||
1. **Configure Conan profile (if you haven't yet on your machine):**
|
#### Standard build (GCC + Ninja + ccache)
|
||||||
|
|
||||||
|
The default conan profile uses **Ninja** as the generator and **ccache** for compiler caching, dramatically speeding up incremental rebuilds.
|
||||||
|
|
||||||
|
1. **Configure Conan profile (first time only):**
|
||||||
```bash
|
```bash
|
||||||
conan profile detect
|
conan profile detect
|
||||||
```
|
```
|
||||||
@@ -51,17 +55,39 @@ conan profile detect
|
|||||||
conan install . --output-folder=build --build=missing
|
conan install . --output-folder=build --build=missing
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Configure the project with CMake:**
|
3. **Configure with CMake:**
|
||||||
```bash
|
```bash
|
||||||
cmake --preset conan-release
|
cmake --preset conan-release
|
||||||
```
|
```
|
||||||
*(Alternatively: `cd build && cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release`)*
|
|
||||||
|
|
||||||
4. **Build the project:**
|
4. **Build:**
|
||||||
```bash
|
```bash
|
||||||
cmake --build build -j10
|
cmake --build build -j$(nproc)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### LLVM/Clang build (clang + lld + ccache — fastest)
|
||||||
|
|
||||||
|
A `fast` conan profile is provided that uses **clang**, **lld** (LLVM linker), and **ccache**. Install them into your environment first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
micromamba install -n mutom -y clang clangxx lld -c conda-forge
|
||||||
|
```
|
||||||
|
|
||||||
|
Then build using the `fast` profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conan install . --output-folder=build --build=missing --profile=fast
|
||||||
|
cmake -B build -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build build -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `fast` profile is defined at `~/.conan2/profiles/fast` and sets:
|
||||||
|
- `CMAKE_C_COMPILER=clang` / `CMAKE_CXX_COMPILER=clang++`
|
||||||
|
- `CMAKE_EXE_LINKER_FLAGS=-fuse-ld=lld`
|
||||||
|
- `CMAKE_CXX_COMPILER_LAUNCHER=ccache`
|
||||||
|
|
||||||
### Make python package
|
### Make python package
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -338,8 +338,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using basic_xml_iarchive::load_override;
|
|
||||||
|
|
||||||
// Anything not an attribute should be a name value pair as nvp or hrp
|
// Anything not an attribute should be a name value pair as nvp or hrp
|
||||||
typedef boost::archive::detail::common_iarchive<Archive>
|
typedef boost::archive::detail::common_iarchive<Archive>
|
||||||
detail_common_iarchive;
|
detail_common_iarchive;
|
||||||
@@ -357,6 +355,9 @@ public:
|
|||||||
// class_name_type can't be handled here as it depends upon the
|
// class_name_type can't be handled here as it depends upon the
|
||||||
// char type used by the stream. So require the derived implementation.
|
// char type used by the stream. So require the derived implementation.
|
||||||
// derived in this case is xml_iarchive_impl or base ..
|
// derived in this case is xml_iarchive_impl or base ..
|
||||||
|
// Note: using base::load_override covers all basic_xml_iarchive overloads
|
||||||
|
// transitively, so a separate 'using basic_xml_iarchive::load_override'
|
||||||
|
// is redundant and creates ambiguity with clang.
|
||||||
using base::load_override;
|
using base::load_override;
|
||||||
|
|
||||||
void load_override(const char *str) {
|
void load_override(const char *str) {
|
||||||
|
|||||||
@@ -121,14 +121,6 @@ void Object::PropertyUpdated() { ULIB_SIGNAL_EMIT(Object::PropertyUpdated); }
|
|||||||
template <class ArchiveT>
|
template <class ArchiveT>
|
||||||
void Object::save_override(ArchiveT &ar, const unsigned int version) {}
|
void Object::save_override(ArchiveT &ar, const unsigned int version) {}
|
||||||
|
|
||||||
// Explicitly instantiate for all uLib archives
|
|
||||||
template void Object::serialize(Archive::xml_oarchive &, const unsigned int);
|
|
||||||
template void Object::serialize(Archive::xml_iarchive &, const unsigned int);
|
|
||||||
template void Object::serialize(Archive::text_oarchive &, const unsigned int);
|
|
||||||
template void Object::serialize(Archive::text_iarchive &, const unsigned int);
|
|
||||||
template void Object::serialize(Archive::hrt_oarchive &, const unsigned int);
|
|
||||||
template void Object::serialize(Archive::hrt_iarchive &, const unsigned int);
|
|
||||||
template void Object::serialize(Archive::log_archive &, const unsigned int);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ typedef bool Bool_t; // Boolean (0=false, 1=true) (bool)
|
|||||||
\
|
\
|
||||||
public: \
|
public: \
|
||||||
typedef type_info::BaseClass BaseClass; \
|
typedef type_info::BaseClass BaseClass; \
|
||||||
virtual const char *type_name() const { return type_info::name; } \
|
virtual const char *type_name() const override { return type_info::name; } \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ vtkVoxImage::vtkVoxImage(Content &content)
|
|||||||
GetContent();
|
GetContent();
|
||||||
InstallPipe();
|
InstallPipe();
|
||||||
RescaleShaderRange();
|
RescaleShaderRange();
|
||||||
|
ULIB_ACTIVATE_DISPLAY_PROPERTIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
vtkVoxImage::~vtkVoxImage() {
|
vtkVoxImage::~vtkVoxImage() {
|
||||||
@@ -298,7 +299,7 @@ void vtkVoxImage::SetRepresentation(Representation mode) {
|
|||||||
|
|
||||||
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 to show Transform and Appearance properties
|
// Call base class to show Transform and Appearance properties
|
||||||
Puppet::serialize_display(ar, version);
|
// Puppet::serialize_display(ar, version);
|
||||||
|
|
||||||
// Use the member variables for volume rendering parameters
|
// Use the member variables for volume rendering parameters
|
||||||
ar & boost::serialization::make_hrp("Window", m_Window);
|
ar & boost::serialization::make_hrp("Window", m_Window);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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,9 +245,91 @@ void Viewer::UpdateGridButtonPosition() {
|
|||||||
rep->PlaceWidget(bds);
|
rep->PlaceWidget(bds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::Start() {
|
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;
|
if (std::getenv("CTEST_PROJECT_NAME")) return;
|
||||||
dv->m_RenderWindow->GetInteractor()->Start();
|
dv->m_RenderWindow->GetInteractor()->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
vtkRenderWindow *Viewer::GetRenderWindow() { return dv->m_RenderWindow; }
|
vtkRenderWindow *Viewer::GetRenderWindow() { return dv->m_RenderWindow; }
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ private:
|
|||||||
void SetupGridButton();
|
void SetupGridButton();
|
||||||
void UpdateGridButtonPosition();
|
void UpdateGridButtonPosition();
|
||||||
|
|
||||||
|
void SetupProjButton();
|
||||||
|
void UpdateProjButtonPosition();
|
||||||
|
|
||||||
struct ViewerData *dv;
|
struct ViewerData *dv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -113,9 +148,14 @@ void QViewport::resizeEvent(QResizeEvent* event)
|
|||||||
// Position under the gizmo (top-right corner).
|
// Position under the gizmo (top-right corner).
|
||||||
// Standard CameraOrientationWidget is usually 150-180px.
|
// Standard CameraOrientationWidget is usually 150-180px.
|
||||||
int x = width() - m_GridButton->width() - 10;
|
int x = width() - m_GridButton->width() - 10;
|
||||||
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