Compare commits
10 Commits
f7ba4b1a17
...
fix-assemb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
010927714f | ||
|
|
bf4006ff91 | ||
|
|
e320c932d2 | ||
|
|
f8f92ebf3d | ||
|
|
e8c10daf6d | ||
|
|
22262d3dc6 | ||
|
|
dfd33e9a9c | ||
|
|
76f29328cd | ||
|
|
db76513e79 | ||
|
|
64a87e97e3 |
@@ -19,6 +19,18 @@ endif()
|
||||
|
||||
project(uLib)
|
||||
|
||||
option(ULIB_USE_CCACHE "Use ccache for build acceleration" ON)
|
||||
if(ULIB_USE_CCACHE)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "")
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "")
|
||||
endif()
|
||||
|
||||
# Applica la flag SOLO se il compilatore è GCC
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_compile_options(-fno-merge-constants)
|
||||
@@ -130,8 +142,11 @@ find_package(Boost 1.45.0 COMPONENTS program_options serialization unit_test_fra
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
find_package(Eigen3 CONFIG REQUIRED)
|
||||
get_target_property(EIGEN3_INCLUDE_DIRS Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
|
||||
include_directories(${EIGEN3_INCLUDE_DIRS})
|
||||
# if(NOT EIGEN3_INCLUDE_DIRS)
|
||||
# get_target_property(EIGEN3_INCLUDE_DIRS Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
|
||||
# else()
|
||||
# include_directories(${EIGEN3_INCLUDE_DIRS})
|
||||
# endif()
|
||||
|
||||
find_package(OpenMP)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ add_executable(gcompose
|
||||
src/PropertyWidgets.cpp
|
||||
src/PropertiesPanel.h
|
||||
src/PropertiesPanel.cpp
|
||||
src/PreferencesDialog.h
|
||||
src/PreferencesDialog.cpp
|
||||
)
|
||||
|
||||
set_target_properties(gcompose PROPERTIES
|
||||
|
||||
@@ -148,6 +148,7 @@ QVariant ContextModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid()) return QVariant();
|
||||
|
||||
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
|
||||
if (!obj) return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
QString typeName = getDemangledName(typeid(*obj));
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
#include <QPushButton>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QShortcut>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include "StyleManager.h"
|
||||
#include "Math/VoxImage.h"
|
||||
#include "PreferencesDialog.h"
|
||||
#include "Settings.h"
|
||||
|
||||
MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_mainVtkContext(nullptr) {
|
||||
this->setObjectName("MainPanel");
|
||||
@@ -45,17 +48,12 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m
|
||||
fileMenu->addAction("Open", this, &MainPanel::onOpen);
|
||||
fileMenu->addAction("Save", this, &MainPanel::onSave);
|
||||
fileMenu->addAction("Save As", this, &MainPanel::onSaveAs);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction("Preferences", this, &MainPanel::onPreferences);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction("Exit", this, &MainPanel::onExit);
|
||||
btnFile->setMenu(fileMenu);
|
||||
|
||||
// Theme Menu Button
|
||||
auto* btnTheme = new QPushButton("Theme", menuPanel);
|
||||
btnTheme->setObjectName("MenuButton");
|
||||
auto* themeMenu = new QMenu(btnTheme);
|
||||
themeMenu->addAction("Dark", this, &MainPanel::onDarkTheme);
|
||||
themeMenu->addAction("Bright", this, &MainPanel::onBrightTheme);
|
||||
btnTheme->setMenu(themeMenu);
|
||||
|
||||
// New Menu Button
|
||||
auto* btnNew = new QPushButton("Add", menuPanel);
|
||||
btnNew->setObjectName("MenuButton");
|
||||
@@ -73,7 +71,6 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m
|
||||
menuLayout->addWidget(logo);
|
||||
menuLayout->addWidget(btnFile);
|
||||
menuLayout->addWidget(btnNew);
|
||||
menuLayout->addWidget(btnTheme);
|
||||
menuLayout->addStretch();
|
||||
|
||||
mainLayout->addWidget(menuPanel);
|
||||
@@ -108,6 +105,14 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m
|
||||
m_rootSplitter->setSizes(sizes);
|
||||
|
||||
mainLayout->addWidget(m_rootSplitter, 1);
|
||||
|
||||
// Shortcuts
|
||||
auto* groupShortcut = new QShortcut(QKeySequence("Ctrl+G"), this);
|
||||
connect(groupShortcut, &QShortcut::activated, [this]() {
|
||||
if (auto* viewport = qobject_cast<uLib::Vtk::QViewport*>(m_firstPane->currentViewport())) {
|
||||
viewport->GroupSelection(m_context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MainPanel::setContext(uLib::ObjectsContext* context) {
|
||||
@@ -236,12 +241,21 @@ void MainPanel::onExit() {
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void MainPanel::onDarkTheme() {
|
||||
StyleManager::applyStyle(qApp, "dark");
|
||||
}
|
||||
void MainPanel::onPreferences() {
|
||||
uLib::Qt::PreferencesDialog dlg(this);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
// Apply theme
|
||||
auto theme = uLib::Qt::Settings::Instance().GetTheme();
|
||||
StyleManager::applyStyle(qApp, theme == uLib::Qt::Settings::Dark ? "dark" : "bright");
|
||||
|
||||
void MainPanel::onBrightTheme() {
|
||||
StyleManager::applyStyle(qApp, "bright");
|
||||
// Apply rendering preference to all viewports
|
||||
bool throttled = uLib::Qt::Settings::Instance().GetThrottledRendering();
|
||||
auto viewports = this->findChildren<uLib::Vtk::QViewport*>();
|
||||
for (auto* vp : viewports) {
|
||||
vp->SetThrottledRendering(throttled);
|
||||
vp->Render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MainPanel::~MainPanel() {}
|
||||
|
||||
@@ -30,8 +30,7 @@ private slots:
|
||||
void onSaveAs();
|
||||
void onExit();
|
||||
|
||||
void onDarkTheme();
|
||||
void onBrightTheme();
|
||||
void onPreferences();
|
||||
|
||||
void onCreateObject(const std::string& className);
|
||||
|
||||
|
||||
99
app/gcompose/src/PreferencesDialog.cpp
Normal file
99
app/gcompose/src/PreferencesDialog.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "PreferencesDialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QGroupBox>
|
||||
|
||||
namespace uLib {
|
||||
namespace Qt {
|
||||
|
||||
PreferencesDialog::PreferencesDialog(QWidget* parent) : QDialog(parent) {
|
||||
setWindowTitle("Preferences");
|
||||
setMinimumWidth(400);
|
||||
|
||||
auto* mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setSpacing(20);
|
||||
mainLayout->setContentsMargins(20, 20, 20, 20);
|
||||
|
||||
// ── General / Rendering Settings ────────────────────────────────────────
|
||||
auto* renderingGroup = new QGroupBox("Appearance & Performance", this);
|
||||
auto* renderingLayout = new QVBoxLayout(renderingGroup);
|
||||
|
||||
auto* themeLayout = new QHBoxLayout();
|
||||
themeLayout->addWidget(new QLabel("Color Theme:"));
|
||||
m_themeCombo = new QComboBox(renderingGroup);
|
||||
m_themeCombo->addItem("Dark");
|
||||
m_themeCombo->addItem("Bright");
|
||||
m_themeCombo->setCurrentIndex(Settings::Instance().GetTheme() == Settings::Dark ? 0 : 1);
|
||||
themeLayout->addWidget(m_themeCombo);
|
||||
themeLayout->addStretch();
|
||||
|
||||
renderingLayout->addLayout(themeLayout);
|
||||
renderingLayout->addSpacing(10);
|
||||
|
||||
m_throttledRendering = new QCheckBox("Enable throttled rendering (recommended for performance)", renderingGroup);
|
||||
m_throttledRendering->setChecked(Settings::Instance().GetThrottledRendering());
|
||||
m_throttledRendering->setToolTip("Limits framerate to ~60fps to reduce CPU/GPU usage.");
|
||||
|
||||
renderingLayout->addWidget(m_throttledRendering);
|
||||
mainLayout->addWidget(renderingGroup);
|
||||
|
||||
// ── Units Settings ──────────────────────────────────────────────────────
|
||||
auto* unitsGroup = new QGroupBox("Preferred Units", this);
|
||||
auto* unitsLayout = new QFormLayout(unitsGroup);
|
||||
unitsLayout->setLabelAlignment(::Qt::AlignRight);
|
||||
unitsLayout->setSpacing(10);
|
||||
|
||||
auto addUnitRow = [&](const QString& label, Settings::Dimension dim, const QStringList& units) {
|
||||
auto* combo = new QComboBox(unitsGroup);
|
||||
combo->addItems(units);
|
||||
std::string current = Settings::Instance().GetPreferredUnit(dim);
|
||||
int idx = combo->findText(QString::fromStdString(current));
|
||||
if (idx >= 0) combo->setCurrentIndex(idx);
|
||||
|
||||
unitsLayout->addRow(label + ":", combo);
|
||||
m_unitCombos[dim] = combo;
|
||||
};
|
||||
|
||||
addUnitRow("Length", Settings::Length, {"m", "cm", "mm", "um", "nm"});
|
||||
addUnitRow("Angle", Settings::Angle, {"deg", "rad"});
|
||||
addUnitRow("Energy", Settings::Energy, {"MeV", "GeV", "eV", "keV", "TeV"});
|
||||
addUnitRow("Time", Settings::Time, {"ns", "s", "ms", "us"});
|
||||
|
||||
mainLayout->addWidget(unitsGroup);
|
||||
|
||||
mainLayout->addStretch();
|
||||
|
||||
// ── Buttons ─────────────────────────────────────────────────────────────
|
||||
auto* buttonLayout = new QHBoxLayout();
|
||||
buttonLayout->addStretch();
|
||||
|
||||
auto* btnCancel = new QPushButton("Cancel", this);
|
||||
connect(btnCancel, &QPushButton::clicked, this, &QDialog::reject);
|
||||
|
||||
auto* btnOk = new QPushButton("Apply", this);
|
||||
btnOk->setDefault(true);
|
||||
btnOk->setObjectName("DisplayToggleBtn"); // Reusing high-contrast style
|
||||
btnOk->setMinimumWidth(100);
|
||||
connect(btnOk, &QPushButton::clicked, this, &PreferencesDialog::onAccept);
|
||||
|
||||
buttonLayout->addWidget(btnCancel);
|
||||
buttonLayout->addWidget(btnOk);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
}
|
||||
|
||||
void PreferencesDialog::onAccept() {
|
||||
Settings::Instance().SetThrottledRendering(m_throttledRendering->isChecked());
|
||||
Settings::Instance().SetTheme(m_themeCombo->currentIndex() == 0 ? Settings::Dark : Settings::Bright);
|
||||
|
||||
for (auto const& pair : m_unitCombos) {
|
||||
Settings::Instance().SetPreferredUnit(pair.first, pair.second->currentText().toStdString());
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
} // namespace Qt
|
||||
} // namespace uLib
|
||||
31
app/gcompose/src/PreferencesDialog.h
Normal file
31
app/gcompose/src/PreferencesDialog.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef GCOMPOSE_PREFERENCESDIALOG_H
|
||||
#define GCOMPOSE_PREFERENCESDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "Settings.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Qt {
|
||||
|
||||
class PreferencesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PreferencesDialog(QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void onAccept();
|
||||
|
||||
private:
|
||||
QCheckBox* m_throttledRendering;
|
||||
QComboBox* m_themeCombo;
|
||||
std::map<Settings::Dimension, QComboBox*> m_unitCombos;
|
||||
};
|
||||
|
||||
} // namespace Qt
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
@@ -94,8 +94,8 @@ void QViewportPane::setObject(uLib::Object* obj) {
|
||||
|
||||
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||
if (m_viewport) {
|
||||
m_viewport->parentWidget()->layout()->removeWidget(m_viewport);
|
||||
delete m_viewport;
|
||||
// Use deleteLater() instead of delete to avoid crashes during repaint cycles
|
||||
m_viewport->deleteLater();
|
||||
}
|
||||
m_viewport = viewport;
|
||||
m_titleLabel->setText(title);
|
||||
@@ -105,21 +105,8 @@ void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||
mainAreaLayout->insertWidget(0, m_viewport);
|
||||
}
|
||||
|
||||
|
||||
QViewportPane::~QViewportPane() {}
|
||||
|
||||
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||
if (m_viewport) {
|
||||
m_layout->removeWidget(m_viewport);
|
||||
delete m_viewport;
|
||||
}
|
||||
m_viewport = viewport;
|
||||
m_titleLabel->setText(title);
|
||||
|
||||
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_layout->addWidget(m_viewport);
|
||||
}
|
||||
|
||||
void QViewportPane::addVtkViewport() {
|
||||
auto* viewport = new uLib::Vtk::QViewport(this);
|
||||
setViewport(viewport, "VTK Viewport");
|
||||
|
||||
@@ -23,6 +23,11 @@ public:
|
||||
Dimensionless
|
||||
};
|
||||
|
||||
enum Theme {
|
||||
Dark,
|
||||
Bright
|
||||
};
|
||||
|
||||
void SetPreferredUnit(Dimension dim, const std::string& unit) {
|
||||
m_PreferredUnits[dim] = unit;
|
||||
}
|
||||
@@ -64,9 +69,17 @@ public:
|
||||
return Dimensionless;
|
||||
}
|
||||
|
||||
bool GetThrottledRendering() const { return m_ThrottledRendering; }
|
||||
void SetThrottledRendering(bool enabled) { m_ThrottledRendering = enabled; }
|
||||
|
||||
Theme GetTheme() const { return m_Theme; }
|
||||
void SetTheme(Theme theme) { m_Theme = theme; }
|
||||
|
||||
private:
|
||||
Settings() {}
|
||||
Settings() : m_ThrottledRendering(true), m_Theme(Dark) {}
|
||||
std::map<Dimension, std::string> m_PreferredUnits;
|
||||
bool m_ThrottledRendering;
|
||||
Theme m_Theme;
|
||||
};
|
||||
|
||||
} // namespace Qt
|
||||
|
||||
@@ -115,7 +115,7 @@ void ViewportPane::setObject(uLib::Object* obj) {
|
||||
|
||||
void ViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||
if (m_viewport) {
|
||||
delete m_viewport;
|
||||
m_viewport->deleteLater();
|
||||
}
|
||||
m_viewport = viewport;
|
||||
m_titleLabel->setText(title);
|
||||
|
||||
37
build.log
37
build.log
@@ -1,36 +1 @@
|
||||
make: Entering directory '/home/rigoni/devel/cmt/ulib/build'
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/VoxImage.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/TriangleMesh.cpp.o
|
||||
[ 30%] Building CXX object src/Core/CMakeFiles/mutomCore.dir/Options.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/Dense.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/StructuredGrid.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/VoxRaytracer.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/StructuredData.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/Structured2DGrid.cpp.o
|
||||
[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/Structured4DGrid.cpp.o
|
||||
[ 33%] Linking CXX shared library libmutomCore.so
|
||||
[ 33%] Built target mutomCore
|
||||
[ 36%] Linking CXX shared library libmutomMath.so
|
||||
[ 36%] Built target mutomMath
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkContainerBox.cpp.o
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/uLibVtkInterface.cxx.o
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkStructuredGrid.cpp.o
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkMuonScatter.cxx.o
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/uLibVtkViewer.cpp.o
|
||||
[ 63%] Generating mutomRootDict.cxx, libmutomRootDict_rdict.pcm, libmutomRootDict.rootmap
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkVoxImage.cpp.o
|
||||
[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkVoxRaytracerRepresentation.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorSkinHit.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorHit.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorMCTrack.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorInfo.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/RootMuonScatter.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorPrimaryVertex.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorMuDetDIGI.cpp.o
|
||||
[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/SkinDetectorWriter.cpp.o
|
||||
[ 93%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/mutomRootDict.cxx.o
|
||||
[ 96%] Linking CXX shared library libmutomVtk.so
|
||||
[ 96%] Built target mutomVtk
|
||||
[100%] Linking CXX shared library libmutomRoot.so
|
||||
[100%] Built target mutomRoot
|
||||
make: Leaving directory '/home/rigoni/devel/cmt/ulib/build'
|
||||
ninja: error: loading 'build.ninja': No such file or directory
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[requires]
|
||||
eigen/3.4.0
|
||||
# eigen/5.0.1
|
||||
boost/1.86.0
|
||||
# pybind11/3.0.2
|
||||
hdf5/1.14.3
|
||||
|
||||
@@ -9,7 +9,7 @@ dependencies:
|
||||
- root
|
||||
- vtk=9.4 # VTK 9.4
|
||||
- pybind11
|
||||
# - boost=1.86.0 # requested by VTK 9.4
|
||||
#- boost=1.86.0 # requested by VTK 9.4
|
||||
- ninja
|
||||
- clang
|
||||
- clangxx
|
||||
|
||||
48
docs/code/vtk/vtk_Prop3D.md
Normal file
48
docs/code/vtk/vtk_Prop3D.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Prop3D
|
||||
|
||||
`uLib::Vtk::Prop3D` is a bridge class that wraps VTK 3D representations (`vtkProp`, `vtkProp3D`) and integrates them into the `uLib` object model. It allows the framework to manage visual objects, synchronize them with underlying data models, and expose display-specific properties to the GUI.
|
||||
|
||||
## Inheritance
|
||||
`uLib::Vtk::Prop3D` : `uLib::Object`
|
||||
|
||||
## Key Functionalities
|
||||
|
||||
### VTK Integration
|
||||
The class provides access to the underlying VTK objects:
|
||||
- `GetProp()`: Returns the `vtkProp`.
|
||||
- `GetProxyProp()`: Returns the `vtkProp3D`.
|
||||
- `GetParts()` / `GetProps()`: Returns `vtkPropCollection` for composite objects.
|
||||
|
||||
### Model-View Synchronization
|
||||
`Prop3D` ensures that the visual representation stays in sync with the domain model:
|
||||
- `Update()`: Synchronizes the VTK representation based on current internal state and properties. Should be called when model data changes.
|
||||
- `SyncFromVtk()`: Updates internal state using data from the VTK representation (e.g., after user interaction via gizmos in the 3D view).
|
||||
- `GetContent()`: Returns the `uLib::Object` that this `Prop3D` represents visually.
|
||||
|
||||
### Visual Appearance
|
||||
- **Color & Opacity**: `SetColor(r, g, b)` and `SetOpacity(alpha)`.
|
||||
- **Selection**: `SetSelectable(bool)` and `SetSelected(bool)` to manage interactivity and highlighting.
|
||||
- **BBox/Scale**: `ShowBoundingBox(bool)` and `ShowScaleMeasures(bool)`.
|
||||
|
||||
### Rendering Modes
|
||||
The rendering style can be controlled via the `Representation` enum:
|
||||
- `Points`
|
||||
- `Wireframe`
|
||||
- `Surface`
|
||||
- `SurfaceWithEdges`
|
||||
- `Volume`
|
||||
- `Outline`
|
||||
- `Slice`
|
||||
|
||||
### Display Properties System
|
||||
`Prop3D` implements a system to expose specific properties (often marked as `hrp` - human readable properties) to a property editor in the GUI.
|
||||
|
||||
- `GetDisplayProperties()`: Returns the list of properties registered for display.
|
||||
- `RegisterDisplayProperty(uLib::PropertyBase*)`: Adds a property to the display list.
|
||||
- `serialize_display(...)`: A virtual method that subclasses implement to define which properties should be exposed.
|
||||
|
||||
#### Activating Display Properties
|
||||
To automatically populate the display properties list, the `ULIB_ACTIVATE_DISPLAY_PROPERTIES` macro should be called in the constructor. This triggers `serialize_display` using a `display_properties_archive`.
|
||||
|
||||
## Implementation Details
|
||||
`Prop3D` uses the Pimpl idiom (via `Prop3DData *pd`) to hide VTK-specific implementation details and reduce header dependencies.
|
||||
@@ -46,6 +46,8 @@ public:
|
||||
DataAllocator(size_t size = 0, bool owns_objects = true)
|
||||
: m_Size(size), m_RamData(nullptr), m_VramData(nullptr),
|
||||
m_Device(MemoryDevice::RAM), m_OwnsObjects(owns_objects) {
|
||||
if (m_Size >= (static_cast<size_t>(1) << 60))
|
||||
throw std::invalid_argument("DataAllocator: absurdly large size requested");
|
||||
if (m_Size > 0) {
|
||||
if (m_OwnsObjects)
|
||||
m_RamData = new T[m_Size]();
|
||||
@@ -139,7 +141,7 @@ public:
|
||||
void MoveToRAM() {
|
||||
if (m_Device == MemoryDevice::RAM)
|
||||
return;
|
||||
if (!m_RamData && m_Size > 0) {
|
||||
if (!m_RamData && m_Size > 0 && m_Size < (static_cast<size_t>(1) << 60)) {
|
||||
if (m_OwnsObjects)
|
||||
m_RamData = new T[m_Size]();
|
||||
else
|
||||
@@ -178,7 +180,7 @@ public:
|
||||
T *newRam = nullptr;
|
||||
T *newVram = nullptr;
|
||||
|
||||
if (size > 0) {
|
||||
if (size > 0 && size < (static_cast<size_t>(1) << 60)) {
|
||||
if (m_OwnsObjects)
|
||||
newRam = new T[size]();
|
||||
else
|
||||
|
||||
@@ -60,6 +60,10 @@ public:
|
||||
std::string slostr;
|
||||
};
|
||||
|
||||
~ObjectPrivate() {
|
||||
for (auto& s : sigv) delete s.signal;
|
||||
}
|
||||
|
||||
std::string m_InstanceName;
|
||||
std::vector<Signal> sigv;
|
||||
std::vector<Slot> slov;
|
||||
@@ -71,7 +75,13 @@ public:
|
||||
|
||||
// Implementations of Property methods
|
||||
void Object::RegisterDisplayProperty(PropertyBase* prop) {
|
||||
if (prop) d->m_DisplayProperties.push_back(prop);
|
||||
if (prop) {
|
||||
for (auto* existing : d->m_DisplayProperties) {
|
||||
if (existing == prop) return;
|
||||
if (existing->GetName() == prop->GetName()) return;
|
||||
}
|
||||
d->m_DisplayProperties.push_back(prop);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<PropertyBase*>& Object::GetDisplayProperties() const {
|
||||
@@ -80,6 +90,10 @@ const std::vector<PropertyBase*>& Object::GetDisplayProperties() const {
|
||||
|
||||
void Object::RegisterProperty(PropertyBase* prop) {
|
||||
if (prop) {
|
||||
for (auto* existing : d->m_Properties) {
|
||||
if (existing == prop) return;
|
||||
if (existing->GetName() == prop->GetName()) return;
|
||||
}
|
||||
d->m_Properties.push_back(prop);
|
||||
}
|
||||
}
|
||||
@@ -105,16 +119,19 @@ PropertyBase* Object::GetProperty(const std::string& name) const {
|
||||
for (auto* p : d->m_DynamicProperties) {
|
||||
if (p->GetName() == name || p->GetQualifiedName() == name) return p;
|
||||
}
|
||||
for (auto* p : d->m_DisplayProperties) {
|
||||
if (p->GetName() == name || p->GetQualifiedName() == name) return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Object::NotifyPropertiesUpdated() {
|
||||
// Only notify properties in the primary list to avoid duplicates,
|
||||
// as all registered properties should be there.
|
||||
for (auto* p : d->m_Properties) p->Updated();
|
||||
for (auto* p : d->m_DynamicProperties) p->Updated();
|
||||
}
|
||||
|
||||
void Object::Updated() { ULIB_SIGNAL_EMIT(Object::Updated); }
|
||||
void Object::PropertyUpdated() { ULIB_SIGNAL_EMIT(Object::PropertyUpdated); }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -147,6 +164,9 @@ Object::~Object() {
|
||||
for (auto* p : d->m_DynamicProperties) {
|
||||
delete p;
|
||||
}
|
||||
for (auto* p : d->m_DisplayProperties) {
|
||||
delete p;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
@@ -188,7 +208,8 @@ void Object::PrintSelf(std::ostream &o) const {
|
||||
|
||||
bool Object::addSignalImpl(SignalBase *sig, GenericMFPtr fptr,
|
||||
const char *name) {
|
||||
ObjectPrivate::Signal s = {fptr, std::string(name), sig};
|
||||
if (!d) return false;
|
||||
ObjectPrivate::Signal s = {fptr, std::string(name ? name : "unnamed"), sig};
|
||||
d->sigv.push_back(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -78,13 +78,14 @@ public:
|
||||
Object(const Object ©);
|
||||
virtual ~Object();
|
||||
|
||||
virtual const char * GetClassName() const { return type_name(); }
|
||||
virtual const char * type_name() const { return "Object"; }
|
||||
virtual const char *GetClassName() const { return type_name(); }
|
||||
virtual const char *type_name() const { return "Object"; }
|
||||
|
||||
const std::string& GetInstanceName() const;
|
||||
void SetInstanceName(const std::string& name);
|
||||
const std::string &GetInstanceName() const;
|
||||
void SetInstanceName(const std::string &name);
|
||||
|
||||
/** @brief Temporarily blocks all signal emissions from this object. Returns previous state. */
|
||||
/** @brief Temporarily blocks all signal emissions from this object. Returns
|
||||
* previous state. */
|
||||
bool blockSignals(bool block);
|
||||
|
||||
/** @brief Checks if signals are currently blocked. */
|
||||
@@ -92,14 +93,15 @@ public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// PROPERTIES //
|
||||
virtual void RegisterProperty(PropertyBase* property);
|
||||
virtual void RegisterDynamicProperty(PropertyBase* property);
|
||||
virtual void RegisterDisplayProperty(PropertyBase* property);
|
||||
virtual const std::vector<PropertyBase*>& GetProperties() const;
|
||||
virtual const std::vector<PropertyBase*>& GetDisplayProperties() const;
|
||||
PropertyBase* GetProperty(const std::string& name) const;
|
||||
virtual void RegisterProperty(PropertyBase *property);
|
||||
virtual void RegisterDynamicProperty(PropertyBase *property);
|
||||
virtual void RegisterDisplayProperty(PropertyBase *property);
|
||||
virtual const std::vector<PropertyBase *> &GetProperties() const;
|
||||
virtual const std::vector<PropertyBase *> &GetDisplayProperties() const;
|
||||
PropertyBase *GetProperty(const std::string &name) const;
|
||||
|
||||
/** @brief Sends an Updated signal for all properties of this object. useful for real-time UI refresh. */
|
||||
/** @brief Sends an Updated signal for all properties of this object. useful
|
||||
* for real-time UI refresh. */
|
||||
void NotifyPropertiesUpdated();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -109,7 +111,7 @@ public:
|
||||
virtual void DeepCopy(const Object ©);
|
||||
|
||||
/** @brief Returns a nested context for children objects, if any. */
|
||||
virtual ObjectsContext* GetChildren() { return nullptr; }
|
||||
virtual ObjectsContext *GetChildren() { return nullptr; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SERIALIZATION //
|
||||
@@ -117,13 +119,20 @@ public:
|
||||
template <class ArchiveT>
|
||||
void serialize(ArchiveT &ar, const unsigned int version);
|
||||
|
||||
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::text_oarchive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::text_iarchive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::log_archive & ar, const unsigned int version) {}
|
||||
virtual void serialize(Archive::xml_oarchive &ar,
|
||||
const unsigned int version) {}
|
||||
virtual void serialize(Archive::xml_iarchive &ar,
|
||||
const unsigned int version) {}
|
||||
virtual void serialize(Archive::text_oarchive &ar,
|
||||
const unsigned int version) {}
|
||||
virtual void serialize(Archive::text_iarchive &ar,
|
||||
const unsigned int version) {}
|
||||
virtual void serialize(Archive::hrt_oarchive &ar,
|
||||
const unsigned int version) {}
|
||||
virtual void serialize(Archive::hrt_iarchive &ar,
|
||||
const unsigned int version) {}
|
||||
virtual void serialize(Archive::log_archive &ar, const unsigned int version) {
|
||||
}
|
||||
|
||||
template <class ArchiveT>
|
||||
void save_override(ArchiveT &ar, const unsigned int version) {}
|
||||
@@ -137,9 +146,8 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SIGNALS //
|
||||
|
||||
signals:
|
||||
signals:
|
||||
virtual void Updated();
|
||||
virtual void PropertyUpdated();
|
||||
|
||||
// Qt4 style connector //
|
||||
static bool connect(const Object *ob1, const char *signal_name,
|
||||
@@ -160,14 +168,14 @@ public:
|
||||
connect(typename FunctionPointer<Func1>::Object *sender, Func1 sigf,
|
||||
typename FunctionPointer<Func2>::Object *receiver, Func2 slof) {
|
||||
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
||||
return ConnectSignal<typename FunctionPointer<Func1>::SignalSignature>(sigb, slof,
|
||||
receiver);
|
||||
return ConnectSignal<typename FunctionPointer<Func1>::SignalSignature>(
|
||||
sigb, slof, receiver);
|
||||
}
|
||||
|
||||
// Lambda/Function object connector //
|
||||
template <typename Func1, typename SlotT>
|
||||
static Connection connect(typename FunctionPointer<Func1>::Object *sender,
|
||||
Func1 sigf, SlotT slof) {
|
||||
Func1 sigf, SlotT slof) {
|
||||
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
||||
typedef typename FunctionPointer<Func1>::SignalSignature SigSignature;
|
||||
typedef typename Signal<SigSignature>::type SigT;
|
||||
@@ -183,9 +191,10 @@ public:
|
||||
}
|
||||
|
||||
template <typename FuncT>
|
||||
static inline Connection connect(SignalBase *sigb, FuncT slof, Object *receiver) {
|
||||
return ConnectSignal<typename FunctionPointer<FuncT>::SignalSignature>(sigb, slof,
|
||||
receiver);
|
||||
static inline Connection connect(SignalBase *sigb, FuncT slof,
|
||||
Object *receiver) {
|
||||
return ConnectSignal<typename FunctionPointer<FuncT>::SignalSignature>(
|
||||
sigb, slof, receiver);
|
||||
}
|
||||
|
||||
template <typename FuncT>
|
||||
|
||||
@@ -1,53 +1,55 @@
|
||||
#ifndef U_CORE_OBJECTFACTORY_H
|
||||
#define U_CORE_OBJECTFACTORY_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "Core/Object.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Singleton factory for dynamic Object instantiation based on class name.
|
||||
* @brief Singleton factory for dynamic Object instantiation based on class
|
||||
* name.
|
||||
*/
|
||||
class ObjectFactory {
|
||||
public:
|
||||
typedef std::function<Object*()> FactoryFunction;
|
||||
typedef std::function<Object *()> FactoryFunction;
|
||||
|
||||
/** @brief Get the singleton instance. */
|
||||
static ObjectFactory& Instance();
|
||||
/** @brief Get the singleton instance. */
|
||||
static ObjectFactory &Instance();
|
||||
|
||||
/** @brief Register a factory function for a given class name. */
|
||||
void Register(const std::string& className, FactoryFunction func);
|
||||
/** @brief Register a factory function for a given class name. */
|
||||
void Register(const std::string &className, FactoryFunction func);
|
||||
|
||||
/** @brief Create a new instance of the specified class. */
|
||||
Object* Create(const std::string& className);
|
||||
/** @brief Create a new instance of the specified class. */
|
||||
Object *Create(const std::string &className);
|
||||
|
||||
/** @brief Get the names of all registered classes. */
|
||||
std::vector<std::string> GetRegisteredClasses() const;
|
||||
/** @brief Get the names of all registered classes. */
|
||||
std::vector<std::string> GetRegisteredClasses() const;
|
||||
|
||||
private:
|
||||
ObjectFactory() = default;
|
||||
~ObjectFactory() = default;
|
||||
ObjectFactory() = default;
|
||||
~ObjectFactory() = default;
|
||||
|
||||
// Prevent copy and assignment
|
||||
ObjectFactory(const ObjectFactory&) = delete;
|
||||
ObjectFactory& operator=(const ObjectFactory&) = delete;
|
||||
// Prevent copy and assignment
|
||||
ObjectFactory(const ObjectFactory &) = delete;
|
||||
ObjectFactory &operator=(const ObjectFactory &) = delete;
|
||||
|
||||
std::map<std::string, FactoryFunction> m_factoryMap;
|
||||
std::map<std::string, FactoryFunction> m_factoryMap;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper class to statically register a factory function.
|
||||
*/
|
||||
template <typename T>
|
||||
class ObjectRegistrar {
|
||||
template <typename T> class ObjectRegistrar {
|
||||
public:
|
||||
ObjectRegistrar(const std::string& className) {
|
||||
ObjectFactory::Instance().Register(className, []() -> Object* { return new T(); });
|
||||
}
|
||||
ObjectRegistrar(const std::string &className) {
|
||||
ObjectFactory::Instance().Register(className,
|
||||
[]() -> Object * { return new T(); });
|
||||
}
|
||||
};
|
||||
|
||||
#define ULIB_REG_CONCAT_IMPL(a, b) a##b
|
||||
@@ -57,11 +59,63 @@ public:
|
||||
* @brief Macro to register a class to the factory.
|
||||
* Put this in the .cpp file of the class.
|
||||
*/
|
||||
#define ULIB_REGISTER_OBJECT(className) \
|
||||
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT(g_ObjectRegistrar_, __LINE__)(#className);
|
||||
#define ULIB_REGISTER_OBJECT(className) \
|
||||
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT( \
|
||||
g_ObjectRegistrar_, __LINE__)(#className);
|
||||
|
||||
#define ULIB_REGISTER_OBJECT_NAME(className, registeredName) \
|
||||
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT(g_ObjectRegistrar_, __LINE__)(registeredName);
|
||||
#define ULIB_REGISTER_OBJECT_NAME(className, registeredName) \
|
||||
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT( \
|
||||
g_ObjectRegistrar_, __LINE__)(registeredName);
|
||||
|
||||
template <typename T> class ObjectWrapper {
|
||||
public:
|
||||
ObjectWrapper(const std::string &className) {
|
||||
ObjectFactory::Instance().Register(className,
|
||||
[]() -> Object * { return new T(); });
|
||||
}
|
||||
|
||||
ObjectWrapper(T *model) : m_model(model) {}
|
||||
|
||||
template <typename U = T,
|
||||
typename = std::enable_if_t<std::is_default_constructible_v<U>>>
|
||||
ObjectWrapper() : m_model(new T()) {}
|
||||
|
||||
ObjectWrapper(const ObjectWrapper &other) : m_model(other.m_model) {}
|
||||
|
||||
ObjectWrapper &operator=(const ObjectWrapper &other) {
|
||||
m_model = other.m_model;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ObjectWrapper(ObjectWrapper &&other) noexcept
|
||||
: m_model(std::move(other.m_model)) {}
|
||||
|
||||
ObjectWrapper &operator=(ObjectWrapper &&other) noexcept {
|
||||
m_model = std::move(other.m_model);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ObjectWrapper() = default;
|
||||
|
||||
T *operator->() const { return m_model.get(); }
|
||||
|
||||
T &operator*() const { return *m_model; }
|
||||
|
||||
T *get() const { return m_model.get(); }
|
||||
|
||||
bool operator==(const ObjectWrapper &other) const {
|
||||
return m_model == other.m_model;
|
||||
}
|
||||
|
||||
bool operator!=(const ObjectWrapper &other) const {
|
||||
return m_model != other.m_model;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return m_model != nullptr; }
|
||||
|
||||
protected:
|
||||
SmartPointer<T> m_model;
|
||||
};
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ ObjectsContext::ObjectsContext() : Object() {}
|
||||
ObjectsContext::~ObjectsContext() {}
|
||||
|
||||
void ObjectsContext::AddObject(Object* obj) {
|
||||
if (obj && std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) {
|
||||
if (!obj || obj == this) return;
|
||||
|
||||
if (std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) {
|
||||
m_objects.push_back(obj);
|
||||
// Connect child's update to context's update to trigger re-renders
|
||||
Object::connect(obj, &Object::Updated, this, &Object::Updated);
|
||||
|
||||
@@ -53,9 +53,6 @@ public:
|
||||
return GetGroup() + "." + GetName();
|
||||
}
|
||||
|
||||
// Signal support
|
||||
signals:
|
||||
virtual void Updated() override { ULIB_SIGNAL_EMIT(PropertyBase::Updated); }
|
||||
|
||||
// Serialization support for different uLib archives
|
||||
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) override = 0;
|
||||
@@ -106,6 +103,7 @@ public:
|
||||
// Accessors
|
||||
const T& Get() const { return *m_value; }
|
||||
void Set(const T& value) {
|
||||
if (!m_value) return;
|
||||
T val = value;
|
||||
if constexpr (std::is_arithmetic<T>::value) {
|
||||
if (m_HasRange) { if (val < m_Min) val = m_Min; if (val > m_Max) val = m_Max; }
|
||||
@@ -169,7 +167,6 @@ public:
|
||||
|
||||
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) override;
|
||||
|
||||
virtual void Updated() override { PropertyBase::Updated(); this->PropertyChanged(); }
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
@@ -263,7 +260,7 @@ public:
|
||||
if (m_DisplayOnly) {
|
||||
m_Object->RegisterDisplayProperty(newP);
|
||||
Object* obj = m_Object;
|
||||
Object::connect(newP, &PropertyBase::Updated, [obj]() { obj->Updated(); });
|
||||
Object::connect(newP, &Object::Updated, [obj]() { obj->Updated(); });
|
||||
} else {
|
||||
m_Object->RegisterDynamicProperty(newP);
|
||||
}
|
||||
@@ -277,7 +274,7 @@ public:
|
||||
if (m_DisplayOnly) {
|
||||
m_Object->RegisterDisplayProperty(p);
|
||||
Object* obj = m_Object;
|
||||
Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); });
|
||||
Object::connect(p, &Object::Updated, [obj]() { obj->Updated(); });
|
||||
} else {
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
}
|
||||
@@ -300,7 +297,7 @@ public:
|
||||
if (m_Object) {
|
||||
EnumProperty* p = new EnumProperty(m_Object, t.name(), (int*)&const_cast<boost::serialization::hrp_enum<T>&>(t).value(), t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
p->SetReadOnly(t.is_read_only());
|
||||
if (m_DisplayOnly) { m_Object->RegisterDisplayProperty(p); Object* obj = m_Object; Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); }); }
|
||||
if (m_DisplayOnly) { m_Object->RegisterDisplayProperty(p); Object* obj = m_Object; Object::connect(p, &Object::Updated, [obj]() { obj->Updated(); }); }
|
||||
else { m_Object->RegisterDynamicProperty(p); }
|
||||
}
|
||||
}
|
||||
@@ -308,7 +305,7 @@ public:
|
||||
if (m_Object) {
|
||||
EnumProperty* p = new EnumProperty(m_Object, t.name(), (int*)&const_cast<boost::serialization::hrp_enum_val<T>&>(t).value(), t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
p->SetReadOnly(t.is_read_only());
|
||||
if (m_DisplayOnly) { m_Object->RegisterDisplayProperty(p); Object* obj = m_Object; Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); }); }
|
||||
if (m_DisplayOnly) { m_Object->RegisterDisplayProperty(p); Object* obj = m_Object; Object::connect(p, &Object::Updated, [obj]() { obj->Updated(); }); }
|
||||
else { m_Object->RegisterDynamicProperty(p); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,89 +23,249 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
|
||||
#ifndef U_CORE_SMARTPOINTER_H
|
||||
#define U_CORE_SMARTPOINTER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief A smart pointer implementation inspired by std::shared_ptr.
|
||||
*
|
||||
* Features modernized C++11/14/17 syntax, thread-safe reference counting,
|
||||
* move semantics, and support for custom deleters.
|
||||
*
|
||||
* NOTE: Default constructor allocates a new T following legacy behavior.
|
||||
*/
|
||||
template <typename T>
|
||||
class SmartPointer {
|
||||
typedef T element_type;
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
explicit
|
||||
SmartPointer(T* ptr = NULL) : m_counter(0) {
|
||||
if(!ptr) ptr = new T;
|
||||
/**
|
||||
* @brief Constructor from raw pointer.
|
||||
* If ptr is nullptr, a new T is allocated (legacy behavior).
|
||||
*/
|
||||
explicit SmartPointer(T* ptr = nullptr) : m_counter(nullptr) {
|
||||
if (!ptr) {
|
||||
if constexpr (std::is_default_constructible_v<T>) {
|
||||
ptr = new T();
|
||||
}
|
||||
}
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr);
|
||||
}
|
||||
|
||||
// TakeReference //
|
||||
SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref,0)) { }
|
||||
|
||||
SmartPointer(const SmartPointer& copy) throw () {
|
||||
acquire(copy.m_counter);
|
||||
/**
|
||||
* @brief Constructor with custom deleter.
|
||||
*/
|
||||
template <typename D>
|
||||
SmartPointer(T* ptr, D deleter) : m_counter(nullptr) {
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr, deleter);
|
||||
}
|
||||
|
||||
SmartPointer(SmartPointer * copy) throw () {
|
||||
acquire(copy->m_counter);
|
||||
/**
|
||||
* @brief Non-owning constructor from reference.
|
||||
* Uses a no-op deleter to ensure the referenced object is not destroyed.
|
||||
*/
|
||||
SmartPointer(T &ref) : m_counter(new ReferenceCounter(&ref, [](T*){}, 1)) { }
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
SmartPointer(const SmartPointer& other) noexcept : m_counter(nullptr) {
|
||||
acquire(other.m_counter);
|
||||
}
|
||||
|
||||
virtual ~SmartPointer() { release(); }
|
||||
/**
|
||||
* @brief Copy constructor from a pointer to SmartPointer (Legacy support).
|
||||
*/
|
||||
SmartPointer(const SmartPointer* other) noexcept : m_counter(nullptr) {
|
||||
if (other) acquire(other->m_counter);
|
||||
}
|
||||
|
||||
SmartPointer & operator=(const SmartPointer& copy) {
|
||||
if (this != ©)
|
||||
{
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*/
|
||||
SmartPointer(SmartPointer&& other) noexcept : m_counter(other.m_counter) {
|
||||
other.m_counter = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor.
|
||||
*/
|
||||
virtual ~SmartPointer() { release(); }
|
||||
|
||||
/**
|
||||
* @brief Copy assignment.
|
||||
*/
|
||||
SmartPointer& operator=(const SmartPointer& other) noexcept {
|
||||
if (this != &other) {
|
||||
release();
|
||||
acquire(copy.m_counter);
|
||||
acquire(other.m_counter);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
T & operator*() const throw () { return *m_counter->ptr; }
|
||||
T * operator->() const throw () { return m_counter->ptr; }
|
||||
|
||||
T * get() const throw () {
|
||||
return m_counter ? m_counter->ptr : 0; }
|
||||
bool unique() const throw () {
|
||||
return (m_counter ? m_counter->count == 1 : true); }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
struct ReferenceCounter
|
||||
{
|
||||
ReferenceCounter(T* ptr = 0, unsigned c = 1) : ptr(ptr), count(c) { }
|
||||
T* ptr;
|
||||
unsigned count;
|
||||
} * m_counter;
|
||||
|
||||
// increment the count
|
||||
void acquire(ReferenceCounter* c) throw ()
|
||||
{
|
||||
m_counter = c;
|
||||
if (c && c->count>0) ++c->count;
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
*/
|
||||
SmartPointer& operator=(SmartPointer&& other) noexcept {
|
||||
if (this != &other) {
|
||||
release();
|
||||
m_counter = other.m_counter;
|
||||
other.m_counter = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// decrement the count, delete if it is 0
|
||||
void release() {
|
||||
if (m_counter) {
|
||||
if (--m_counter->count == 0) {
|
||||
delete m_counter->ptr;
|
||||
}
|
||||
if (m_counter->count <= 0) {
|
||||
delete m_counter;
|
||||
m_counter = NULL;
|
||||
}
|
||||
/**
|
||||
* @brief Resets the smart pointer to hold a new raw pointer.
|
||||
*/
|
||||
void reset(T* ptr = nullptr) {
|
||||
release();
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the smart pointer with a custom deleter.
|
||||
*/
|
||||
template <typename D>
|
||||
void reset(T* ptr, D deleter) {
|
||||
release();
|
||||
if (ptr) m_counter = new ReferenceCounter(ptr, deleter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps contents with another SmartPointer.
|
||||
*/
|
||||
void swap(SmartPointer& other) noexcept {
|
||||
std::swap(m_counter, other.m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dereference operator.
|
||||
*/
|
||||
T& operator*() const noexcept { return *m_counter->ptr; }
|
||||
|
||||
/**
|
||||
* @brief Member access operator.
|
||||
*/
|
||||
T* operator->() const noexcept { return m_counter->ptr; }
|
||||
|
||||
/**
|
||||
* @brief Returns the raw pointer.
|
||||
*/
|
||||
T* get() const noexcept { return m_counter ? m_counter->ptr : nullptr; }
|
||||
|
||||
/**
|
||||
* @brief Implicit conversion to raw pointer (legacy compatibility).
|
||||
*/
|
||||
operator T*() const noexcept { return get(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the number of SmartPointers sharing ownership.
|
||||
*/
|
||||
uint32_t use_count() const noexcept {
|
||||
return m_counter ? m_counter->count.load(std::memory_order_relaxed) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if this is the only SmartPointer owning the resource.
|
||||
*/
|
||||
bool unique() const noexcept { return use_count() == 1; }
|
||||
|
||||
/**
|
||||
* @brief Boolean conversion operator.
|
||||
*/
|
||||
explicit operator bool() const noexcept { return get() != nullptr; }
|
||||
|
||||
private:
|
||||
struct ReferenceCounter {
|
||||
T* ptr;
|
||||
std::atomic<uint32_t> count;
|
||||
std::function<void(T*)> deleter;
|
||||
|
||||
ReferenceCounter(T* p, uint32_t initial_count = 1)
|
||||
: ptr(p), count(initial_count), deleter([](T* ptr_to_del) { delete ptr_to_del; }) {}
|
||||
|
||||
template <typename D>
|
||||
ReferenceCounter(T* p, D d, uint32_t initial_count = 1)
|
||||
: ptr(p), count(initial_count), deleter(d) {}
|
||||
};
|
||||
|
||||
ReferenceCounter* m_counter;
|
||||
|
||||
void acquire(ReferenceCounter* c) noexcept {
|
||||
m_counter = c;
|
||||
if (c) {
|
||||
c->count.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void release() noexcept {
|
||||
if (m_counter) {
|
||||
if (m_counter->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
if (m_counter->ptr) {
|
||||
m_counter->deleter(m_counter->ptr);
|
||||
}
|
||||
delete m_counter;
|
||||
}
|
||||
m_counter = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Global swap for SmartPointer.
|
||||
*/
|
||||
template <typename T>
|
||||
void swap(SmartPointer<T>& a, SmartPointer<T>& b) noexcept {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
#endif // SMARTPOINTER_H
|
||||
/**
|
||||
* @brief Equality comparison.
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
bool operator==(const SmartPointer<T>& a, const SmartPointer<U>& b) noexcept {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality comparison.
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const SmartPointer<T>& a, const SmartPointer<U>& b) noexcept {
|
||||
return a.get() != b.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Comparison with nullptr.
|
||||
*/
|
||||
template <typename T>
|
||||
bool operator==(const SmartPointer<T>& a, std::nullptr_t) noexcept {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(std::nullptr_t, const SmartPointer<T>& a) noexcept {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const SmartPointer<T>& a, std::nullptr_t) noexcept {
|
||||
return a.get() != nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(std::nullptr_t, const SmartPointer<T>& a) noexcept {
|
||||
return a.get() != nullptr;
|
||||
}
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_SMARTPOINTER_H
|
||||
|
||||
@@ -139,7 +139,6 @@ typedef id_t Id_t;
|
||||
typedef void *Pointer_t;
|
||||
typedef bool Bool_t; // Boolean (0=false, 1=true) (bool)
|
||||
|
||||
|
||||
//--- bit manipulation ---------------------------------------------------------
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1ULL << (n))
|
||||
@@ -182,7 +181,7 @@ typedef bool Bool_t; // Boolean (0=false, 1=true) (bool)
|
||||
\
|
||||
public: \
|
||||
typedef type_info::BaseClass BaseClass; \
|
||||
virtual const char *type_name() const override { return type_info::name; } \
|
||||
virtual const char *type_name() const override { return type_info::name; } \
|
||||
/**/
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# TESTS
|
||||
set( TESTS
|
||||
SmartVectorTest
|
||||
SmartPointerTest
|
||||
ObjectWrapperTest
|
||||
VectorTest
|
||||
ObjectFlagsTest
|
||||
ObjectParametersTest
|
||||
|
||||
28
src/Core/testing/ObjectWrapperTest.cpp
Normal file
28
src/Core/testing/ObjectWrapperTest.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include <iostream>
|
||||
|
||||
struct NonDefault {
|
||||
NonDefault(int) {}
|
||||
};
|
||||
|
||||
struct Default {
|
||||
Default() : value(42) {}
|
||||
int value;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "Testing ObjectWrapper with Default Constructible type..." << std::endl;
|
||||
uLib::ObjectWrapper<Default> w1;
|
||||
|
||||
std::cout << "Testing ObjectWrapper with Non-Default Constructible type..." << std::endl;
|
||||
NonDefault nd(10);
|
||||
uLib::ObjectWrapper<NonDefault> w2(&nd);
|
||||
|
||||
// The following would NOT compile without SFINAE:
|
||||
// uLib::ObjectWrapper<NonDefault> w3;
|
||||
|
||||
std::cout << "Tests passed (compilation and manual instantiation)!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,27 +37,18 @@ Assembly::Assembly(const Assembly ©)
|
||||
m_GroupSelection(copy.m_GroupSelection) {}
|
||||
|
||||
Assembly::~Assembly() {
|
||||
for (auto const& [obj, conn] : m_ChildConnections) {
|
||||
conn.disconnect();
|
||||
}
|
||||
m_ChildConnections.clear();
|
||||
}
|
||||
|
||||
void Assembly::AddObject(Object *obj) {
|
||||
if (!obj || obj == this) return;
|
||||
|
||||
if (auto *at = dynamic_cast<AffineTransform *>(obj)) {
|
||||
at->SetParent(this);
|
||||
}
|
||||
|
||||
// Base class already handles the list and child->parent update connection
|
||||
ObjectsContext::AddObject(obj);
|
||||
|
||||
// Connect to child updates to recompute AABB
|
||||
m_ChildConnections[obj] = Object::connect(obj, &Object::Updated, [this](){
|
||||
this->ComputeBoundingBox();
|
||||
this->Updated(); // Signal that assembly itself changed (AABB-wise)
|
||||
});
|
||||
|
||||
// Parent -> Child propagation for world matrix updates
|
||||
Object::connect(this, &Object::Updated, obj, &Object::Updated);
|
||||
|
||||
this->ComputeBoundingBox();
|
||||
}
|
||||
|
||||
@@ -67,12 +58,6 @@ void Assembly::RemoveObject(Object *obj) {
|
||||
at->SetParent(nullptr);
|
||||
}
|
||||
|
||||
auto itConn = m_ChildConnections.find(obj);
|
||||
if (itConn != m_ChildConnections.end()) {
|
||||
itConn->second.disconnect();
|
||||
m_ChildConnections.erase(itConn);
|
||||
}
|
||||
|
||||
ObjectsContext::RemoveObject(obj);
|
||||
this->ComputeBoundingBox();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "Math/Transform.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk { class Assembly; }
|
||||
|
||||
/**
|
||||
* @brief Assembly groups geometric objects (ContainerBox, Cylinder, etc.)
|
||||
@@ -46,6 +47,7 @@ namespace uLib {
|
||||
class Assembly : public ObjectsContext, public TRS {
|
||||
public:
|
||||
uLibTypeMacro(Assembly, ObjectsContext, TRS)
|
||||
friend class Vtk::Assembly;
|
||||
|
||||
|
||||
Assembly();
|
||||
@@ -112,7 +114,6 @@ private:
|
||||
bool m_ShowBoundingBox;
|
||||
bool m_GroupSelection;
|
||||
bool m_InUpdated = false;
|
||||
std::map<Object*, Connection> m_ChildConnections;
|
||||
|
||||
ULIB_DECLARE_PROPERTIES(Assembly)
|
||||
};
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
// #include <Eigen/src/Core/Matrix.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include "Core/Types.h"
|
||||
#include "Core/Property.h"
|
||||
#include "Core/Types.h"
|
||||
#include <Eigen/Dense>
|
||||
|
||||
//// BOOST SERIALIZATION ///////////////////////////////////////////////////////
|
||||
|
||||
@@ -150,7 +150,6 @@ typedef Eigen::MatrixXi MatrixXi;
|
||||
typedef Eigen::MatrixXf MatrixXf;
|
||||
typedef Eigen::MatrixXd MatrixXd;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vector String interaction ///////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -192,7 +191,7 @@ std::string VectorxT_ToString(const Eigen::Matrix<T, size, 1> &vec) {
|
||||
// }
|
||||
|
||||
template <typename T, int size>
|
||||
void operator >> (std::string &str, Eigen::Matrix<T, size, 1> &vec) {
|
||||
void operator>>(std::string &str, Eigen::Matrix<T, size, 1> &vec) {
|
||||
VectorxT_StringTo(vec, str);
|
||||
}
|
||||
|
||||
@@ -205,9 +204,7 @@ public:
|
||||
typedef Eigen::Matrix<Scalarf, 4, 1> BaseClass;
|
||||
|
||||
_HPoint3f() : BaseClass(0, 0, 0, p) {}
|
||||
_HPoint3f(int rows, int cols) : BaseClass() {
|
||||
this->operator()(3) = p;
|
||||
}
|
||||
_HPoint3f(int rows, int cols) : BaseClass() { this->operator()(3) = p; }
|
||||
_HPoint3f(float x, float y, float z) : BaseClass(x, y, z, p) {}
|
||||
_HPoint3f(Vector3f &in) : BaseClass(in.homogeneous()) {
|
||||
this->operator()(3) = p;
|
||||
@@ -250,24 +247,24 @@ struct _HError3f {
|
||||
HVector3f position_error;
|
||||
HVector3f direction_error;
|
||||
};
|
||||
typedef struct _HError3f HError3f;
|
||||
typedef struct _HError3f HError3f;
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const HError3f &err) {
|
||||
stream << "HError3f(" << "ept[" << err.position_error.transpose()
|
||||
<< "] , edr[" << err.direction_error.transpose() << "]) ";
|
||||
return stream;
|
||||
}
|
||||
inline std::ostream &operator<<(std::ostream &stream, const HError3f &err) {
|
||||
stream << "HError3f(" << "ept[" << err.position_error.transpose()
|
||||
<< "] , edr[" << err.direction_error.transpose() << "]) ";
|
||||
return stream;
|
||||
}
|
||||
|
||||
typedef Property<Scalari> ScalariProperty;
|
||||
typedef Property<Scalarui> ScalaruiProperty;
|
||||
typedef Property<Scalarl> ScalarlProperty;
|
||||
typedef Property<Scalarul> ScalarulProperty;
|
||||
typedef Property<Scalarf> ScalarfProperty;
|
||||
typedef Property<Scalard> ScalardProperty;
|
||||
typedef Property<Scalari> ScalariProperty;
|
||||
typedef Property<Scalarui> ScalaruiProperty;
|
||||
typedef Property<Scalarl> ScalarlProperty;
|
||||
typedef Property<Scalarul> ScalarulProperty;
|
||||
typedef Property<Scalarf> ScalarfProperty;
|
||||
typedef Property<Scalard> ScalardProperty;
|
||||
|
||||
typedef Property<Vector1i> Vector1iProperty;
|
||||
typedef Property<Vector1f> Vector1fProperty;
|
||||
typedef Property<Vector1d> Vector1dProperty;
|
||||
typedef Property<Vector1i> Vector1iProperty;
|
||||
typedef Property<Vector1f> Vector1fProperty;
|
||||
typedef Property<Vector1d> Vector1dProperty;
|
||||
|
||||
typedef Property<Vector2i> Vector2iProperty;
|
||||
typedef Property<Vector3i> Vector3iProperty;
|
||||
@@ -294,9 +291,9 @@ typedef Property<Matrix3d> Matrix3dProperty;
|
||||
typedef Property<Matrix4d> Matrix4dProperty;
|
||||
|
||||
typedef Property<HVector3f> HVector3fProperty;
|
||||
typedef Property<HPoint3f> HPoint3fProperty;
|
||||
typedef Property<HPoint3f> HPoint3fProperty;
|
||||
|
||||
} // namespace uLib
|
||||
} // namespace uLib
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/Assembly.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Cylinder.h"
|
||||
#include "Math/Geometry.h"
|
||||
#include "Math/TriangleMesh.h"
|
||||
#include "Math/QuadMesh.h"
|
||||
#include "Math/VoxImage.h"
|
||||
#include "Math/Assembly.h"
|
||||
#include "Math/StructuredData.h"
|
||||
#include "Math/TriangleMesh.h"
|
||||
#include "Math/VoxImage.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
@@ -14,8 +14,6 @@ ULIB_REGISTER_OBJECT(TRS)
|
||||
ULIB_REGISTER_OBJECT(ContainerBox)
|
||||
ULIB_REGISTER_OBJECT(Cylinder)
|
||||
ULIB_REGISTER_OBJECT(Assembly)
|
||||
ULIB_REGISTER_OBJECT(CylindricalGeometry)
|
||||
ULIB_REGISTER_OBJECT(SphericalGeometry)
|
||||
ULIB_REGISTER_OBJECT(TriangleMesh)
|
||||
ULIB_REGISTER_OBJECT(QuadMesh)
|
||||
ULIB_REGISTER_OBJECT_NAME(VoxImage<Voxel>, "VoxImage")
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 Andrea Rigoni Garola <andrea.rigoni@pd.infn.it>
|
||||
*
|
||||
@@ -45,142 +43,120 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef U_TRANSFORM_H
|
||||
#define U_TRANSFORM_H
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
#include "Math/Units.h"
|
||||
#include "Math/Dense.h"
|
||||
|
||||
#include "Math/Units.h"
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
|
||||
|
||||
using Eigen::Isometry3f;
|
||||
using Eigen::Isometry3d;
|
||||
using Eigen::Isometry3f;
|
||||
|
||||
using Eigen::Affine3f;
|
||||
using Eigen::Affine3d;
|
||||
using Eigen::Affine3f;
|
||||
|
||||
using Eigen::Projective3f;
|
||||
using Eigen::Projective3d;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using Eigen::Projective3f;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////// AFFINE TRANSFORM WRAPPER //////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
class AffineTransform : virtual public Object {
|
||||
public:
|
||||
uLibTypeMacro(AffineTransform, Object)
|
||||
protected:
|
||||
uLibTypeMacro(AffineTransform, Object) protected :
|
||||
|
||||
Affine3f m_T;
|
||||
AffineTransform *m_Parent;
|
||||
Affine3f m_T;
|
||||
AffineTransform *m_Parent;
|
||||
|
||||
public:
|
||||
AffineTransform() :
|
||||
m_T(Matrix4f::Identity()),
|
||||
m_Parent(NULL)
|
||||
{}
|
||||
AffineTransform() : m_T(Matrix4f::Identity()), m_Parent(NULL) {}
|
||||
|
||||
AffineTransform(AffineTransform *parent) :
|
||||
m_T(Matrix4f::Identity()),
|
||||
m_Parent(parent)
|
||||
{}
|
||||
AffineTransform(AffineTransform *parent)
|
||||
: m_T(Matrix4f::Identity()), m_Parent(parent) {}
|
||||
|
||||
AffineTransform(const AffineTransform ©) :
|
||||
m_T(copy.m_T),
|
||||
m_Parent(copy.m_Parent)
|
||||
{}
|
||||
AffineTransform(const AffineTransform ©)
|
||||
: m_T(copy.m_T), m_Parent(copy.m_Parent) {}
|
||||
|
||||
Affine3f& GetTransform() { return m_T; }
|
||||
Affine3f &GetTransform() { return m_T; }
|
||||
|
||||
AffineTransform *GetParent() const { return this->m_Parent; }
|
||||
AffineTransform *GetParent() const { return this->m_Parent; }
|
||||
|
||||
void SetParent(AffineTransform *name) { this->m_Parent = name; }
|
||||
void SetParent(AffineTransform *name) { this->m_Parent = name; }
|
||||
|
||||
void SetMatrix (const Matrix4f &mat) { m_T.matrix() = mat; }
|
||||
Matrix4f& GetMatrix () { return m_T.matrix(); }
|
||||
const Matrix4f& GetMatrix () const { return m_T.matrix(); }
|
||||
void SetMatrix(const Matrix4f &mat) { m_T.matrix() = mat; }
|
||||
Matrix4f &GetMatrix() { return m_T.matrix(); }
|
||||
const Matrix4f &GetMatrix() const { return m_T.matrix(); }
|
||||
|
||||
Matrix4f GetWorldMatrix() const
|
||||
{
|
||||
if(!m_Parent) return m_T.matrix();
|
||||
else return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A //
|
||||
}
|
||||
Matrix4f GetWorldMatrix() const {
|
||||
if (!m_Parent)
|
||||
return m_T.matrix();
|
||||
else
|
||||
return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A //
|
||||
}
|
||||
|
||||
void SetWorldMatrix(const Matrix4f &mat)
|
||||
{
|
||||
if(!m_Parent) m_T.matrix() = mat;
|
||||
else m_T.matrix() = m_Parent->GetWorldMatrix().inverse() * mat;
|
||||
}
|
||||
void SetWorldMatrix(const Matrix4f &mat) {
|
||||
if (!m_Parent)
|
||||
m_T.matrix() = mat;
|
||||
else
|
||||
m_T.matrix() = m_Parent->GetWorldMatrix().inverse() * mat;
|
||||
}
|
||||
|
||||
void SetPosition(const Vector3f &v) { this->m_T.translation() = v; }
|
||||
void SetPosition(const Vector3f &v) { this->m_T.translation() = v; }
|
||||
|
||||
Vector3f GetPosition() const { return this->m_T.translation(); }
|
||||
Vector3f GetPosition() const { return this->m_T.translation(); }
|
||||
|
||||
void SetRotation(const Matrix3f &m) { this->m_T.linear() = m; }
|
||||
void SetRotation(const Matrix3f &m) { this->m_T.linear() = m; }
|
||||
|
||||
Matrix3f GetRotation() const { return this->m_T.rotation(); }
|
||||
Matrix3f GetRotation() const { return this->m_T.rotation(); }
|
||||
|
||||
void Translate(const Vector3f &v) { this->m_T.translate(v); }
|
||||
void Translate(const Vector3f &v) { this->m_T.translate(v); }
|
||||
|
||||
void Scale(const Vector3f &v) { this->m_T.scale(v); }
|
||||
void Scale(const Vector3f &v) { this->m_T.scale(v); }
|
||||
|
||||
Vector3f GetScale() const {
|
||||
return Vector3f(this->m_T.linear().col(0).norm(),
|
||||
this->m_T.linear().col(1).norm(),
|
||||
this->m_T.linear().col(2).norm());
|
||||
}
|
||||
Vector3f GetScale() const {
|
||||
return Vector3f(this->m_T.linear().col(0).norm(),
|
||||
this->m_T.linear().col(1).norm(),
|
||||
this->m_T.linear().col(2).norm());
|
||||
}
|
||||
|
||||
void Rotate(const Matrix3f &m) { this->m_T.rotate(m); }
|
||||
|
||||
void Rotate(const Matrix3f &m) { this->m_T.rotate(m); }
|
||||
void Rotate(const float angle, Vector3f axis) {
|
||||
axis.normalize(); // prehaps not necessary ( see eigens )
|
||||
Eigen::AngleAxisf ax(angle, axis);
|
||||
this->m_T.rotate(Eigen::Quaternion<float>(ax));
|
||||
}
|
||||
|
||||
void Rotate(const float angle, Vector3f axis)
|
||||
{
|
||||
axis.normalize(); // prehaps not necessary ( see eigens )
|
||||
Eigen::AngleAxisf ax(angle,axis);
|
||||
this->m_T.rotate(Eigen::Quaternion<float>(ax));
|
||||
}
|
||||
void Rotate(const Vector3f euler_axis) {
|
||||
float angle = euler_axis.norm();
|
||||
Rotate(angle, euler_axis);
|
||||
}
|
||||
|
||||
void Rotate(const Vector3f euler_axis) {
|
||||
float angle = euler_axis.norm();
|
||||
Rotate(angle,euler_axis);
|
||||
}
|
||||
void PreRotate(const Matrix3f &m) { this->m_T.prerotate(m); }
|
||||
|
||||
void PreRotate(const Matrix3f &m) { this->m_T.prerotate(m); }
|
||||
void QuaternionRotate(const Vector4f &q) {
|
||||
this->m_T.rotate(Eigen::Quaternion<float>(q));
|
||||
}
|
||||
|
||||
void QuaternionRotate(const Vector4f &q)
|
||||
{ this->m_T.rotate(Eigen::Quaternion<float>(q)); }
|
||||
void EulerYZYRotate(const Vector3f &e) {
|
||||
Matrix3f mat;
|
||||
mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY()) *
|
||||
Eigen::AngleAxisf(e.y(), Vector3f::UnitZ()) *
|
||||
Eigen::AngleAxisf(e.z(), Vector3f::UnitY());
|
||||
m_T.rotate(mat);
|
||||
}
|
||||
|
||||
void EulerYZYRotate(const Vector3f &e) {
|
||||
Matrix3f mat;
|
||||
mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY())
|
||||
* Eigen::AngleAxisf(e.y(), Vector3f::UnitZ())
|
||||
* Eigen::AngleAxisf(e.z(), Vector3f::UnitY());
|
||||
m_T.rotate(mat);
|
||||
}
|
||||
|
||||
void FlipAxes(int first, int second)
|
||||
{
|
||||
Matrix3f mat = Matrix3f::Identity();
|
||||
mat.col(first).swap(mat.col(second));
|
||||
m_T.rotate(mat);
|
||||
}
|
||||
void FlipAxes(int first, int second) {
|
||||
Matrix3f mat = Matrix3f::Identity();
|
||||
mat.col(first).swap(mat.col(second));
|
||||
m_T.rotate(mat);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////// TRS PARAMETERS /////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -189,116 +165,99 @@ typedef Eigen::Affine3f AffineMatrix;
|
||||
|
||||
class TRS : public AffineTransform {
|
||||
|
||||
uLibTypeMacro(TRS, AffineTransform)
|
||||
ULIB_SERIALIZE_ACCESS
|
||||
uLibTypeMacro(TRS, AffineTransform) ULIB_SERIALIZE_ACCESS
|
||||
// ULIB_DECLARE_PROPERTIES(TRS)
|
||||
|
||||
public:
|
||||
public :
|
||||
|
||||
Vector3f position = Vector3f::Zero();
|
||||
Vector3f rotation = Vector3f::Zero();
|
||||
Vector3f scaling = Vector3f::Ones();
|
||||
Vector3f rotation = Vector3f::Zero();
|
||||
Vector3f scaling = Vector3f::Ones();
|
||||
|
||||
TRS() = default;
|
||||
TRS() = default;
|
||||
|
||||
TRS(const class AffineTransform& at) {
|
||||
this->FromMatrix(at.GetMatrix());
|
||||
}
|
||||
TRS(const class AffineTransform &at) { this->FromMatrix(at.GetMatrix()); }
|
||||
|
||||
TRS(const Matrix4f& mat) {
|
||||
this->FromMatrix(mat);
|
||||
}
|
||||
TRS(const Matrix4f &mat) { this->FromMatrix(mat); }
|
||||
|
||||
void FromMatrix(const Matrix4f& mat) {
|
||||
this->position = mat.block<3,1>(0,3);
|
||||
void FromMatrix(const Matrix4f &mat) {
|
||||
this->position = mat.block<3, 1>(0, 3);
|
||||
|
||||
Matrix3f linear = mat.block<3,3>(0,0);
|
||||
this->scaling(0) = linear.col(0).norm();
|
||||
this->scaling(1) = linear.col(1).norm();
|
||||
this->scaling(2) = linear.col(2).norm();
|
||||
Matrix3f linear = mat.block<3, 3>(0, 0);
|
||||
this->scaling(0) = linear.col(0).norm();
|
||||
this->scaling(1) = linear.col(1).norm();
|
||||
this->scaling(2) = linear.col(2).norm();
|
||||
|
||||
Matrix3f rot = linear;
|
||||
if (this->scaling(0) > 1e-6) rot.col(0) /= this->scaling(0);
|
||||
if (this->scaling(1) > 1e-6) rot.col(1) /= this->scaling(1);
|
||||
if (this->scaling(2) > 1e-6) rot.col(2) /= this->scaling(2);
|
||||
Matrix3f rot = linear;
|
||||
if (this->scaling(0) > 1e-6)
|
||||
rot.col(0) /= this->scaling(0);
|
||||
if (this->scaling(1) > 1e-6)
|
||||
rot.col(1) /= this->scaling(1);
|
||||
if (this->scaling(2) > 1e-6)
|
||||
rot.col(2) /= this->scaling(2);
|
||||
|
||||
Vector3f euler = rot.eulerAngles(2, 1, 0);
|
||||
this->rotation = Vector3f(euler(2), euler(1), euler(0));
|
||||
Vector3f euler = rot.canonicalEulerAngles(2, 1, 0);
|
||||
this->rotation = Vector3f(euler(2), euler(1), euler(0));
|
||||
|
||||
this->SetMatrix(mat);
|
||||
this->NotifyPropertiesUpdated();
|
||||
}
|
||||
this->SetMatrix(mat);
|
||||
this->NotifyPropertiesUpdated();
|
||||
}
|
||||
|
||||
void SetPosition(const Vector3f &v) {
|
||||
position = v;
|
||||
this->AffineTransform::SetPosition(v);
|
||||
}
|
||||
void SetPosition(const Vector3f &v) {
|
||||
position = v;
|
||||
this->AffineTransform::SetPosition(v);
|
||||
}
|
||||
|
||||
void SetRotation(const Vector3f &v) {
|
||||
rotation = v;
|
||||
this->SyncMatrix();
|
||||
}
|
||||
void SetRotation(const Vector3f &v) {
|
||||
rotation = v;
|
||||
this->SyncMatrix();
|
||||
}
|
||||
|
||||
void SetOrientation(const Vector3f &v) { SetRotation(v); }
|
||||
void SetOrientation(const Vector3f &v) { SetRotation(v); }
|
||||
|
||||
void SetScale(const Vector3f &v) {
|
||||
scaling = v;
|
||||
this->SyncMatrix();
|
||||
}
|
||||
void SetScale(const Vector3f &v) {
|
||||
scaling = v;
|
||||
this->SyncMatrix();
|
||||
}
|
||||
|
||||
void SyncMatrix() {
|
||||
this->GetTransform() = GetAffineMatrix();
|
||||
}
|
||||
|
||||
void Updated() override {
|
||||
this->SyncMatrix();
|
||||
this->NotifyPropertiesUpdated();
|
||||
this->AffineTransform::Updated();
|
||||
}
|
||||
|
||||
template <class ArchiveT>
|
||||
void serialize(ArchiveT & ar, const unsigned int version) {
|
||||
ar & HRPU(position, "mm");
|
||||
ar & HRPU(rotation, "rad");
|
||||
ar & HRP(scaling);
|
||||
}
|
||||
|
||||
|
||||
AffineMatrix GetAffineMatrix() const {
|
||||
AffineMatrix m = AffineMatrix::Identity();
|
||||
m.translate(position);
|
||||
m.rotate(Eigen::AngleAxisf(rotation.z(), Vector3f::UnitZ()));
|
||||
m.rotate(Eigen::AngleAxisf(rotation.y(), Vector3f::UnitY()));
|
||||
m.rotate(Eigen::AngleAxisf(rotation.x(), Vector3f::UnitX()));
|
||||
m.scale(scaling);
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix4f GetMatrix() const {
|
||||
return this->GetAffineMatrix().matrix();
|
||||
}
|
||||
void SyncMatrix() { this->GetTransform() = GetAffineMatrix(); }
|
||||
|
||||
void Updated() override {
|
||||
this->SyncMatrix();
|
||||
this->NotifyPropertiesUpdated();
|
||||
this->AffineTransform::Updated();
|
||||
}
|
||||
|
||||
template <class ArchiveT>
|
||||
void serialize(ArchiveT &ar, const unsigned int version) {
|
||||
ar &HRPU(position, "mm");
|
||||
ar &HRPU(rotation, "rad");
|
||||
ar &HRP(scaling);
|
||||
}
|
||||
|
||||
AffineMatrix GetAffineMatrix() const {
|
||||
AffineMatrix m = AffineMatrix::Identity();
|
||||
m.translate(position);
|
||||
m.rotate(Eigen::AngleAxisf(rotation.z(), Vector3f::UnitZ()));
|
||||
m.rotate(Eigen::AngleAxisf(rotation.y(), Vector3f::UnitY()));
|
||||
m.rotate(Eigen::AngleAxisf(rotation.x(), Vector3f::UnitX()));
|
||||
m.scale(scaling);
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix4f GetMatrix() const { return this->GetAffineMatrix().matrix(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const TRS& trs) {
|
||||
os << trs.position << " " << trs.rotation << " " << trs.scaling;
|
||||
return os;
|
||||
inline std::ostream &operator<<(std::ostream &os, const TRS &trs) {
|
||||
os << trs.position << " " << trs.rotation << " " << trs.scaling;
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::istream& operator>>(std::istream& is, TRS& trs) {
|
||||
is >> trs.position >> trs.rotation >> trs.scaling;
|
||||
return is;
|
||||
inline std::istream &operator>>(std::istream &is, TRS &trs) {
|
||||
is >> trs.position >> trs.rotation >> trs.scaling;
|
||||
return is;
|
||||
}
|
||||
|
||||
} // uLib
|
||||
} // namespace uLib
|
||||
|
||||
|
||||
|
||||
#endif//U_TRANSFORM_H
|
||||
#endif // U_TRANSFORM_H
|
||||
|
||||
@@ -33,7 +33,11 @@ set(DICTIONARY_HEADERS muCastorMCTrack.h
|
||||
SkinDetectorWriter.h)
|
||||
|
||||
set(LIBRARIES ${ROOT_LIBRARIES}
|
||||
${PACKAGE_LIBPREFIX}Math)
|
||||
${PACKAGE_LIBPREFIX}Math
|
||||
Eigen3::Eigen)
|
||||
|
||||
get_target_property(EIGEN3_INC Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
|
||||
include_directories(${EIGEN3_INC})
|
||||
|
||||
set(rDictName ${PACKAGE_LIBPREFIX}RootDict)
|
||||
root_generate_dictionary(${rDictName} ${DICTIONARY_HEADERS}
|
||||
|
||||
@@ -1,88 +1,84 @@
|
||||
#ifndef muCastor_MCTRACK_H
|
||||
#define muCastor_MCTRACK_H
|
||||
|
||||
//########################################
|
||||
// muCastorMCTrack class
|
||||
// Created at the University of Brescia, Italy
|
||||
// Date: December 2011
|
||||
// Autors: Germano Bonomi germano.bonomi@ing.unibs.it
|
||||
// Martin Subieta martin.subieta@ing.unibs.it
|
||||
//########################################
|
||||
// ########################################
|
||||
// muCastorMCTrack class
|
||||
// Created at the University of Brescia, Italy
|
||||
// Date: December 2011
|
||||
// Autors: Germano Bonomi germano.bonomi@ing.unibs.it
|
||||
// Martin Subieta martin.subieta@ing.unibs.it
|
||||
// ########################################
|
||||
|
||||
#include <iostream>
|
||||
#include "vector"
|
||||
#include "Detectors/MuonScatter.h"
|
||||
#include "TObject.h"
|
||||
#include "TParticle.h"
|
||||
#include "Detectors/MuonScatter.h"
|
||||
#include "vector"
|
||||
#include <iostream>
|
||||
|
||||
class TClonesArray;
|
||||
|
||||
class muCastorMCTrack : public TObject
|
||||
{
|
||||
|
||||
public:
|
||||
class muCastorMCTrack : public TObject {
|
||||
|
||||
public:
|
||||
/** Default constructor **/
|
||||
muCastorMCTrack();
|
||||
|
||||
/** Constructor from TParticle **/
|
||||
muCastorMCTrack(TParticle* particle);
|
||||
muCastorMCTrack(TParticle *particle);
|
||||
|
||||
/** Destructor **/
|
||||
virtual ~muCastorMCTrack();
|
||||
void Reset();
|
||||
|
||||
|
||||
/** Accessors **/
|
||||
Int_t GetFirstDaughter() const { return fDaughter[0];}
|
||||
Int_t GetMother() const { return fMother[0];}
|
||||
Int_t GetFirstDaughter() const { return fDaughter[0]; }
|
||||
Int_t GetMother() const { return fMother[0]; }
|
||||
|
||||
/** Modifiers **/
|
||||
virtual void AddPoint(TLorentzVector pos, TLorentzVector mom);
|
||||
virtual void SetFirstDaughter(Int_t trkid) { fDaughter[0] = trkid; }
|
||||
virtual void SetLastDaughter(Int_t trkid) { fDaughter[1] = trkid; }
|
||||
virtual void AddPoint(TLorentzVector pos, TLorentzVector mom);
|
||||
virtual void SetFirstDaughter(Int_t trkid) { fDaughter[0] = trkid; }
|
||||
virtual void SetLastDaughter(Int_t trkid) { fDaughter[1] = trkid; }
|
||||
|
||||
inline void Dump() {
|
||||
std::cout << "muCastorMCTrack\n"
|
||||
<< "PDG code: " << fPdgCode << "\n"
|
||||
<< "Momentum: " << fPx << ", " << fPy << ", " << fPz << "\n"
|
||||
<< "Position: " << fVx << ", " << fVy << ", " << fVz << "\n"
|
||||
<< "Npoints: " << fNpoints << "\n";
|
||||
return;
|
||||
}
|
||||
inline void Dump() {
|
||||
std::cout << "muCastorMCTrack\n"
|
||||
<< "PDG code: " << fPdgCode << "\n"
|
||||
<< "Momentum: " << fPx << ", " << fPy << ", " << fPz << "\n"
|
||||
<< "Position: " << fVx << ", " << fVy << ", " << fVz << "\n"
|
||||
<< "Npoints: " << fNpoints << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
public:
|
||||
/* Private variables - copying private variables of TParticle */
|
||||
|
||||
/* Private variables - copying private variables of TParticle */
|
||||
Int_t fPdgCode; // PDG code of the particle
|
||||
Int_t fMother[2]; // Indices of the mother particles
|
||||
Int_t fDaughter[2]; // Indices of the daughter particles
|
||||
|
||||
Int_t fPdgCode; // PDG code of the particle
|
||||
Int_t fMother[2]; // Indices of the mother particles
|
||||
Int_t fDaughter[2]; // Indices of the daughter particles
|
||||
Double_t fPx; // x component of momentum
|
||||
Double_t fPy; // y component of momentum
|
||||
Double_t fPz; // z component of momentum
|
||||
Double_t fE; // Energy
|
||||
|
||||
Double_t fPx; // x component of momentum
|
||||
Double_t fPy; // y component of momentum
|
||||
Double_t fPz; // z component of momentum
|
||||
Double_t fE; // Energy
|
||||
Double_t fVx; // x of production vertex
|
||||
Double_t fVy; // y of production vertex
|
||||
Double_t fVz; // z of production vertex
|
||||
Double_t fVt; // t of production vertex
|
||||
|
||||
Double_t fVx; // x of production vertex
|
||||
Double_t fVy; // y of production vertex
|
||||
Double_t fVz; // z of production vertex
|
||||
Double_t fVt; // t of production vertex
|
||||
|
||||
Int_t fPointsSize; // capacity of points array
|
||||
Int_t fNpoints; // number of stored points
|
||||
Double_t *fPntPosX; //[fNpoints] array of points (x) belonging to this track
|
||||
Double_t *fPntPosY; //[fNpoints] array of points (y) belonging to this track
|
||||
Double_t *fPntPosZ; //[fNpoints] array of points (z) belonging to this track
|
||||
Double_t *fPntT; //[fNpoints] array of points (t) belonging to this track
|
||||
Double_t *fPntMomX; //[fNpoints] array of points (px) belonging to this track
|
||||
Double_t *fPntMomY; //[fNpoints] array of points (py) belonging to this track
|
||||
Double_t *fPntMomZ; //[fNpoints] array of points (pz) belonging to this track
|
||||
Double_t *fPntE; //[fNpoints] array of points (E) belonging to this track
|
||||
|
||||
ClassDef(muCastorMCTrack,1);
|
||||
Int_t fPointsSize; // capacity of points array
|
||||
Int_t fNpoints; // number of stored points
|
||||
Double_t *fPntPosX; //[fNpoints] array of points (x) belonging to this track
|
||||
Double_t *fPntPosY; //[fNpoints] array of points (y) belonging to this track
|
||||
Double_t *fPntPosZ; //[fNpoints] array of points (z) belonging to this track
|
||||
Double_t *fPntT; //[fNpoints] array of points (t) belonging to this track
|
||||
Double_t *fPntMomX; //[fNpoints] array of points (px) belonging to this track
|
||||
Double_t *fPntMomY; //[fNpoints] array of points (py) belonging to this track
|
||||
Double_t *fPntMomZ; //[fNpoints] array of points (pz) belonging to this track
|
||||
Double_t *fPntE; //[fNpoints] array of points (E) belonging to this track
|
||||
|
||||
ClassDef(muCastorMCTrack, 1);
|
||||
};
|
||||
|
||||
uLib::MuonScatter &operator << (uLib::MuonScatter &mu, const muCastorMCTrack &bsmu);
|
||||
#endif //muCastor_MCTRACK_H
|
||||
uLib::MuonScatter &operator<<(uLib::MuonScatter &mu,
|
||||
const muCastorMCTrack &bsmu);
|
||||
#endif // muCastor_MCTRACK_H
|
||||
|
||||
@@ -4,6 +4,7 @@ set(HEADERS uLibVtkInterface.h
|
||||
vtkQViewport.h
|
||||
vtkViewport.h
|
||||
vtkObjectsContext.h
|
||||
vtkMultiSelectionProp.h
|
||||
)
|
||||
|
||||
set(SOURCES uLibVtkInterface.cxx
|
||||
@@ -12,6 +13,7 @@ set(SOURCES uLibVtkInterface.cxx
|
||||
vtkQViewport.cpp
|
||||
vtkViewport.cpp
|
||||
vtkObjectsContext.cpp
|
||||
vtkMultiSelectionProp.cpp
|
||||
)
|
||||
|
||||
## Pull in Math VTK wrappers (sets MATH_SOURCES / MATH_HEADERS)
|
||||
|
||||
@@ -73,13 +73,13 @@ DetectorChamber::~DetectorChamber() {
|
||||
}
|
||||
|
||||
DetectorChamber::Content *DetectorChamber::GetContent() const {
|
||||
return static_cast<Content *>(m_Content);
|
||||
return static_cast<Content *>(this->m_model.get());
|
||||
}
|
||||
|
||||
void DetectorChamber::Update() {
|
||||
this->BaseClass::Update();
|
||||
|
||||
if (!m_Content) return;
|
||||
if (!this->m_model) return;
|
||||
Content *c = this->GetContent();
|
||||
Vector3f size = c->GetSize();
|
||||
HLine3f plane = c->GetProjectionPlane();
|
||||
|
||||
@@ -61,9 +61,9 @@ public:
|
||||
Content &GetModel();
|
||||
uLib::Object* GetContent() const override;
|
||||
|
||||
void PrintSelf(std::ostream &o) const;
|
||||
void PrintSelf(std::ostream &o) const override;
|
||||
|
||||
virtual vtkPolyData *GetPolyData() const;
|
||||
vtkPolyData *GetPolyData() const override;
|
||||
|
||||
void AddPocaPoint(HPoint3f poca);
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(vtkVoxRaytracerRepresentationTest) {
|
||||
grid.SetSpacing(Vector3f(1, 1, 1));
|
||||
grid.SetPosition(Vector3f(0, 0, 0));
|
||||
|
||||
Vtk::StructuredGrid v_grid(grid);
|
||||
Vtk::StructuredGrid v_grid(&grid);
|
||||
|
||||
// voxraytracer //
|
||||
VoxRaytracer rt(grid);
|
||||
|
||||
@@ -28,33 +28,33 @@ using namespace uLib;
|
||||
int main(int argc, char **argv) {
|
||||
bool interactive = (argc > 1 && std::string(argv[1]) == "-i");
|
||||
|
||||
// ---- 1. Build model objects ----
|
||||
ContainerBox box1;
|
||||
box1.Scale(Vector3f(1_m, 2_m, 0.5_m));
|
||||
box1.SetPosition(Vector3f(0, 0, 0));
|
||||
// ---- 1. Build model objects on the heap (expected by uLib SmartPointer) ----
|
||||
auto* box1 = new ContainerBox();
|
||||
box1->Scale(Vector3f(1, 2, 0.5));
|
||||
// box1->SetPosition(Vector3f(0, 0, 0));
|
||||
|
||||
ContainerBox box2;
|
||||
box2.Scale(Vector3f(0.5_m, 0.5_m, 3_m));
|
||||
box2.SetPosition(Vector3f(2_m, 0, 0));
|
||||
auto* box2 = new ContainerBox();
|
||||
box2->Scale(Vector3f(0.5, 0.5, 3));
|
||||
box2->SetPosition(Vector3f(2, 0, 0));
|
||||
|
||||
Cylinder cyl(0.3_m, 1.5_m, 1);
|
||||
cyl.SetPosition(Vector3f(0, 3_m, 0));
|
||||
auto* cyl = new Cylinder(1, 1.5, 1);
|
||||
cyl->SetPosition(Vector3f(0, 3, 0));
|
||||
|
||||
// ---- 2. Create an Assembly and add objects ----
|
||||
Assembly assembly;
|
||||
assembly.AddObject(&box1);
|
||||
assembly.AddObject(&box2);
|
||||
assembly.AddObject(&cyl);
|
||||
assembly.SetShowBoundingBox(true);
|
||||
auto* assembly = new Assembly();
|
||||
assembly->AddObject(box1);
|
||||
assembly->AddObject(box2);
|
||||
assembly->AddObject(cyl);
|
||||
assembly->SetShowBoundingBox(true);
|
||||
|
||||
// ---- 3. Apply a group transform ----
|
||||
assembly.SetPosition(Vector3f(1_m, 1_m, 0));
|
||||
// assembly->SetPosition(Vector3f(1_m, 1_m, 0));
|
||||
|
||||
// ---- 5. Visualize (create prop3ds to set properties) ----
|
||||
Vtk::Assembly vtkAsm(&assembly);
|
||||
Vtk::Assembly vtkAsm(assembly); // Vtk::Assembly takes ownership of the model wrapper
|
||||
|
||||
Vtk::Viewer viewer;
|
||||
vtkAsm.AddToViewer(viewer); // This triggers prop3d creation via ConnectRenderer which eventually calls Prop3D::GetProp
|
||||
vtkAsm.AddToViewer(viewer);
|
||||
|
||||
// Explicitly update to ensure prop3ds exist and are added to assemblies
|
||||
vtkAsm.Update();
|
||||
@@ -74,16 +74,16 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
};
|
||||
|
||||
setProps(childCtx->GetProp3D(&box1), 1.0, 0.0, 0.0); // Red
|
||||
setProps(childCtx->GetProp3D(&box2), 0.0, 1.0, 0.0); // Green
|
||||
setProps(childCtx->GetProp3D(&cyl), 0.0, 0.0, 1.0); // Blue
|
||||
setProps(childCtx->GetProp3D(box1), 1.0, 0.0, 0.0); // Red
|
||||
setProps(childCtx->GetProp3D(box2), 0.0, 1.0, 0.0); // Green
|
||||
setProps(childCtx->GetProp3D(cyl), 0.0, 0.0, 1.0); // Blue
|
||||
}
|
||||
|
||||
std::cout << "Prop3Ds in viewport: " << viewer.getProp3Ds().size() << " (Expected 4: 1 assembly + 3 children)" << std::endl;
|
||||
|
||||
// ---- 4. Query the bounding box for terminal output ----
|
||||
Vector3f bbMin, bbMax;
|
||||
assembly.GetBoundingBox(bbMin, bbMax);
|
||||
assembly->GetBoundingBox(bbMin, bbMax);
|
||||
std::cout << "Assembly bounding box:" << std::endl;
|
||||
std::cout << " min = " << bbMin.transpose() << std::endl;
|
||||
std::cout << " max = " << bbMax.transpose() << std::endl;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "Vtk/uLibVtkViewer.h"
|
||||
|
||||
#include "Vtk/Math/vtkContainerBox.h"
|
||||
#include "Math/Units.h"
|
||||
#include "Vtk/Math/vtkContainerBox.h"
|
||||
|
||||
@@ -36,19 +35,23 @@ using namespace uLib;
|
||||
int main() {
|
||||
BEGIN_TESTING(vtk ContainerBox Test);
|
||||
|
||||
ContainerBox box;
|
||||
box.Scale(Vector3f(1_m,2_m,1_m));
|
||||
box.SetPosition(Vector3f(0,0,0));
|
||||
{
|
||||
ContainerBox* box = new ContainerBox();
|
||||
box->Scale(Vector3f(1_m, 2_m, 1_m));
|
||||
box->SetPosition(Vector3f(0, 0, 0));
|
||||
|
||||
Vtk::ContainerBox v_box(&box);
|
||||
v_box.Update();
|
||||
Vtk::ContainerBox v_box(box);
|
||||
v_box.Update();
|
||||
|
||||
// v_box.SetRepresentation(Vtk::Prop3D::Surface);
|
||||
// v_box.SetOpacity(0.5);
|
||||
// v_box.SetSelectable(true);
|
||||
v_box.SetRepresentation(Vtk::Prop3D::Surface);
|
||||
v_box.SetOpacity(0.5);
|
||||
v_box.SetSelectable(true);
|
||||
}
|
||||
|
||||
box.findOrAddSignal(&Object::Updated)->connect([&box](){
|
||||
std::cout << "box updated: " << box.GetWorldPoint(HPoint3f(1,1,1)) << std::endl;
|
||||
Vtk::ContainerBox v_box;
|
||||
v_box.findOrAddSignal(&Object::Updated)->connect([&v_box]() {
|
||||
std::cout << "box updated: "
|
||||
<< v_box.get()->GetWorldPoint(HPoint3f(1, 1, 1)) << std::endl;
|
||||
});
|
||||
|
||||
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {
|
||||
|
||||
@@ -15,11 +15,11 @@ using namespace uLib;
|
||||
|
||||
int main() {
|
||||
std::cout << "Creating ContainerBox..." << std::endl;
|
||||
ContainerBox box(Vector3f(1.0, 1.0, 1.0)); // 1x1x1 unit box
|
||||
box.SetInstanceName("MyTestBox");
|
||||
ContainerBox* box = new ContainerBox(Vector3f(1.0, 1.0, 1.0)); // 1x1x1 unit box
|
||||
box->SetInstanceName("MyTestBox");
|
||||
|
||||
std::cout << "Creating VTK representation..." << std::endl;
|
||||
Vtk::ContainerBox v_box(&box);
|
||||
Vtk::ContainerBox v_box(box);
|
||||
v_box.SetRepresentation(Vtk::Prop3D::Wireframe);
|
||||
v_box.SetColor(1.0, 0.0, 0.0); // Red
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#include "Vtk/Math/vtkQuadMesh.h"
|
||||
#include "Vtk/Math/vtkQuadMesh.h"
|
||||
#include "Vtk/uLibVtkViewer.h"
|
||||
|
||||
@@ -42,7 +41,7 @@ BOOST_AUTO_TEST_CASE(vtkQuadMeshConstruction) {
|
||||
|
||||
mesh.AddQuad(Vector4i(0, 1, 2, 3));
|
||||
|
||||
Vtk::QuadMesh v_mesh(mesh);
|
||||
Vtk::QuadMesh v_mesh(&mesh);
|
||||
|
||||
Object::connect(&mesh, &QuadMesh::Updated, [&mesh]() {
|
||||
Vector3f points[4];
|
||||
@@ -50,8 +49,8 @@ BOOST_AUTO_TEST_CASE(vtkQuadMeshConstruction) {
|
||||
points[1] = mesh.GetPoint(1);
|
||||
points[2] = mesh.GetPoint(2);
|
||||
points[3] = mesh.GetPoint(3);
|
||||
std::cout << "mesh updated: " << points[0] << " " << points[1]
|
||||
<< " " << points[2] << " " << points[3] << std::endl;
|
||||
std::cout << "mesh updated: " << points[0] << " " << points[1] << " "
|
||||
<< points[2] << " " << points[3] << std::endl;
|
||||
});
|
||||
|
||||
v_mesh.Update();
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#include "Vtk/Math/vtkStructuredGrid.h"
|
||||
#include "Vtk/Math/vtkStructuredGrid.h"
|
||||
#include "Vtk/uLibVtkViewer.h"
|
||||
|
||||
@@ -36,7 +35,7 @@ BOOST_AUTO_TEST_CASE(vtkStructuredGridTest) {
|
||||
StructuredGrid grid(Vector3i(10, 10, 100));
|
||||
grid.SetSpacing(Vector3f(3, 1, 1));
|
||||
|
||||
Vtk::StructuredGrid grid_viewer(grid);
|
||||
Vtk::StructuredGrid grid_viewer(&grid);
|
||||
|
||||
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {
|
||||
Vtk::Viewer viewer;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#include "Vtk/Math/vtkTriangleMesh.h"
|
||||
#include "Vtk/Math/vtkTriangleMesh.h"
|
||||
#include "Vtk/uLibVtkViewer.h"
|
||||
|
||||
@@ -40,15 +39,16 @@ BOOST_AUTO_TEST_CASE(vtkTriangleMeshConstruction) {
|
||||
mesh.AddPoint(Vector3f(1, 0, 0));
|
||||
mesh.AddTriangle(Vector3i(0, 1, 2));
|
||||
|
||||
Vtk::TriangleMesh v_mesh(mesh);
|
||||
Vtk::TriangleMesh v_mesh(&mesh);
|
||||
|
||||
Object::connect(&mesh, &TriangleMesh::Updated, [&mesh]() {
|
||||
Vector3f points[3];
|
||||
points[0] = mesh.GetPoint(0);
|
||||
points[1] = mesh.GetPoint(1);
|
||||
points[2] = mesh.GetPoint(2);
|
||||
std::cout << "mesh updated: " << points[0].transpose() << " " << points[1].transpose()
|
||||
<< " " << points[2].transpose() << std::endl;
|
||||
std::cout << "mesh updated: " << points[0].transpose() << " "
|
||||
<< points[1].transpose() << " " << points[2].transpose()
|
||||
<< std::endl;
|
||||
});
|
||||
|
||||
v_mesh.Update();
|
||||
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(vtkTriangleMeshConstruction) {
|
||||
BOOST_AUTO_TEST_CASE(vtkTriangleMeshConstruction2) {
|
||||
TriangleMesh mesh;
|
||||
|
||||
Vtk::TriangleMesh v_mesh(mesh);
|
||||
Vtk::TriangleMesh v_mesh(&mesh);
|
||||
v_mesh.ReadFromStlFile("capelluzzo.stl");
|
||||
v_mesh.Update();
|
||||
|
||||
|
||||
@@ -9,134 +9,140 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#include "Vtk/Math/vtkVoxImage.h"
|
||||
#include "Vtk/Math/vtkVoxImage.h"
|
||||
#include "Vtk/uLibVtkViewer.h"
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkCallbackCommand.h>
|
||||
#include <vtkRenderWindowInteractor.h>
|
||||
#include <vtkProperty.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkRenderWindow.h>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <vtkCallbackCommand.h>
|
||||
#include <vtkProperty.h>
|
||||
#include <vtkRenderWindow.h>
|
||||
#include <vtkRenderWindowInteractor.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkSmartPointer.h>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
struct AppState {
|
||||
std::vector<Vtk::VoxImage*> images;
|
||||
Vtk::Viewer* viewer;
|
||||
std::vector<Vtk::VoxImage *> images;
|
||||
Vtk::Viewer *viewer;
|
||||
};
|
||||
|
||||
void KeyPressCallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData) {
|
||||
auto* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
|
||||
auto* state = static_cast<AppState*>(clientData);
|
||||
void KeyPressCallbackFunction(vtkObject *caller, long unsigned int eventId,
|
||||
void *clientData, void *callData) {
|
||||
auto *interactor = static_cast<vtkRenderWindowInteractor *>(caller);
|
||||
auto *state = static_cast<AppState *>(clientData);
|
||||
|
||||
std::string key = interactor->GetKeySym();
|
||||
if (key == "w") {
|
||||
std::cout << "--> Switching all images to Wireframe Box" << std::endl;
|
||||
for (auto* img : state->images) img->SetRepresentation(Vtk::Prop3D::Wireframe);
|
||||
state->viewer->GetRenderWindow()->Render();
|
||||
}
|
||||
else if (key == "s") {
|
||||
std::cout << "--> Switching all images to Surface (Volume Rendering)" << std::endl;
|
||||
for (auto* img : state->images) img->SetRepresentation(Vtk::Prop3D::Surface);
|
||||
state->viewer->GetRenderWindow()->Render();
|
||||
}
|
||||
else if (key >= "0" && key <= "5") {
|
||||
int preset = key[0] - '0';
|
||||
std::cout << "--> Switching all images to Rendering Preset " << preset << std::endl;
|
||||
for (auto* img : state->images) img->setShadingPreset(preset);
|
||||
state->viewer->GetRenderWindow()->Render();
|
||||
}
|
||||
std::string key = interactor->GetKeySym();
|
||||
if (key == "w") {
|
||||
std::cout << "--> Switching all images to Wireframe Box" << std::endl;
|
||||
for (auto *img : state->images)
|
||||
img->SetRepresentation(Vtk::Prop3D::Wireframe);
|
||||
state->viewer->GetRenderWindow()->Render();
|
||||
} else if (key == "s") {
|
||||
std::cout << "--> Switching all images to Surface (Volume Rendering)"
|
||||
<< std::endl;
|
||||
for (auto *img : state->images)
|
||||
img->SetRepresentation(Vtk::Prop3D::Surface);
|
||||
state->viewer->GetRenderWindow()->Render();
|
||||
} else if (key >= "0" && key <= "5") {
|
||||
int preset = key[0] - '0';
|
||||
std::cout << "--> Switching all images to Rendering Preset " << preset
|
||||
<< std::endl;
|
||||
for (auto *img : state->images)
|
||||
img->setShadingPreset(preset);
|
||||
state->viewer->GetRenderWindow()->Render();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
float factor = 1.0e6f;
|
||||
int main(int argc, char **argv) {
|
||||
float factor = 1.0e6f;
|
||||
|
||||
// --- Image 1: Spherical Shell ---
|
||||
Vector3i dims1(64, 64, 64);
|
||||
VoxImage<Voxel> img1(dims1);
|
||||
img1.SetSpacing(Vector3f(1.0, 1.0, 1.0));
|
||||
img1.SetPosition(Vector3f(-40, -32, -32));
|
||||
// --- Image 1: Spherical Shell ---
|
||||
Vector3i dims1(64, 64, 64);
|
||||
VoxImage<Voxel> img1(dims1);
|
||||
img1.SetSpacing(Vector3f(1.0, 1.0, 1.0));
|
||||
img1.SetPosition(Vector3f(-40, -32, -32));
|
||||
|
||||
for (int z = 0; z < dims1(2); ++z) {
|
||||
for (int y = 0; y < dims1(1); ++y) {
|
||||
for (int x = 0; x < dims1(0); ++x) {
|
||||
float dx = x - 32.0f;
|
||||
float dy = y - 32.0f;
|
||||
float dz = z - 32.0f;
|
||||
float dist = std::sqrt(dx*dx + dy*dy + dz*dz);
|
||||
Voxel v;
|
||||
if (dist < 25.0f && dist > 10.0f) {
|
||||
v.Value = (40.0f * (25.0f - dist) / 15.0f) / factor;
|
||||
} else {
|
||||
v.Value = 0.0f;
|
||||
}
|
||||
img1[Vector3i(x, y, z)] = v;
|
||||
}
|
||||
for (int z = 0; z < dims1(2); ++z) {
|
||||
for (int y = 0; y < dims1(1); ++y) {
|
||||
for (int x = 0; x < dims1(0); ++x) {
|
||||
float dx = x - 32.0f;
|
||||
float dy = y - 32.0f;
|
||||
float dz = z - 32.0f;
|
||||
float dist = std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||
Voxel v;
|
||||
if (dist < 25.0f && dist > 10.0f) {
|
||||
v.Value = (40.0f * (25.0f - dist) / 15.0f) / factor;
|
||||
} else {
|
||||
v.Value = 0.0f;
|
||||
}
|
||||
img1[Vector3i(x, y, z)] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Image 2: Axes Gradient ---
|
||||
Vector3i dims2(64, 64, 64);
|
||||
VoxImage<Voxel> img2(dims2);
|
||||
img2.SetSpacing(Vector3f(1.0, 1.0, 1.0));
|
||||
img2.SetPosition(Vector3f(40, -32, -32));
|
||||
// --- Image 2: Axes Gradient ---
|
||||
Vector3i dims2(64, 64, 64);
|
||||
VoxImage<Voxel> img2(dims2);
|
||||
img2.SetSpacing(Vector3f(1.0, 1.0, 1.0));
|
||||
img2.SetPosition(Vector3f(40, -32, -32));
|
||||
|
||||
for (int z = 0; z < dims2(2); ++z) {
|
||||
for (int y = 0; y < dims2(1); ++y) {
|
||||
for (int x = 0; x < dims2(0); ++x) {
|
||||
Voxel v;
|
||||
// Linear gradient along X, Y, Z
|
||||
float val = (float(x)/dims2(0) + float(y)/dims2(1) + float(z)/dims2(2)) / 3.0f;
|
||||
v.Value = (40.0f * val) / factor;
|
||||
img2[Vector3i(x, y, z)] = v;
|
||||
}
|
||||
}
|
||||
for (int z = 0; z < dims2(2); ++z) {
|
||||
for (int y = 0; y < dims2(1); ++y) {
|
||||
for (int x = 0; x < dims2(0); ++x) {
|
||||
Voxel v;
|
||||
// Linear gradient along X, Y, Z
|
||||
float val =
|
||||
(float(x) / dims2(0) + float(y) / dims2(1) + float(z) / dims2(2)) /
|
||||
3.0f;
|
||||
v.Value = (40.0f * val) / factor;
|
||||
img2[Vector3i(x, y, z)] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vtk::VoxImage vtk_img1(img1);
|
||||
vtk_img1.setShadingPreset(0);
|
||||
Vtk::VoxImage vtk_img1(&img1);
|
||||
vtk_img1.setShadingPreset(0);
|
||||
|
||||
Vtk::VoxImage vtk_img2(img2);
|
||||
vtk_img2.setShadingPreset(1); // Use Composite without MIP for variety
|
||||
Vtk::VoxImage vtk_img2(&img2);
|
||||
vtk_img2.setShadingPreset(1); // Use Composite without MIP for variety
|
||||
|
||||
Vtk::Viewer viewer;
|
||||
viewer.GetRenderer()->SetBackground(0.05, 0.05, 0.1);
|
||||
viewer.AddProp3D(vtk_img1);
|
||||
viewer.AddProp3D(vtk_img2);
|
||||
Vtk::Viewer viewer;
|
||||
viewer.GetRenderer()->SetBackground(0.05, 0.05, 0.1);
|
||||
viewer.AddProp3D(vtk_img1);
|
||||
viewer.AddProp3D(vtk_img2);
|
||||
|
||||
// Setup KeyPress Callback
|
||||
AppState state;
|
||||
state.images.push_back(&vtk_img1);
|
||||
state.images.push_back(&vtk_img2);
|
||||
state.viewer = &viewer;
|
||||
// Setup KeyPress Callback
|
||||
AppState state;
|
||||
state.images.push_back(&vtk_img1);
|
||||
state.images.push_back(&vtk_img2);
|
||||
state.viewer = &viewer;
|
||||
|
||||
vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();
|
||||
keyCallback->SetCallback(KeyPressCallbackFunction);
|
||||
keyCallback->SetClientData(&state);
|
||||
viewer.GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
|
||||
vtkSmartPointer<vtkCallbackCommand> keyCallback =
|
||||
vtkSmartPointer<vtkCallbackCommand>::New();
|
||||
keyCallback->SetCallback(KeyPressCallbackFunction);
|
||||
keyCallback->SetClientData(&state);
|
||||
viewer.GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
|
||||
|
||||
std::cout << "=========================================" << std::endl;
|
||||
std::cout << " VoxImage Interactive Viewer Test" << std::endl;
|
||||
std::cout << " [LEFT] Spherical Shell (MIP)" << std::endl;
|
||||
std::cout << " [RIGHT] Axes Gradient (Composite)" << std::endl;
|
||||
std::cout << "-----------------------------------------" << std::endl;
|
||||
std::cout << " Press [w] to show Wireframe Bounding Boxes" << std::endl;
|
||||
std::cout << " Press [s] to show Volume Rendering" << std::endl;
|
||||
std::cout << " Press [0..5] to switch Rendering Presets:" << std::endl;
|
||||
std::cout << " 0: MIP (Grayscale)" << std::endl;
|
||||
std::cout << " 1: Composite (Grayscale)" << std::endl;
|
||||
std::cout << " 2: Composite (Shaded)" << std::endl;
|
||||
std::cout << " 3: CT Bone/Tissue (Bone colors)" << std::endl;
|
||||
std::cout << " 4: MIP (Rainbow)" << std::endl;
|
||||
std::cout << " 5: Additive (Total path sum)" << std::endl;
|
||||
std::cout << " Press [q] to quit" << std::endl;
|
||||
std::cout << "=========================================" << std::endl;
|
||||
std::cout << "=========================================" << std::endl;
|
||||
std::cout << " VoxImage Interactive Viewer Test" << std::endl;
|
||||
std::cout << " [LEFT] Spherical Shell (MIP)" << std::endl;
|
||||
std::cout << " [RIGHT] Axes Gradient (Composite)" << std::endl;
|
||||
std::cout << "-----------------------------------------" << std::endl;
|
||||
std::cout << " Press [w] to show Wireframe Bounding Boxes" << std::endl;
|
||||
std::cout << " Press [s] to show Volume Rendering" << std::endl;
|
||||
std::cout << " Press [0..5] to switch Rendering Presets:" << std::endl;
|
||||
std::cout << " 0: MIP (Grayscale)" << std::endl;
|
||||
std::cout << " 1: Composite (Grayscale)" << std::endl;
|
||||
std::cout << " 2: Composite (Shaded)" << std::endl;
|
||||
std::cout << " 3: CT Bone/Tissue (Bone colors)" << std::endl;
|
||||
std::cout << " 4: MIP (Rainbow)" << std::endl;
|
||||
std::cout << " 5: Additive (Total path sum)" << std::endl;
|
||||
std::cout << " Press [q] to quit" << std::endl;
|
||||
std::cout << "=========================================" << std::endl;
|
||||
|
||||
viewer.ZoomAuto();
|
||||
viewer.Start();
|
||||
viewer.ZoomAuto();
|
||||
viewer.Start();
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#include "Vtk/Math/vtkVoxImage.h"
|
||||
#include "Vtk/Math/vtkVoxImage.h"
|
||||
#include "Vtk/uLibVtkViewer.h"
|
||||
|
||||
@@ -46,7 +45,7 @@ BOOST_AUTO_TEST_CASE(vtkVoxImageConstruction) {
|
||||
img.InitVoxels(zero);
|
||||
img[Vector3i(3, 3, 3)] = nonzero;
|
||||
|
||||
Vtk::VoxImage vtk_img(img);
|
||||
Vtk::VoxImage vtk_img(&img);
|
||||
vtk_img.SaveToXMLFile("test_vtkvoximage.vti");
|
||||
|
||||
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {
|
||||
|
||||
@@ -31,29 +31,27 @@ namespace Vtk {
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
Assembly::Assembly(uLib::Assembly *content)
|
||||
: m_Content(content),
|
||||
: ObjectWrapper(content),
|
||||
m_ChildContext(nullptr),
|
||||
m_BBoxActor(nullptr),
|
||||
m_VtkAsm(nullptr),
|
||||
m_InUpdate(false) {
|
||||
this->InstallPipe();
|
||||
if (m_Content) {
|
||||
Object::connect(m_Content, &uLib::Assembly::Updated,
|
||||
if (this->m_model) {
|
||||
Object::connect(this->m_model.get(), &uLib::Assembly::Updated,
|
||||
this, &Assembly::Update);
|
||||
}
|
||||
}
|
||||
|
||||
Assembly::~Assembly() {
|
||||
delete m_ChildContext;
|
||||
if (m_BBoxActor) m_BBoxActor->Delete();
|
||||
if (m_VtkAsm) m_VtkAsm->Delete();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
void Assembly::InstallPipe() {
|
||||
// 1. Create the VTK library assembly that groups everything
|
||||
m_VtkAsm = ::vtkAssembly::New();
|
||||
m_VtkAsm->PickableOff();
|
||||
// 1. Setup the internal VTK assembly
|
||||
m_VtkAsm = vtkSmartPointer<::vtkAssembly>::New();
|
||||
m_BBoxActor = vtkSmartPointer<::vtkActor>::New();
|
||||
this->SetProp(m_VtkAsm);
|
||||
|
||||
// 2. Create the bounding-box wireframe actor
|
||||
@@ -64,20 +62,19 @@ void Assembly::InstallPipe() {
|
||||
vtkNew<vtkPolyDataMapper> mapper;
|
||||
mapper->SetInputConnection(cube->GetOutputPort());
|
||||
|
||||
m_BBoxActor = vtkActor::New();
|
||||
m_BBoxActor->SetMapper(mapper);
|
||||
m_BBoxActor->GetProperty()->SetRepresentationToWireframe();
|
||||
m_BBoxActor->GetProperty()->SetColor(1.0, 0.85, 0.0); // gold wireframe
|
||||
m_BBoxActor->GetProperty()->SetLineWidth(1.5);
|
||||
m_BBoxActor->GetProperty()->SetOpacity(0.6);
|
||||
m_BBoxActor->PickableOff();
|
||||
m_BBoxActor->SetVisibility(m_Content ? m_Content->GetShowBoundingBox() : false);
|
||||
m_BBoxActor->SetVisibility(this->m_model ? this->m_model->GetShowBoundingBox() : false);
|
||||
|
||||
m_VtkAsm->AddPart(m_BBoxActor);
|
||||
|
||||
// 3. Build a child-objects context (auto-creates prop3ds for each child)
|
||||
if (m_Content) {
|
||||
m_ChildContext = new ObjectsContext(m_Content);
|
||||
if (this->m_model) {
|
||||
m_ChildContext = new ObjectsContext(this->m_model);
|
||||
// Link the children context's assembly into our group assembly
|
||||
if (auto* childProp = vtkProp3D::SafeDownCast(m_ChildContext->GetProp())) {
|
||||
m_VtkAsm->AddPart(childProp);
|
||||
@@ -93,14 +90,7 @@ void Assembly::Update() {
|
||||
if (m_InUpdate) return;
|
||||
m_InUpdate = true;
|
||||
|
||||
if (m_Content && m_VtkAsm) {
|
||||
// Apply world matrix from the assembly content
|
||||
vtkNew<vtkMatrix4x4> m;
|
||||
Matrix4fToVtk(m_Content->GetMatrix(), m);
|
||||
m_VtkAsm->SetUserMatrix(m);
|
||||
m_VtkAsm->Modified();
|
||||
}
|
||||
|
||||
// Delegate to Prop3D to handle standard transformation application (uses GetContent())
|
||||
this->Prop3D::Update();
|
||||
this->UpdateBoundingBox();
|
||||
if (m_ChildContext)
|
||||
@@ -110,33 +100,37 @@ void Assembly::Update() {
|
||||
|
||||
void Assembly::SyncFromVtk() {
|
||||
if (m_InUpdate) return;
|
||||
if (!m_Content || !m_VtkAsm) return;
|
||||
|
||||
m_InUpdate = true;
|
||||
|
||||
// VTK -> Model: Update world matrix (accounting for model parents)
|
||||
if (vtkProp3D* proxy = this->GetProxyProp()) {
|
||||
m_Content->SetWorldMatrix(VtkToMatrix4f(proxy->GetUserMatrix()));
|
||||
m_Content->FromMatrix(m_Content->GetMatrix());
|
||||
}
|
||||
// Sync the group-level transformation from VTK to the domain model
|
||||
this->Prop3D::SyncFromVtk();
|
||||
|
||||
this->UpdateBoundingBox();
|
||||
// Propagate sync to children
|
||||
if (m_ChildContext)
|
||||
m_ChildContext->SyncFromVtk();
|
||||
|
||||
m_Content->Updated(); // Notify change in model
|
||||
|
||||
m_InUpdate = false;
|
||||
}
|
||||
|
||||
|
||||
void Assembly::serialize_display(uLib::Archive::display_properties_archive &ar, const unsigned int version) {
|
||||
// 1. Register base class appearance/transform
|
||||
this->Prop3D::serialize_display(ar, version);
|
||||
|
||||
// 2. Map domain model properties (Bounding Box visibility, etc.)
|
||||
if (this->m_model) {
|
||||
ar & HRP("ShowBoundingBox", m_model->m_ShowBoundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
void Assembly::UpdateBoundingBox() {
|
||||
if (!m_Content || !m_BBoxActor) return;
|
||||
if (!this->m_model || !m_BBoxActor) return;
|
||||
|
||||
m_BBoxActor->SetVisibility(m_Content->GetShowBoundingBox());
|
||||
m_BBoxActor->SetVisibility(this->m_model->GetShowBoundingBox());
|
||||
|
||||
Vector3f bbMin, bbMax;
|
||||
m_Content->GetBoundingBox(bbMin, bbMax);
|
||||
this->m_model->GetBoundingBox(bbMin, bbMax);
|
||||
|
||||
// Avoid degenerate boxes
|
||||
Vector3f size = bbMax - bbMin;
|
||||
@@ -188,10 +182,6 @@ void Assembly::UpdateBoundingBox() {
|
||||
m_BBoxActor->Modified();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
ObjectsContext *Assembly::GetChildrenContext() const {
|
||||
return m_ChildContext;
|
||||
}
|
||||
|
||||
} // namespace Vtk
|
||||
} // namespace uLib
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
#ifndef U_VTK_ASSEMBLY_H
|
||||
#define U_VTK_ASSEMBLY_H
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/Assembly.h"
|
||||
#include "uLibVtkInterface.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
|
||||
class vtkActor;
|
||||
class vtkAssembly; // VTK library forward declaration (must be before namespace)
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkActor.h>
|
||||
#include <vtkAssembly.h>
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
@@ -34,38 +36,40 @@ class ObjectsContext; // forward
|
||||
* the VTK library class vtkAssembly for grouping, but the two
|
||||
* are distinct.
|
||||
*/
|
||||
class Assembly : public Prop3D {
|
||||
class Assembly : public Prop3D, public uLib::ObjectWrapper<uLib::Assembly> {
|
||||
public:
|
||||
uLibTypeMacro(Assembly, Prop3D)
|
||||
|
||||
Assembly(uLib::Assembly *content);
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param content Pointer to the domain assembly model.
|
||||
*/
|
||||
explicit Assembly(uLib::Assembly *content);
|
||||
virtual ~Assembly();
|
||||
|
||||
/** @brief Updates the VTK representation from the model (model→VTK). */
|
||||
virtual void Update() override;
|
||||
|
||||
/** @brief Synchronizes the model from the VTK representation (VTK→model). */
|
||||
virtual void SyncFromVtk() override;
|
||||
|
||||
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
|
||||
virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_Content; }
|
||||
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_model.get(); }
|
||||
virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_model.get(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the prop3d managing child objects.
|
||||
*/
|
||||
/** @brief Returns the visualization context for children. */
|
||||
uLib::Vtk::ObjectsContext* GetChildrenContext() const { return m_ChildContext; }
|
||||
|
||||
/** @brief Returns the prop3d managing child objects. */
|
||||
ObjectsContext *GetChildrenContext() const;
|
||||
/** @brief Property serialization for Display Properties Panel. */
|
||||
void serialize_display(Archive::display_properties_archive &ar, const unsigned int version = 0) override;
|
||||
|
||||
private:
|
||||
void UpdateBoundingBox();
|
||||
void InstallPipe();
|
||||
|
||||
uLib::Assembly *m_Content;
|
||||
ObjectsContext *m_ChildContext;
|
||||
vtkActor *m_BBoxActor;
|
||||
::vtkAssembly *m_VtkAsm; // VTK library assembly — NOT this class
|
||||
bool m_InUpdate; // re-entrancy guard
|
||||
vtkSmartPointer<::vtkAssembly> m_VtkAsm;
|
||||
vtkSmartPointer<::vtkActor> m_BBoxActor;
|
||||
|
||||
uLib::Vtk::ObjectsContext *m_ChildContext;
|
||||
bool m_InUpdate;
|
||||
};
|
||||
|
||||
} // namespace Vtk
|
||||
|
||||
@@ -43,53 +43,49 @@
|
||||
|
||||
#include "Vtk/Math/vtkDense.h"
|
||||
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
struct ContainerBoxData {
|
||||
vtkSmartPointer<vtkActor> m_Cube;
|
||||
vtkSmartPointer<vtkActor> m_Axes;
|
||||
vtkSmartPointer<vtkAssembly> m_VtkAsm;
|
||||
uLib::Connection m_UpdateSignal;
|
||||
vtkSmartPointer<vtkActor> m_Cube;
|
||||
vtkSmartPointer<vtkActor> m_Axes;
|
||||
vtkSmartPointer<vtkAssembly> m_VtkAsm;
|
||||
uLib::Connection m_UpdateSignal;
|
||||
|
||||
|
||||
ContainerBoxData() : m_Cube(vtkSmartPointer<vtkActor>::New()),
|
||||
m_Axes(vtkSmartPointer<vtkActor>::New()),
|
||||
m_VtkAsm(vtkSmartPointer<vtkAssembly>::New()) {}
|
||||
ContainerBoxData()
|
||||
: m_Cube(vtkSmartPointer<vtkActor>::New()),
|
||||
m_Axes(vtkSmartPointer<vtkActor>::New()),
|
||||
m_VtkAsm(vtkSmartPointer<vtkAssembly>::New()) {}
|
||||
~ContainerBoxData() {
|
||||
m_UpdateSignal.disconnect();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ContainerBox::ContainerBox(ContainerBox::Content *content)
|
||||
: d(new ContainerBoxData()), m_Content(content) {
|
||||
: d(new ContainerBoxData()),
|
||||
ObjectWrapper(content ? content : new Content()) {
|
||||
this->InstallPipe();
|
||||
d->m_UpdateSignal = Object::connect(m_Content, &uLib::Object::Updated, this, &ContainerBox::Update);
|
||||
d->m_UpdateSignal = Object::connect(
|
||||
this->m_model.get(), &uLib::Object::Updated, this, &ContainerBox::Update);
|
||||
}
|
||||
|
||||
ContainerBox::~ContainerBox() {
|
||||
delete d;
|
||||
}
|
||||
ContainerBox::~ContainerBox() { delete d; }
|
||||
|
||||
vtkPolyData *ContainerBox::GetPolyData() const {
|
||||
// TODO
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void ContainerBox::Update() {
|
||||
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
|
||||
if (!m_Content) return;
|
||||
if (!this->m_model)
|
||||
return;
|
||||
|
||||
vtkProp3D* prop = vtkProp3D::SafeDownCast(this->GetProp());
|
||||
vtkProp3D *prop = vtkProp3D::SafeDownCast(this->GetProp());
|
||||
if (prop) {
|
||||
// Apply the full volume matrix (TRS * m_LocalT)
|
||||
vtkNew<vtkMatrix4x4> m;
|
||||
Matrix4fToVtk(m_Content->GetMatrix(), m);
|
||||
Matrix4fToVtk(this->m_model->GetMatrix(), m);
|
||||
prop->SetUserMatrix(m);
|
||||
prop->Modified();
|
||||
}
|
||||
@@ -101,34 +97,10 @@ void ContainerBox::Update() {
|
||||
|
||||
|
||||
|
||||
|
||||
void ContainerBox::SyncFromVtk() {
|
||||
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
|
||||
if (!m_Content) return;
|
||||
|
||||
vtkProp3D* root = this->GetProxyProp();
|
||||
if (!root) return;
|
||||
|
||||
// VTK -> Model: Extract new world TRS from proxy, which matches the model's TRS center
|
||||
vtkMatrix4x4* rootMat = root->GetUserMatrix();
|
||||
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
|
||||
|
||||
// Synchronize TRS property members from the updated local matrix
|
||||
m_Content->FromMatrix(vtkWorld);
|
||||
|
||||
// Since we modified the model, notify observers, but block the loop back to VTK
|
||||
// ConnectionBlock blocker(d->m_UpdateSignal);
|
||||
m_Content->Updated();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ContainerBox::InstallPipe() {
|
||||
if (!m_Content)
|
||||
if (!this->m_model)
|
||||
return;
|
||||
Content *c = m_Content;
|
||||
Content *c = this->m_model;
|
||||
|
||||
// CUBE
|
||||
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef U_VTKCONTAINERBOX_H
|
||||
#define U_VTKCONTAINERBOX_H
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "uLibVtkInterface.h"
|
||||
#include "vtkPolydata.h"
|
||||
@@ -37,13 +38,15 @@ namespace Vtk {
|
||||
|
||||
struct ContainerBoxData;
|
||||
|
||||
class ContainerBox : public Prop3D, public Polydata {
|
||||
class ContainerBox : public Prop3D,
|
||||
public Polydata,
|
||||
public uLib::ObjectWrapper<uLib::ContainerBox> {
|
||||
|
||||
uLibTypeMacro(ContainerBox, Prop3D, Polydata)
|
||||
typedef uLib::ContainerBox Content;
|
||||
uLibTypeMacro(ContainerBox, Prop3D,
|
||||
Polydata) typedef uLib::ContainerBox Content;
|
||||
|
||||
public:
|
||||
ContainerBox(Content *content);
|
||||
ContainerBox(Content *content = nullptr);
|
||||
~ContainerBox();
|
||||
|
||||
virtual class vtkPolyData *GetPolyData() const override;
|
||||
@@ -56,15 +59,16 @@ public:
|
||||
/**
|
||||
* @brief Synchronizes the model from the VTK representation (VTK→model).
|
||||
*/
|
||||
virtual void SyncFromVtk() override;
|
||||
|
||||
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
|
||||
|
||||
virtual uLib::Object *GetContent() const override {
|
||||
return (uLib::Object *)m_model.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void InstallPipe();
|
||||
|
||||
struct ContainerBoxData *d;
|
||||
uLib::ContainerBox *m_Content;
|
||||
|
||||
ULIB_DECLARE_PROPERTIES(ContainerBox)
|
||||
};
|
||||
|
||||
@@ -38,25 +38,24 @@ namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
Cylinder::Cylinder(Cylinder::Content *content)
|
||||
: m_Content(content), m_Actor(nullptr), m_VtkAsm(nullptr) {
|
||||
: ObjectWrapper(content), m_Actor(nullptr), m_VtkAsm(nullptr) {
|
||||
this->InstallPipe();
|
||||
m_UpdateSignal = Object::connect(m_Content, &uLib::Object::Updated, this, &Cylinder::Update);
|
||||
m_UpdateSignal = Object::connect(this->m_model.get(), &uLib::Object::Updated, this, &Cylinder::Update);
|
||||
}
|
||||
|
||||
Cylinder::~Cylinder() {
|
||||
if (m_Actor) m_Actor->Delete();
|
||||
if (m_VtkAsm) m_VtkAsm->Delete();
|
||||
m_UpdateSignal.disconnect();
|
||||
}
|
||||
|
||||
void Cylinder::Update() {
|
||||
if (!m_Content)
|
||||
if (!this->m_model)
|
||||
return;
|
||||
|
||||
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
|
||||
if (root) {
|
||||
// 1. Placement handled specifically from content (use TRS GetMatrix, not World)
|
||||
vtkNew<vtkMatrix4x4> m;
|
||||
Matrix4fToVtk(m_Content->GetMatrix(), m);
|
||||
Matrix4fToVtk(this->m_model->GetMatrix(), m);
|
||||
root->SetUserMatrix(m);
|
||||
|
||||
// 2. Shape-local properties (Radius, Height, Axis alignment) go to the internal actor
|
||||
@@ -68,10 +67,10 @@ void Cylinder::Update() {
|
||||
|
||||
// Initial source is centered Y-cylinder (Radial XZ [-1,1], Height Y [-0.5, 0.5])
|
||||
// Apply Radius and Height scaling
|
||||
alignment->Scale(m_Content->GetRadius(), m_Content->GetHeight(), m_Content->GetRadius());
|
||||
alignment->Scale(this->m_model->GetRadius(), this->m_model->GetHeight(), this->m_model->GetRadius());
|
||||
|
||||
// Apply Axis alignment
|
||||
int axis = m_Content->GetAxis();
|
||||
int axis = this->m_model->GetAxis();
|
||||
if (axis == 0) alignment->RotateZ(-90); // Y -> X
|
||||
else if (axis == 1) ; // Y -> Y (identity)
|
||||
else if (axis == 2) alignment->RotateX(90); // Y -> Z
|
||||
@@ -85,26 +84,13 @@ void Cylinder::Update() {
|
||||
this->Prop3D::Update();
|
||||
}
|
||||
|
||||
void Cylinder::SyncFromVtk() {
|
||||
if (!m_Content) return;
|
||||
|
||||
vtkProp3D* root = this->GetProxyProp();
|
||||
if (!root) return;
|
||||
|
||||
// VTK -> Model: Extract new world TRS from proxy
|
||||
vtkMatrix4x4* rootMat = root->GetUserMatrix();
|
||||
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
|
||||
|
||||
// Directly sync model from the world matrix
|
||||
m_Content->FromMatrix(vtkWorld);
|
||||
m_Content->Updated();
|
||||
}
|
||||
|
||||
void Cylinder::InstallPipe() {
|
||||
if (!m_Content)
|
||||
if (!this->m_model)
|
||||
return;
|
||||
|
||||
m_VtkAsm = ::vtkAssembly::New();
|
||||
m_VtkAsm = vtkSmartPointer<::vtkAssembly>::New();
|
||||
this->SetProp(m_VtkAsm);
|
||||
|
||||
vtkNew<vtkCylinderSource> cylinder;
|
||||
@@ -112,7 +98,7 @@ void Cylinder::InstallPipe() {
|
||||
cylinder->SetHeight(1.0);
|
||||
cylinder->SetResolution(32);
|
||||
|
||||
m_Actor = vtkActor::New();
|
||||
m_Actor = vtkSmartPointer<vtkActor>::New();
|
||||
vtkNew<vtkTransform> alignment;
|
||||
m_Actor->SetUserTransform(alignment);
|
||||
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
#ifndef U_VTKCYLINDER_H
|
||||
#define U_VTKCYLINDER_H
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/Cylinder.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
#include <vtkActor.h>
|
||||
#include <vtkSmartPointer.h>
|
||||
class vtkAssembly;
|
||||
|
||||
namespace uLib {
|
||||
@@ -41,7 +43,7 @@ namespace Vtk {
|
||||
* mathematical state of a Cylinder object. It manages the alignment
|
||||
* between VTK's Y-centered cylinder and uLib's Z-based coordinate system.
|
||||
*/
|
||||
class Cylinder : public Prop3D {
|
||||
class Cylinder : public Prop3D, public uLib::ObjectWrapper<uLib::Cylinder> {
|
||||
typedef uLib::Cylinder Content;
|
||||
|
||||
public:
|
||||
@@ -52,17 +54,18 @@ public:
|
||||
virtual void Update() override;
|
||||
|
||||
/** Synchronizes the uLib model matrix with the VTK actor specifically for gizmo interactions */
|
||||
virtual void SyncFromVtk() override;
|
||||
|
||||
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
|
||||
|
||||
virtual uLib::Object *GetContent() const override {
|
||||
return (uLib::Object *)m_model.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Sets up the VTK visualization pipeline */
|
||||
virtual void InstallPipe();
|
||||
|
||||
vtkActor *m_Actor;
|
||||
::vtkAssembly *m_VtkAsm;
|
||||
Content *m_Content;
|
||||
vtkSmartPointer<vtkActor> m_Actor;
|
||||
vtkSmartPointer<::vtkAssembly> m_VtkAsm;
|
||||
uLib::Connection m_UpdateSignal;
|
||||
};
|
||||
|
||||
|
||||
@@ -58,22 +58,22 @@ void QuadMesh::vtk2uLib_update() {
|
||||
<< "number of quads = " << number_of_quads << "\n"
|
||||
<< "//////\n";
|
||||
|
||||
m_content.Points().clear();
|
||||
this->m_model->Points().clear();
|
||||
for (int i = 0; i < number_of_points; ++i) {
|
||||
double *point = m_Poly->GetPoint(i);
|
||||
m_content.Points().push_back(Vector3f(point[0], point[1], point[2]));
|
||||
this->m_model->Points().push_back(Vector3f(point[0], point[1], point[2]));
|
||||
}
|
||||
|
||||
m_content.Quads().resize(number_of_quads);
|
||||
this->m_model->Quads().resize(number_of_quads);
|
||||
m_Poly->GetPolys()->InitTraversal();
|
||||
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New();
|
||||
for (int i = 0; i < number_of_quads; ++i) {
|
||||
m_Poly->GetPolys()->GetNextCell(idList);
|
||||
if (idList->GetNumberOfIds() == 4) {
|
||||
m_content.Quads()[i](0) = idList->GetId(0);
|
||||
m_content.Quads()[i](1) = idList->GetId(1);
|
||||
m_content.Quads()[i](2) = idList->GetId(2);
|
||||
m_content.Quads()[i](3) = idList->GetId(3);
|
||||
this->m_model->Quads()[i](0) = idList->GetId(0);
|
||||
this->m_model->Quads()[i](1) = idList->GetId(1);
|
||||
this->m_model->Quads()[i](2) = idList->GetId(2);
|
||||
this->m_model->Quads()[i](3) = idList->GetId(3);
|
||||
}
|
||||
}
|
||||
m_Poly->Modified();
|
||||
@@ -81,23 +81,23 @@ void QuadMesh::vtk2uLib_update() {
|
||||
}
|
||||
|
||||
void QuadMesh::uLib2vtk_update() {
|
||||
vtkIdType number_of_points = m_content.Points().size();
|
||||
vtkIdType number_of_quads = m_content.Quads().size();
|
||||
vtkIdType number_of_points = this->m_model->Points().size();
|
||||
vtkIdType number_of_quads = this->m_model->Quads().size();
|
||||
|
||||
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
|
||||
points->SetNumberOfPoints(number_of_points);
|
||||
for (vtkIdType i = 0; i < number_of_points; i++) {
|
||||
Vector3f p = m_content.Points().at(i);
|
||||
Vector3f p = this->m_model->Points().at(i);
|
||||
points->SetPoint(i, p(0), p(1), p(2));
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
|
||||
for (vtkIdType i = 0; i < number_of_quads; i++) {
|
||||
vtkIdType a, b, c, d;
|
||||
a = m_content.Quads().at(i)(0);
|
||||
b = m_content.Quads().at(i)(1);
|
||||
c = m_content.Quads().at(i)(2);
|
||||
d = m_content.Quads().at(i)(3);
|
||||
a = this->m_model->Quads().at(i)(0);
|
||||
b = this->m_model->Quads().at(i)(1);
|
||||
c = this->m_model->Quads().at(i)(2);
|
||||
d = this->m_model->Quads().at(i)(3);
|
||||
polys->InsertNextCell(4);
|
||||
polys->InsertCellPoint(a);
|
||||
polys->InsertCellPoint(b);
|
||||
@@ -118,7 +118,7 @@ void QuadMesh::contentUpdate() {
|
||||
vmat = mat;
|
||||
}
|
||||
|
||||
Matrix4f transform = m_content.GetWorldMatrix();
|
||||
Matrix4f transform = this->m_model->GetWorldMatrix();
|
||||
Matrix4fToVtk(transform, vmat);
|
||||
|
||||
uLib2vtk_update();
|
||||
@@ -133,30 +133,30 @@ void QuadMesh::Update() {
|
||||
if (!vmat) return;
|
||||
|
||||
Matrix4f transform = VtkToMatrix4f(vmat);
|
||||
m_content.SetMatrix(transform);
|
||||
m_content.Updated();
|
||||
this->m_model->SetMatrix(transform);
|
||||
this->m_model->Updated();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
QuadMesh::QuadMesh(QuadMesh::Content &content)
|
||||
: m_content(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
|
||||
QuadMesh::QuadMesh(QuadMesh::Content *content)
|
||||
: ObjectWrapper(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper =
|
||||
vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||
mapper->SetInputData(m_Poly);
|
||||
m_Actor->SetMapper(mapper);
|
||||
|
||||
vtkNew<vtkMatrix4x4> vmat;
|
||||
Matrix4fToVtk(m_content.GetWorldMatrix(), vmat);
|
||||
Matrix4fToVtk(this->m_model->GetWorldMatrix(), vmat);
|
||||
m_Actor->SetUserMatrix(vmat);
|
||||
|
||||
this->SetProp(m_Actor);
|
||||
Object::connect(&m_content, &Content::Updated, this, &QuadMesh::contentUpdate);
|
||||
Object::connect(this->m_model.get(), &Content::Updated, this, &QuadMesh::contentUpdate);
|
||||
this->contentUpdate();
|
||||
}
|
||||
|
||||
QuadMesh::~QuadMesh() {
|
||||
Object::disconnect(&m_content, &Content::Updated, this, &QuadMesh::contentUpdate);
|
||||
Object::disconnect(this->m_model.get(), &Content::Updated, this, &QuadMesh::contentUpdate);
|
||||
m_Poly->Delete();
|
||||
m_Actor->Delete();
|
||||
}
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
#ifndef VTKQUADMESH_H
|
||||
#define VTKQUADMESH_H
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/QuadMesh.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
#include "Vtk/Math/vtkPolydata.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
|
||||
class vtkPolyData;
|
||||
class vtkActor;
|
||||
@@ -36,11 +37,13 @@ class vtkActor;
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
class QuadMesh : public Prop3D, public Polydata {
|
||||
class QuadMesh : public Prop3D,
|
||||
public Polydata,
|
||||
public uLib::ObjectWrapper<uLib::QuadMesh> {
|
||||
typedef uLib::QuadMesh Content;
|
||||
|
||||
public:
|
||||
QuadMesh(Content &content);
|
||||
QuadMesh(Content *content);
|
||||
~QuadMesh();
|
||||
|
||||
void ReadFromFile(const char *filename);
|
||||
@@ -51,17 +54,20 @@ public:
|
||||
|
||||
void ReadFromStlFile(const char *filename);
|
||||
|
||||
virtual class vtkPolyData *GetPolyData() const;
|
||||
vtkPolyData *GetPolyData() const override;
|
||||
|
||||
virtual void contentUpdate();
|
||||
|
||||
virtual void Update();
|
||||
void Update() override;
|
||||
uLib::Object *GetContent() const override {
|
||||
return (uLib::Object *)m_model.get();
|
||||
}
|
||||
|
||||
private:
|
||||
void vtk2uLib_update();
|
||||
void uLib2vtk_update();
|
||||
|
||||
uLib::QuadMesh &m_content;
|
||||
// ObjectWrapper provides m_model
|
||||
vtkPolyData *m_Poly;
|
||||
vtkActor *m_Actor;
|
||||
};
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace Vtk {
|
||||
////// VTK STRUCTURED GRID /////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StructuredGrid::StructuredGrid(Content &content)
|
||||
: m_Content(&content), m_Actor(vtkActor::New()),
|
||||
StructuredGrid::StructuredGrid(Content *content)
|
||||
: ObjectWrapper(content), m_Actor(vtkActor::New()),
|
||||
m_Transform(vtkTransform::New()) {
|
||||
|
||||
this->InstallPipe();
|
||||
@@ -54,10 +54,10 @@ StructuredGrid::~StructuredGrid() {
|
||||
void StructuredGrid::SetTransform(vtkTransform *t) {
|
||||
vtkMatrix4x4 *vmat = t->GetMatrix();
|
||||
Matrix4f mat = VtkToMatrix4f(vmat);
|
||||
m_Content->SetMatrix(mat);
|
||||
this->m_model->SetMatrix(mat);
|
||||
|
||||
vtkSmartPointer<vtkMatrix4x4> vmat2 = vtkSmartPointer<vtkMatrix4x4>::New();
|
||||
mat = m_Content->GetWorldMatrix();
|
||||
mat = this->m_model->GetWorldMatrix();
|
||||
Matrix4fToVtk(mat, vmat2);
|
||||
m_Transform->SetMatrix(vmat2);
|
||||
m_Transform->Update();
|
||||
@@ -65,7 +65,7 @@ void StructuredGrid::SetTransform(vtkTransform *t) {
|
||||
}
|
||||
|
||||
void StructuredGrid::Update() {
|
||||
if (!m_Content) return;
|
||||
if (!this->m_model) return;
|
||||
|
||||
vtkProp3D* actor = vtkProp3D::SafeDownCast(this->GetProp());
|
||||
if (!actor) return;
|
||||
@@ -76,20 +76,20 @@ void StructuredGrid::Update() {
|
||||
Matrix4f transform = VtkToMatrix4f(vmat);
|
||||
|
||||
// Update uLib model's affine transform
|
||||
if (m_Content->GetParent()) {
|
||||
Matrix4f localT = m_Content->GetParent()->GetWorldMatrix().inverse() * transform;
|
||||
m_Content->SetMatrix(localT);
|
||||
if (this->m_model->GetParent()) {
|
||||
Matrix4f localT = this->m_model->GetParent()->GetWorldMatrix().inverse() * transform;
|
||||
this->m_model->SetMatrix(localT);
|
||||
} else {
|
||||
m_Content->SetMatrix(transform);
|
||||
this->m_model->SetMatrix(transform);
|
||||
}
|
||||
|
||||
m_Content->Updated(); // Notify others (like raytracer)
|
||||
this->m_model->Updated(); // Notify others (like raytracer)
|
||||
}
|
||||
|
||||
void StructuredGrid::InstallPipe() {
|
||||
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
|
||||
|
||||
Vector3i dims = m_Content->GetDims();
|
||||
Vector3i dims = this->m_model->GetDims();
|
||||
cube->SetBounds(0, dims(0), 0, dims(1), 0, dims(2));
|
||||
cube->Update();
|
||||
|
||||
@@ -104,7 +104,7 @@ void StructuredGrid::InstallPipe() {
|
||||
m_Actor->GetProperty()->SetAmbient(0.7);
|
||||
|
||||
vtkNew<vtkMatrix4x4> vmat;
|
||||
Matrix4fToVtk(m_Content->GetWorldMatrix(), vmat);
|
||||
Matrix4fToVtk(this->m_model->GetWorldMatrix(), vmat);
|
||||
m_Actor->SetUserMatrix(vmat);
|
||||
|
||||
this->SetProp(m_Actor);
|
||||
|
||||
@@ -41,28 +41,32 @@
|
||||
|
||||
#include "Vtk/Math/vtkDense.h"
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/StructuredGrid.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
class StructuredGrid : public Prop3D {
|
||||
class StructuredGrid : public Prop3D,
|
||||
public uLib::ObjectWrapper<uLib::StructuredGrid> {
|
||||
typedef uLib::StructuredGrid Content;
|
||||
|
||||
public:
|
||||
StructuredGrid(Content &content);
|
||||
StructuredGrid(Content *content);
|
||||
~StructuredGrid();
|
||||
|
||||
void SetTransform(class vtkTransform *t);
|
||||
|
||||
virtual void Update() override;
|
||||
uLib::Object *GetContent() const override {
|
||||
return (uLib::Object *)m_model.get();
|
||||
}
|
||||
|
||||
private:
|
||||
void InstallPipe();
|
||||
|
||||
vtkActor *m_Actor;
|
||||
uLib::StructuredGrid *m_Content;
|
||||
vtkTransform *m_Transform;
|
||||
};
|
||||
|
||||
|
||||
@@ -58,42 +58,42 @@ void TriangleMesh::vtk2uLib_update() {
|
||||
<< "number of polys = " << number_of_triangles << "\n"
|
||||
<< "//////\n";
|
||||
|
||||
m_content.Points().clear();
|
||||
this->m_model->Points().clear();
|
||||
for (int i = 0; i < number_of_points; ++i) {
|
||||
double *point = m_Poly->GetPoint(i);
|
||||
m_content.Points().push_back(Vector3f(point[0], point[1], point[2]));
|
||||
this->m_model->Points().push_back(Vector3f(point[0], point[1], point[2]));
|
||||
}
|
||||
|
||||
m_content.Triangles().resize(number_of_triangles);
|
||||
this->m_model->Triangles().resize(number_of_triangles);
|
||||
m_Poly->GetPolys()->InitTraversal();
|
||||
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New();
|
||||
for (int i = 0; i < number_of_triangles; ++i) {
|
||||
m_Poly->GetPolys()->GetNextCell(idList);
|
||||
m_content.Triangles()[i](0) = idList->GetId(0);
|
||||
m_content.Triangles()[i](1) = idList->GetId(1);
|
||||
m_content.Triangles()[i](2) = idList->GetId(2);
|
||||
this->m_model->Triangles()[i](0) = idList->GetId(0);
|
||||
this->m_model->Triangles()[i](1) = idList->GetId(1);
|
||||
this->m_model->Triangles()[i](2) = idList->GetId(2);
|
||||
}
|
||||
m_Poly->Modified();
|
||||
m_Actor->GetMapper()->Update();
|
||||
}
|
||||
|
||||
void TriangleMesh::uLib2vtk_update() {
|
||||
vtkIdType number_of_points = m_content.Points().size();
|
||||
vtkIdType number_of_triangles = m_content.Triangles().size();
|
||||
vtkIdType number_of_points = this->m_model->Points().size();
|
||||
vtkIdType number_of_triangles = this->m_model->Triangles().size();
|
||||
|
||||
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
|
||||
points->SetNumberOfPoints(number_of_points);
|
||||
for (vtkIdType i = 0; i < number_of_points; i++) {
|
||||
Vector3f p = m_content.Points().at(i);
|
||||
Vector3f p = this->m_model->Points().at(i);
|
||||
points->SetPoint(i, p(0), p(1), p(2));
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
|
||||
for (vtkIdType i = 0; i < number_of_triangles; i++) {
|
||||
vtkIdType a, b, c;
|
||||
a = m_content.Triangles().at(i)(0);
|
||||
b = m_content.Triangles().at(i)(1);
|
||||
c = m_content.Triangles().at(i)(2);
|
||||
a = this->m_model->Triangles().at(i)(0);
|
||||
b = this->m_model->Triangles().at(i)(1);
|
||||
c = this->m_model->Triangles().at(i)(2);
|
||||
polys->InsertNextCell(3);
|
||||
polys->InsertCellPoint(a);
|
||||
polys->InsertCellPoint(b);
|
||||
@@ -113,7 +113,7 @@ void TriangleMesh::contentUpdate() {
|
||||
vmat = mat;
|
||||
}
|
||||
|
||||
Matrix4f transform = m_content.GetWorldMatrix();
|
||||
Matrix4f transform = this->m_model->GetWorldMatrix();
|
||||
Matrix4fToVtk(transform, vmat);
|
||||
|
||||
uLib2vtk_update();
|
||||
@@ -128,30 +128,30 @@ void TriangleMesh::Update() {
|
||||
if (!vmat) return;
|
||||
|
||||
Matrix4f transform = VtkToMatrix4f(vmat);
|
||||
m_content.SetMatrix(transform);
|
||||
m_content.Updated();
|
||||
this->m_model->SetMatrix(transform);
|
||||
this->m_model->Updated();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
TriangleMesh::TriangleMesh(TriangleMesh::Content &content)
|
||||
: m_content(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
|
||||
TriangleMesh::TriangleMesh(TriangleMesh::Content *content)
|
||||
: ObjectWrapper(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper =
|
||||
vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||
mapper->SetInputData(m_Poly);
|
||||
m_Actor->SetMapper(mapper);
|
||||
|
||||
vtkNew<vtkMatrix4x4> vmat;
|
||||
Matrix4fToVtk(m_content.GetWorldMatrix(), vmat);
|
||||
Matrix4fToVtk(this->m_model->GetWorldMatrix(), vmat);
|
||||
m_Actor->SetUserMatrix(vmat);
|
||||
|
||||
this->SetProp(m_Actor);
|
||||
Object::connect(&m_content, &Content::Updated, this, &TriangleMesh::contentUpdate);
|
||||
Object::connect(this->m_model.get(), &Content::Updated, this, &TriangleMesh::contentUpdate);
|
||||
this->contentUpdate();
|
||||
}
|
||||
|
||||
TriangleMesh::~TriangleMesh() {
|
||||
Object::disconnect(&m_content, &Content::Updated, this, &TriangleMesh::contentUpdate);
|
||||
Object::disconnect(this->m_model.get(), &Content::Updated, this, &TriangleMesh::contentUpdate);
|
||||
m_Poly->Delete();
|
||||
m_Actor->Delete();
|
||||
}
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
#ifndef VTKTRIANGLEMESH_H
|
||||
#define VTKTRIANGLEMESH_H
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Math/TriangleMesh.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
#include "Vtk/Math/vtkPolydata.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
|
||||
class vtkPolyData;
|
||||
class vtkActor;
|
||||
@@ -36,11 +37,13 @@ class vtkActor;
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
class TriangleMesh : public Prop3D, public Polydata {
|
||||
class TriangleMesh : public Prop3D,
|
||||
public Polydata,
|
||||
public uLib::ObjectWrapper<uLib::TriangleMesh> {
|
||||
typedef uLib::TriangleMesh Content;
|
||||
|
||||
public:
|
||||
TriangleMesh(Content &content);
|
||||
TriangleMesh(Content *content);
|
||||
~TriangleMesh();
|
||||
|
||||
void ReadFromFile(const char *filename);
|
||||
@@ -51,17 +54,20 @@ public:
|
||||
|
||||
void ReadFromStlFile(const char *filename);
|
||||
|
||||
virtual class vtkPolyData *GetPolyData() const;
|
||||
vtkPolyData *GetPolyData() const override;
|
||||
|
||||
virtual void contentUpdate();
|
||||
|
||||
virtual void Update();
|
||||
void Update() override;
|
||||
uLib::Object *GetContent() const override {
|
||||
return (uLib::Object *)m_model.get();
|
||||
}
|
||||
|
||||
private:
|
||||
void vtk2uLib_update();
|
||||
void uLib2vtk_update();
|
||||
|
||||
uLib::TriangleMesh &m_content;
|
||||
// ObjectWrapper provides m_model
|
||||
vtkPolyData *m_Poly;
|
||||
vtkActor *m_Actor;
|
||||
};
|
||||
|
||||
@@ -61,13 +61,13 @@ namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
void VoxImage::UpdateFromContent() {
|
||||
Vector3i ev_dims = m_Content.GetDims();
|
||||
Vector3i ev_dims = this->m_model->GetDims();
|
||||
m_Image->SetDimensions(ev_dims.data());
|
||||
|
||||
Vector3f ev_spacing = m_Content.GetSpacing();
|
||||
Vector3f ev_spacing = this->m_model->GetSpacing();
|
||||
m_Image->SetSpacing(ev_spacing(0), ev_spacing(1), ev_spacing(2));
|
||||
|
||||
Vector3f ev_pos = m_Content.GetPosition();
|
||||
Vector3f ev_pos = this->m_model->GetPosition();
|
||||
m_Image->SetOrigin(ev_pos(0), ev_pos(1), ev_pos(2));
|
||||
|
||||
vtkFloatArray *array =
|
||||
@@ -78,14 +78,14 @@ void VoxImage::UpdateFromContent() {
|
||||
array->Delete();
|
||||
}
|
||||
|
||||
array->SetNumberOfTuples(m_Content.GetDims().prod());
|
||||
array->SetNumberOfTuples(this->m_model->GetDims().prod());
|
||||
Vector3i index(0, 0, 0);
|
||||
int i = 0;
|
||||
for (int zv = 0; zv < ev_dims(2); ++zv) {
|
||||
for (int yv = 0; yv < ev_dims(1); ++yv) {
|
||||
for (int xv = 0; xv < ev_dims(0); ++xv) {
|
||||
index << xv, yv, zv;
|
||||
array->SetValue(i++, m_Content.GetValue(index));
|
||||
array->SetValue(i++, this->m_model->GetValue(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,13 +94,13 @@ void VoxImage::UpdateFromContent() {
|
||||
void VoxImage::UpdateToContent() {
|
||||
int *ext = m_Image->GetExtent();
|
||||
int dims[3] = {ext[1] - ext[0] + 1, ext[3] - ext[2] + 1, ext[5] - ext[4] + 1};
|
||||
m_Content.SetDims(Vector3i(dims[0], dims[1], dims[2]));
|
||||
this->m_model->SetDims(Vector3i(dims[0], dims[1], dims[2]));
|
||||
|
||||
double *spacing = m_Image->GetSpacing();
|
||||
m_Content.SetSpacing(Vector3f(spacing[0], spacing[1], spacing[2]));
|
||||
this->m_model->SetSpacing(Vector3f(spacing[0], spacing[1], spacing[2]));
|
||||
|
||||
double *pos = m_Image->GetOrigin();
|
||||
m_Content.SetPosition(Vector3f(pos[0], pos[1], pos[2]));
|
||||
this->m_model->SetPosition(Vector3f(pos[0], pos[1], pos[2]));
|
||||
|
||||
vtkFloatArray *array =
|
||||
vtkFloatArray::SafeDownCast(m_Image->GetPointData()->GetScalars());
|
||||
@@ -111,7 +111,7 @@ void VoxImage::UpdateToContent() {
|
||||
for (int yv = 0; yv < dims[1]; ++yv) {
|
||||
for (int xv = 0; xv < dims[0]; ++xv) {
|
||||
index << xv, yv, zv;
|
||||
m_Content.SetValue(index, array->GetValue(i++));
|
||||
this->m_model->SetValue(index, array->GetValue(i++));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,8 +124,8 @@ void VoxImage::UpdateToContent() {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// VTK VOXIMAGE
|
||||
|
||||
VoxImage::VoxImage(Content &content)
|
||||
: m_Content(content), m_Actor(vtkVolume::New()),
|
||||
VoxImage::VoxImage(Content *content)
|
||||
: ObjectWrapper(content), m_Actor(vtkVolume::New()),
|
||||
m_Asm(vtkAssembly::New()),
|
||||
m_Image(vtkImageData::New()), m_Outline(vtkCubeSource::New()),
|
||||
m_OutlineActor(vtkActor::New()),
|
||||
@@ -134,7 +134,7 @@ VoxImage::VoxImage(Content &content)
|
||||
// Transfer functions
|
||||
m_ColorFun = vtkColorTransferFunction::New();
|
||||
m_OpacityFun = vtkPiecewiseFunction::New();
|
||||
m_UpdateConnection = Object::connect(&m_Content, &uLib::Object::Updated, this, &VoxImage::Update);
|
||||
m_UpdateConnection = Object::connect(this->m_model.get(), &uLib::Object::Updated, this, &VoxImage::Update);
|
||||
|
||||
UpdateFromContent();
|
||||
InstallPipe();
|
||||
@@ -308,26 +308,16 @@ void VoxImage::serialize_display(uLib::Archive::display_properties_archive & ar,
|
||||
{"MIP", "Composite", "Composite Shaded", "MIP Bone", "MIP Hot", "Additive"});
|
||||
}
|
||||
|
||||
void VoxImage::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 VoxImage::Update() {
|
||||
if (auto *root = vtkProp3D::SafeDownCast(this->GetProp())) {
|
||||
vtkNew<vtkMatrix4x4> m;
|
||||
Matrix4fToVtk(m_Content.GetMatrix(), m); // * m_Content.GetLocalMatrix(), m);
|
||||
Matrix4fToVtk(this->m_model->GetMatrix(), m); // * this->m_model->GetLocalMatrix(), m);
|
||||
root->SetUserMatrix(m);
|
||||
root->Modified();
|
||||
// std::cout << "[VoxImage::Update] Set Proxy UserMatrix:" << std::endl;
|
||||
// std::cout << m_Content.GetMatrix() << std::endl;
|
||||
// std::cout << this->m_model->GetMatrix() << std::endl;
|
||||
}
|
||||
setShadingPreset(m_ShadingPreset);
|
||||
m_Actor->Update();
|
||||
|
||||
@@ -26,15 +26,16 @@
|
||||
#ifndef U_VTKVOXIMAGE_H
|
||||
#define U_VTKVOXIMAGE_H
|
||||
|
||||
#include <vtkAssembly.h>
|
||||
#include <vtkCubeSource.h>
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkVolume.h>
|
||||
#include <vtkXMLImageDataReader.h>
|
||||
#include <vtkXMLImageDataWriter.h>
|
||||
#include <vtkAssembly.h>
|
||||
|
||||
#include <Math/VoxImage.h>
|
||||
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
|
||||
class vtkImageData;
|
||||
@@ -45,18 +46,21 @@ class vtkPiecewiseFunction;
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
class VoxImage : public Prop3D {
|
||||
class VoxImage : public Prop3D,
|
||||
public uLib::ObjectWrapper<uLib::Abstract::VoxImage> {
|
||||
public:
|
||||
typedef Abstract::VoxImage Content;
|
||||
|
||||
VoxImage(Content &content);
|
||||
VoxImage(Content *content);
|
||||
|
||||
~VoxImage();
|
||||
|
||||
void UpdateFromContent();
|
||||
|
||||
void UpdateToContent();
|
||||
uLib::Object* GetContent() const override { return (uLib::Object*)&m_Content; }
|
||||
uLib::Object *GetContent() const override {
|
||||
return (uLib::Object *)m_model.get();
|
||||
}
|
||||
|
||||
vtkProp3D *GetProp() override { return m_Asm; }
|
||||
|
||||
@@ -73,8 +77,9 @@ public:
|
||||
void RescaleShaderRange();
|
||||
|
||||
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:
|
||||
void InstallPipe();
|
||||
@@ -90,7 +95,7 @@ private:
|
||||
vtkXMLImageDataReader *m_Reader;
|
||||
vtkXMLImageDataWriter *m_Writer;
|
||||
|
||||
VoxImage::Content &m_Content;
|
||||
// ObjectWrapper provides m_model
|
||||
|
||||
float m_Window;
|
||||
float m_Level;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
#include "Core/Property.h"
|
||||
#include "Math/Dense.h"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "testing-prototype.h"
|
||||
@@ -33,7 +34,7 @@ int main() {
|
||||
|
||||
// Verify specific properties exist
|
||||
Property<double>* opacityProp = nullptr;
|
||||
Property<double>* colorRProp = nullptr;
|
||||
Property<Vector3d>* colorProp = nullptr;
|
||||
|
||||
for (auto* prop : props) {
|
||||
std::cout << " - [" << prop->GetTypeName() << "] " << prop->GetName()
|
||||
@@ -42,13 +43,13 @@ int main() {
|
||||
if (prop->GetName() == "Opacity") {
|
||||
opacityProp = dynamic_cast<Property<double>*>(prop);
|
||||
}
|
||||
if (prop->GetName() == "ColorR") {
|
||||
colorRProp = dynamic_cast<Property<double>*>(prop);
|
||||
if (prop->GetName() == "Color") {
|
||||
colorProp = dynamic_cast<Property<Vector3d>*>(prop);
|
||||
}
|
||||
}
|
||||
|
||||
assert(opacityProp != nullptr && "Opacity property not registered!");
|
||||
assert(colorRProp != nullptr && "ColorR property not registered!");
|
||||
assert(colorProp != nullptr && "Color property not registered!");
|
||||
|
||||
// Test modification via uLib Property interface
|
||||
std::cout << "Modifying Opacity via property proxy (0.25)..." << std::endl;
|
||||
@@ -58,9 +59,9 @@ int main() {
|
||||
assert(opacityProp->Get() == 0.25);
|
||||
assert(opacityProp->GetValueAsString().find("0.25") != std::string::npos);
|
||||
|
||||
std::cout << "Modifying ColorR via property proxy (0.9)..." << std::endl;
|
||||
*colorRProp = 0.9;
|
||||
assert(colorRProp->Get() == 0.9);
|
||||
std::cout << "Modifying Color via property proxy (0.9, 0.1, 0.1)..." << std::endl;
|
||||
*colorProp = Vector3d(0.9, 0.1, 0.1);
|
||||
assert(colorProp->Get().x() == 0.9);
|
||||
|
||||
std::cout << "All Prop3D Property Registration Tests PASSED!" << std::endl;
|
||||
|
||||
|
||||
@@ -93,14 +93,28 @@ public:
|
||||
m_Selectable(true),
|
||||
m_Selected(false),
|
||||
m_Visibility(true),
|
||||
m_Dragable(true)
|
||||
m_Dragable(true),
|
||||
m_HighlightMode(Prop3D::HighlightPlain)
|
||||
{
|
||||
m_Color = Vector3d(-1, -1, -1);
|
||||
m_PrevMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
|
||||
m_PrevMatrix->Identity();
|
||||
}
|
||||
|
||||
~Prop3DData() {
|
||||
// No manual Delete needed for smart pointers
|
||||
|
||||
if (m_Renderers) {
|
||||
m_Renderers->InitTraversal();
|
||||
for (int i = 0; i < m_Renderers->GetNumberOfItems(); ++i) {
|
||||
vtkRenderer* ren = m_Renderers->GetNextItem();
|
||||
if (ren) {
|
||||
if (m_Prop) ren->RemoveViewProp(m_Prop);
|
||||
if (m_OutlineActor) ren->RemoveActor(m_OutlineActor);
|
||||
if (m_CubeAxesActor) ren->RemoveActor(m_CubeAxesActor);
|
||||
if (m_HighlightActor) ren->RemoveActor(m_HighlightActor);
|
||||
}
|
||||
}
|
||||
m_Renderers->RemoveAllItems();
|
||||
}
|
||||
}
|
||||
|
||||
Prop3D *m_Prop3D;
|
||||
@@ -125,6 +139,10 @@ public:
|
||||
bool m_Visibility;
|
||||
bool m_Dragable;
|
||||
|
||||
int m_HighlightMode; // 0: Plain, 1: Corners
|
||||
|
||||
vtkSmartPointer<vtkMatrix4x4> m_PrevMatrix;
|
||||
|
||||
//
|
||||
TRS m_Transform;
|
||||
|
||||
@@ -212,39 +230,71 @@ public:
|
||||
}
|
||||
|
||||
if (!m_HighlightActor) {
|
||||
m_HighlightActor = vtkSmartPointer<vtkActor>::New();
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||
m_HighlightActor->SetMapper(mapper);
|
||||
m_HighlightActor->GetProperty()->SetRepresentationToWireframe();
|
||||
m_HighlightActor->GetProperty()->SetColor(1.0, 0.0, 0.0); // Red
|
||||
m_HighlightActor->GetProperty()->SetLineWidth(2.0);
|
||||
m_HighlightActor->GetProperty()->SetLighting(0);
|
||||
}
|
||||
|
||||
if (m_HighlightMode == Prop3D::HighlightPlain) {
|
||||
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
|
||||
double bounds[6];
|
||||
polydata->GetBounds(bounds);
|
||||
// Add a small padding to prevent z-fighting
|
||||
double maxDim = std::max({bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4]});
|
||||
double pad = maxDim * 0.02;
|
||||
if(pad < 1e-4) pad = 0.05;
|
||||
cube->SetBounds(bounds[0]-pad, bounds[1]+pad,
|
||||
bounds[2]-pad, bounds[3]+pad,
|
||||
bounds[4]-pad, bounds[5]+pad);
|
||||
|
||||
m_HighlightActor = vtkSmartPointer<vtkActor>::New();
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
|
||||
mapper->SetInputConnection(cube->GetOutputPort());
|
||||
m_HighlightActor->SetMapper(mapper);
|
||||
m_HighlightActor->GetProperty()->SetRepresentationToWireframe();
|
||||
m_HighlightActor->GetProperty()->SetColor(1.0, 0.0, 0.0); // Red
|
||||
m_HighlightActor->GetProperty()->SetLineWidth(2.0);
|
||||
m_HighlightActor->GetProperty()->SetLighting(0);
|
||||
cube->Update();
|
||||
m_HighlightActor->GetMapper()->SetInputConnection(cube->GetOutputPort());
|
||||
} else {
|
||||
if (auto* mapper = vtkPolyDataMapper::SafeDownCast(m_HighlightActor->GetMapper())) {
|
||||
if (auto* cube = vtkCubeSource::SafeDownCast(mapper->GetInputAlgorithm())) {
|
||||
double bounds[6];
|
||||
polydata->GetBounds(bounds);
|
||||
double maxDim = std::max({bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4]});
|
||||
double pad = maxDim * 0.02;
|
||||
if(pad < 1e-4) pad = 0.05;
|
||||
cube->SetBounds(bounds[0]-pad, bounds[1]+pad,
|
||||
bounds[2]-pad, bounds[3]+pad,
|
||||
bounds[4]-pad, bounds[5]+pad);
|
||||
cube->Modified();
|
||||
// Corners mode logic
|
||||
double bounds[6];
|
||||
polydata->GetBounds(bounds);
|
||||
double maxDim = std::max({bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4]});
|
||||
double pad = maxDim * 0.02;
|
||||
if(pad < 1e-4) pad = 0.05;
|
||||
|
||||
double b[6] = {bounds[0]-pad, bounds[1]+pad, bounds[2]-pad, bounds[3]+pad, bounds[4]-pad, bounds[5]+pad};
|
||||
|
||||
vtkNew<vtkPoints> points;
|
||||
vtkNew<vtkCellArray> lines;
|
||||
|
||||
float len[3] = {
|
||||
(float)(b[1] - b[0]) * 0.15f,
|
||||
(float)(b[3] - b[2]) * 0.15f,
|
||||
(float)(b[5] - b[4]) * 0.15f
|
||||
};
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
double p[3];
|
||||
p[0] = b[(i & 1) ? 1 : 0];
|
||||
p[1] = b[(i & 2) ? 1 : 0];
|
||||
p[2] = b[(i & 4) ? 1 : 0];
|
||||
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
double p2[3] = {p[0], p[1], p[2]};
|
||||
double delta = (i & (1 << axis)) ? -len[axis] : len[axis];
|
||||
p2[axis] += delta;
|
||||
|
||||
vtkIdType id1 = points->InsertNextPoint(p);
|
||||
vtkIdType id2 = points->InsertNextPoint(p2);
|
||||
lines->InsertNextCell(2);
|
||||
lines->InsertCellPoint(id1);
|
||||
lines->InsertCellPoint(id2);
|
||||
}
|
||||
}
|
||||
|
||||
vtkNew<vtkPolyData> cornerPoly;
|
||||
cornerPoly->SetPoints(points);
|
||||
cornerPoly->SetLines(lines);
|
||||
if (auto* mapper = vtkPolyDataMapper::SafeDownCast(m_HighlightActor->GetMapper())) {
|
||||
mapper->SetInputData(cornerPoly);
|
||||
}
|
||||
}
|
||||
|
||||
// Update highlight matrix from the model world matrix
|
||||
@@ -254,6 +304,7 @@ public:
|
||||
vtkNew<vtkMatrix4x4> vwm;
|
||||
Matrix4fToVtk(tr->GetWorldMatrix(), vwm);
|
||||
m_HighlightActor->SetUserMatrix(vwm);
|
||||
m_PrevMatrix->DeepCopy(vwm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,9 +341,6 @@ public:
|
||||
|
||||
Prop3D::Prop3D() : Object(), pd(new Prop3DData(this)) {
|
||||
ULIB_ACTIVATE_DISPLAY_PROPERTIES;
|
||||
for (auto* p : this->GetDisplayProperties()) {
|
||||
uLib::Object::connect(p, &uLib::PropertyBase::Updated, this, &Prop3D::Update);
|
||||
}
|
||||
}
|
||||
|
||||
Prop3D::~Prop3D()
|
||||
@@ -517,6 +565,12 @@ void Prop3D::SetRepresentation(const char *mode)
|
||||
else if (s == "slice") SetRepresentation(Slice);
|
||||
}
|
||||
|
||||
void Prop3D::SetHighlightMode(HighlightMode mode)
|
||||
{
|
||||
pd->m_HighlightMode = static_cast<int>(mode);
|
||||
pd->UpdateHighlight();
|
||||
}
|
||||
|
||||
void Prop3D::SetColor(double r, double g, double b)
|
||||
{
|
||||
pd->m_Color[0] = r;
|
||||
@@ -531,6 +585,18 @@ void Prop3D::SetOpacity(double alpha)
|
||||
pd->ApplyAppearance(pd->m_Prop);
|
||||
}
|
||||
|
||||
void Prop3D::GetColor(double &r, double &g, double &b) const
|
||||
{
|
||||
r = pd->m_Color[0];
|
||||
g = pd->m_Color[1];
|
||||
b = pd->m_Color[2];
|
||||
}
|
||||
|
||||
double Prop3D::GetOpacity() const
|
||||
{
|
||||
return pd->m_Opacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -570,6 +636,7 @@ void Prop3D::ApplyProp3DTransform(vtkProp3D* prop)
|
||||
Matrix4fToVtk(tr->GetMatrix(), m);
|
||||
prop->SetUserMatrix(m);
|
||||
prop->Modified();
|
||||
pd->m_PrevMatrix->DeepCopy(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -580,7 +647,18 @@ void Prop3D::SyncFromVtk()
|
||||
if (auto* tr = dynamic_cast<uLib::TRS*>(content)) {
|
||||
if (auto* proxy = this->GetProxyProp()) {
|
||||
if (vtkMatrix4x4* mat = proxy->GetUserMatrix()) {
|
||||
tr->FromMatrix(VtkToMatrix4f(mat));
|
||||
// Calculate Delta: currentMatrix * Inv(m_PrevMatrix)
|
||||
vtkNew<vtkMatrix4x4> invPrev;
|
||||
vtkMatrix4x4::Invert(pd->m_PrevMatrix, invPrev);
|
||||
|
||||
vtkNew<vtkMatrix4x4> delta;
|
||||
vtkMatrix4x4::Multiply4x4(mat, invPrev, delta);
|
||||
|
||||
// Apply delta to world matrix
|
||||
Matrix4f nextWorldMatrix = VtkToMatrix4f(delta) * tr->GetWorldMatrix();
|
||||
tr->SetWorldMatrix(nextWorldMatrix);
|
||||
|
||||
pd->m_PrevMatrix->DeepCopy(mat);
|
||||
content->Updated();
|
||||
}
|
||||
}
|
||||
@@ -658,6 +736,8 @@ struct AppearanceProxy {
|
||||
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);
|
||||
ar & boost::serialization::make_hrp_enum("HighlightMode",
|
||||
pd->m_HighlightMode, {"Plain", "Corners"});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -86,8 +86,10 @@ public:
|
||||
void DisonnectViewer(Viewer *viewer);
|
||||
|
||||
void SetColor(double r, double g, double b);
|
||||
void GetColor(double &r, double &g, double &b) const;
|
||||
|
||||
void SetOpacity(double alpha);
|
||||
double GetOpacity() const;
|
||||
|
||||
void SetSelectable(bool selectable = true);
|
||||
bool IsSelectable() const;
|
||||
@@ -123,6 +125,12 @@ public:
|
||||
void SetRepresentation(Representation mode);
|
||||
void SetRepresentation(const char *mode);
|
||||
|
||||
enum HighlightMode {
|
||||
HighlightPlain = 0,
|
||||
HighlightCorners = 1
|
||||
};
|
||||
void SetHighlightMode(HighlightMode mode);
|
||||
|
||||
virtual void PrintSelf(std::ostream &o) const;
|
||||
|
||||
void ShowBoundingBox(bool show);
|
||||
@@ -222,7 +230,7 @@ public:
|
||||
|
||||
m_Prop3D->RegisterDisplayProperty(p);
|
||||
Vtk::Prop3D *prop3d = m_Prop3D;
|
||||
uLib::Object::connect(p, &uLib::PropertyBase::Updated,
|
||||
uLib::Object::connect(p, &uLib::Object::Updated,
|
||||
[prop3d]() { prop3d->Update(); });
|
||||
}
|
||||
}
|
||||
@@ -236,7 +244,7 @@ public:
|
||||
t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
m_Prop3D->RegisterDisplayProperty(p);
|
||||
Vtk::Prop3D *prop3d = m_Prop3D;
|
||||
uLib::Object::connect(p, &uLib::PropertyBase::Updated,
|
||||
uLib::Object::connect(p, &uLib::Object::Updated,
|
||||
[prop3d]() { prop3d->Update(); });
|
||||
}
|
||||
}
|
||||
@@ -295,7 +303,7 @@ public:
|
||||
if (m_Prop3D) {
|
||||
m_Prop3D->RegisterDisplayProperty(&p);
|
||||
Vtk::Prop3D *prop3d = m_Prop3D;
|
||||
uLib::Object::connect(&p, &uLib::PropertyBase::Updated,
|
||||
uLib::Object::connect(&p, &uLib::Object::Updated,
|
||||
[prop3d]() { prop3d->Update(); });
|
||||
}
|
||||
}
|
||||
|
||||
175
src/Vtk/vtkMultiSelectionProp.cpp
Normal file
175
src/Vtk/vtkMultiSelectionProp.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "uLibVtkInterface.h"
|
||||
#include "vtkMultiSelectionProp.h"
|
||||
#include <vtkActor.h>
|
||||
#include <vtkPolyDataMapper.h>
|
||||
#include <vtkCubeSource.h>
|
||||
#include <vtkProperty.h>
|
||||
#include <vtkMatrix4x4.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkRendererCollection.h>
|
||||
#include "Math/Transform.h"
|
||||
#include "Vtk/Math/vtkDense.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
MultiSelectionProp::MultiSelectionProp() : Prop3D() {
|
||||
((::uLib::Object*)this)->SetInstanceName("Selection Group");
|
||||
m_PrevMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
|
||||
m_GroupHighlightActor = vtkSmartPointer<vtkActor>::New();
|
||||
|
||||
vtkNew<vtkPolyDataMapper> mapper;
|
||||
m_GroupHighlightActor->SetMapper(mapper);
|
||||
m_GroupHighlightActor->GetProperty()->SetRepresentationToWireframe();
|
||||
m_GroupHighlightActor->GetProperty()->SetLineWidth(2.0);
|
||||
m_GroupHighlightActor->GetProperty()->SetLighting(0);
|
||||
m_GroupHighlightActor->PickableOff();
|
||||
|
||||
// Set default display color in Prop3D state
|
||||
((Prop3D*)this)->SetColor(0.0, 1.0, 0.0);
|
||||
}
|
||||
|
||||
MultiSelectionProp::~MultiSelectionProp() {
|
||||
}
|
||||
|
||||
MultiSelectionProp* MultiSelectionProp::Clone() const {
|
||||
auto* copy = new MultiSelectionProp();
|
||||
copy->SetMembers(this->m_Members);
|
||||
((::uLib::Object*)copy)->SetInstanceName(((::uLib::Object*)this)->GetInstanceName());
|
||||
return copy;
|
||||
}
|
||||
|
||||
void MultiSelectionProp::SetMembers(const std::vector<Prop3D*>& members) {
|
||||
m_Members = members;
|
||||
Update();
|
||||
|
||||
// Reset prev matrix to current highlight position
|
||||
if (m_GroupHighlightActor->GetUserMatrix()) {
|
||||
m_PrevMatrix->DeepCopy(m_GroupHighlightActor->GetUserMatrix());
|
||||
} else {
|
||||
m_PrevMatrix->Identity();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSelectionProp::Update() {
|
||||
if (m_Members.empty()) {
|
||||
m_GroupHighlightActor->VisibilityOff();
|
||||
return;
|
||||
}
|
||||
m_GroupHighlightActor->VisibilityOn();
|
||||
double combinedBounds[6] = {VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
|
||||
VTK_DOUBLE_MAX, VTK_DOUBLE_MIN,
|
||||
VTK_DOUBLE_MAX, VTK_DOUBLE_MIN};
|
||||
|
||||
for (auto* member : m_Members) {
|
||||
if (vtkProp* prop = member->GetProp()) {
|
||||
double* b = prop->GetBounds();
|
||||
if (b) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (b[2*i] < combinedBounds[2*i]) combinedBounds[2*i] = b[2*i];
|
||||
if (b[2*i+1] > combinedBounds[2*i+1]) combinedBounds[2*i+1] = b[2*i+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (combinedBounds[0] > combinedBounds[1]) return;
|
||||
|
||||
vtkNew<vtkCubeSource> cube;
|
||||
double maxDim = std::max({combinedBounds[1]-combinedBounds[0],
|
||||
combinedBounds[3]-combinedBounds[2],
|
||||
combinedBounds[5]-combinedBounds[4]});
|
||||
double pad = maxDim * 0.02;
|
||||
if (pad < 1e-4) pad = 0.05;
|
||||
|
||||
cube->SetBounds(combinedBounds[0]-pad, combinedBounds[1]+pad,
|
||||
combinedBounds[2]-pad, combinedBounds[3]+pad,
|
||||
combinedBounds[4]-pad, combinedBounds[5]+pad);
|
||||
cube->Update();
|
||||
|
||||
if (auto* mapper = vtkPolyDataMapper::SafeDownCast(m_GroupHighlightActor->GetMapper())) {
|
||||
mapper->SetInputConnection(cube->GetOutputPort());
|
||||
}
|
||||
|
||||
// Apply TRS from m_SelectionTransform
|
||||
vtkNew<vtkMatrix4x4> trsMatrix;
|
||||
Matrix4fToVtk(m_SelectionTransform.GetWorldMatrix(), trsMatrix);
|
||||
m_GroupHighlightActor->SetUserMatrix(trsMatrix);
|
||||
|
||||
// Apply Display Properties (Color, Opacity)
|
||||
double r, g, b;
|
||||
((Prop3D*)this)->GetColor(r, g, b);
|
||||
m_GroupHighlightActor->GetProperty()->SetColor(r, g, b);
|
||||
m_GroupHighlightActor->GetProperty()->SetOpacity(((Prop3D*)this)->GetOpacity());
|
||||
|
||||
// Update Prev Matrix for delta calculations
|
||||
m_PrevMatrix->DeepCopy(trsMatrix);
|
||||
|
||||
// Ensure it's in the renderers
|
||||
vtkRendererCollection* rens = ((Prop3D*)this)->GetRenderers();
|
||||
rens->InitTraversal();
|
||||
for (int i = 0; i < rens->GetNumberOfItems(); ++i) {
|
||||
vtkRenderer* ren = rens->GetNextItem();
|
||||
ren->AddActor(m_GroupHighlightActor);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSelectionProp::SyncFromVtk() {
|
||||
if (m_Members.empty()) return;
|
||||
|
||||
vtkMatrix4x4* currentMatrix = m_GroupHighlightActor->GetUserMatrix();
|
||||
if (!currentMatrix) return;
|
||||
|
||||
// Calculate Delta: currentMatrix * Inv(m_PrevMatrix)
|
||||
vtkNew<vtkMatrix4x4> invPrev;
|
||||
vtkMatrix4x4::Invert(m_PrevMatrix, invPrev);
|
||||
|
||||
vtkNew<vtkMatrix4x4> delta;
|
||||
vtkMatrix4x4::Multiply4x4(currentMatrix, invPrev, delta);
|
||||
|
||||
// Apply delta to all members
|
||||
for (auto* member : m_Members) {
|
||||
if (auto* content = member->GetContent()) {
|
||||
if (auto* tr = dynamic_cast<uLib::TRS*>(content)) {
|
||||
vtkNew<vtkMatrix4x4> memberWorldMatrix;
|
||||
Matrix4fToVtk(tr->GetWorldMatrix(), memberWorldMatrix);
|
||||
|
||||
vtkNew<vtkMatrix4x4> nextWorldMatrix;
|
||||
vtkMatrix4x4::Multiply4x4(delta, memberWorldMatrix, nextWorldMatrix);
|
||||
|
||||
// Set the new world matrix.
|
||||
if (tr->GetParent()) {
|
||||
Matrix4f invParentWorld = tr->GetParent()->GetWorldMatrix().inverse();
|
||||
Matrix4f nextLocalMatrix = invParentWorld * VtkToMatrix4f(nextWorldMatrix);
|
||||
tr->FromMatrix(nextLocalMatrix);
|
||||
} else {
|
||||
tr->FromMatrix(VtkToMatrix4f(nextWorldMatrix));
|
||||
}
|
||||
member->Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_PrevMatrix->DeepCopy(currentMatrix);
|
||||
m_SelectionTransform.FromMatrix(VtkToMatrix4f(currentMatrix));
|
||||
}
|
||||
|
||||
vtkProp* MultiSelectionProp::GetProp() {
|
||||
return m_GroupHighlightActor;
|
||||
}
|
||||
|
||||
vtkProp3D* MultiSelectionProp::GetProxyProp() {
|
||||
return m_GroupHighlightActor;
|
||||
}
|
||||
|
||||
void MultiSelectionProp::serialize(Archive::property_register_archive &ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp("Transform", m_SelectionTransform);
|
||||
}
|
||||
|
||||
void MultiSelectionProp::serialize_display(uLib::Archive::display_properties_archive &ar, const unsigned int version) {
|
||||
// Call base class to register standard display properties (Color, Opacity)
|
||||
((Prop3D*)this)->serialize_display(ar, version);
|
||||
}
|
||||
|
||||
} // namespace Vtk
|
||||
} // namespace uLib
|
||||
61
src/Vtk/vtkMultiSelectionProp.h
Normal file
61
src/Vtk/vtkMultiSelectionProp.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef ULIB_VTK_MULTISELECTIONPROP_H
|
||||
#define ULIB_VTK_MULTISELECTIONPROP_H
|
||||
|
||||
#include "uLibVtkInterface.h"
|
||||
#include "Math/Transform.h"
|
||||
#include <vector>
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkMatrix4x4.h>
|
||||
|
||||
class vtkActor;
|
||||
class vtkProp;
|
||||
class vtkProp3D;
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
/**
|
||||
* @class MultiSelectionProp
|
||||
* @brief A proxy Prop3D that represents a group of selected Prop3Ds.
|
||||
* It manages a combined highlight and propagates transformations to its members.
|
||||
*/
|
||||
class MultiSelectionProp : public Prop3D {
|
||||
public:
|
||||
uLibTypeMacro(MultiSelectionProp, Prop3D)
|
||||
|
||||
MultiSelectionProp();
|
||||
virtual ~MultiSelectionProp();
|
||||
|
||||
/** @brief Creates a new instance that is a copy of this one's selection state. */
|
||||
MultiSelectionProp* Clone() const;
|
||||
|
||||
void SetMembers(const std::vector<Prop3D*>& members);
|
||||
const std::vector<Prop3D*>& GetMembers() const { return m_Members; }
|
||||
|
||||
virtual void Update() override;
|
||||
virtual void SyncFromVtk() override;
|
||||
|
||||
virtual vtkProp* GetProp() override;
|
||||
virtual vtkProp3D* GetProxyProp() override;
|
||||
|
||||
// Serialization for Properties Panel (TRS)
|
||||
void serialize(Archive::property_register_archive &ar, const unsigned int version);
|
||||
|
||||
// Serialization for Display Properties Panel (Color, Opacity)
|
||||
void serialize_display(uLib::Archive::display_properties_archive &ar, const unsigned int version = 0) override;
|
||||
|
||||
virtual uLib::Object* GetContent() const override { return const_cast<MultiSelectionProp*>(this); }
|
||||
|
||||
private:
|
||||
std::vector<Prop3D*> m_Members;
|
||||
uLib::TRS m_SelectionTransform;
|
||||
vtkSmartPointer<vtkMatrix4x4> m_PrevMatrix;
|
||||
vtkSmartPointer<vtkActor> m_GroupHighlightActor;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace Vtk
|
||||
} // namespace uLib
|
||||
|
||||
#endif // ULIB_VTK_MULTISELECTIONPROP_H
|
||||
@@ -1,24 +1,23 @@
|
||||
#include "vtkObjectsContext.h"
|
||||
#include "Vtk/Math/vtkAssembly.h"
|
||||
#include "Vtk/Math/vtkContainerBox.h"
|
||||
#include "Vtk/Math/vtkCylinder.h"
|
||||
#include "Vtk/Math/vtkAssembly.h"
|
||||
#include "Vtk/Math/vtkVoxImage.h"
|
||||
|
||||
#include "Vtk/HEP/Detectors/vtkDetectorChamber.h"
|
||||
#include "Vtk/HEP/Geant/vtkBoxSolid.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vtkAssembly.h>
|
||||
#include <vtkPropCollection.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Cylinder.h"
|
||||
#include "Math/Assembly.h"
|
||||
#include "Math/VoxImage.h"
|
||||
#include "HEP/Detectors/DetectorChamber.h"
|
||||
#include "HEP/Geant/Solid.h"
|
||||
|
||||
#include "Math/Assembly.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Cylinder.h"
|
||||
#include "Math/VoxImage.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
@@ -27,35 +26,43 @@ ObjectsContext::ObjectsContext(uLib::ObjectsContext *context)
|
||||
: m_Context(context), m_Assembly(::vtkAssembly::New()) {
|
||||
this->SetProp(m_Assembly);
|
||||
if (m_Context) {
|
||||
Object::connect(m_Context, &uLib::ObjectsContext::ObjectAdded, this, &ObjectsContext::OnObjectAdded);
|
||||
Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this, &ObjectsContext::OnObjectRemoved);
|
||||
m_AddedConnection = Object::connect(m_Context, &uLib::ObjectsContext::ObjectAdded, this,
|
||||
&ObjectsContext::OnObjectAdded);
|
||||
m_RemovedConnection = Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this,
|
||||
&ObjectsContext::OnObjectRemoved);
|
||||
this->Synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
ObjectsContext::~ObjectsContext() {
|
||||
for (auto const& [obj, prop3d] : m_Prop3Ds) {
|
||||
m_AddedConnection.disconnect();
|
||||
m_RemovedConnection.disconnect();
|
||||
for (auto const &[obj, prop3d] : m_Prop3Ds) {
|
||||
delete prop3d;
|
||||
}
|
||||
m_Assembly->Delete();
|
||||
}
|
||||
|
||||
void ObjectsContext::Synchronize() {
|
||||
if (!m_Context) return;
|
||||
if (!m_Context)
|
||||
return;
|
||||
|
||||
// 1. Identify objects to add and remove
|
||||
const auto& objects = m_Context->GetObjects();
|
||||
std::map<uLib::Object*, bool> currentObjects;
|
||||
for (auto obj : objects) currentObjects[obj] = true;
|
||||
const auto &objects = m_Context->GetObjects();
|
||||
std::map<uLib::Object *, bool> currentObjects;
|
||||
for (auto obj : objects)
|
||||
currentObjects[obj] = true;
|
||||
|
||||
// Remove Prop3Ds for objects no longer in context
|
||||
for (auto it = m_Prop3Ds.begin(); it != m_Prop3Ds.end(); ) {
|
||||
for (auto it = m_Prop3Ds.begin(); it != m_Prop3Ds.end();) {
|
||||
if (currentObjects.find(it->first) == currentObjects.end()) {
|
||||
it->second->DisconnectRenderer(nullptr); // If we have a ref to a renderer we should disconnect but Prop3D doesn't store it easily
|
||||
it->second->DisconnectRenderer(
|
||||
nullptr); // If we have a ref to a renderer we should disconnect but
|
||||
// Prop3D doesn't store it easily
|
||||
// Actually Prop3D::DisconnectRenderer(vtkRenderer*) needs the renderer.
|
||||
// For now we just remove from assembly
|
||||
if (auto* p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
|
||||
m_Assembly->RemovePart(p3d);
|
||||
if (auto *p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
|
||||
m_Assembly->RemovePart(p3d);
|
||||
this->Prop3DRemoved(it->second);
|
||||
delete it->second;
|
||||
it = m_Prop3Ds.erase(it);
|
||||
@@ -67,92 +74,101 @@ void ObjectsContext::Synchronize() {
|
||||
// Add Prop3Ds for new objects
|
||||
for (auto obj : objects) {
|
||||
if (m_Prop3Ds.find(obj) == m_Prop3Ds.end()) {
|
||||
Prop3D* prop3d = this->CreateProp3D(obj);
|
||||
Prop3D *prop3d = this->CreateProp3D(obj);
|
||||
if (prop3d) {
|
||||
m_Prop3Ds[obj] = prop3d;
|
||||
if (auto* p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
|
||||
m_Assembly->AddPart(p3d);
|
||||
if (auto *p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
|
||||
m_Assembly->AddPart(p3d);
|
||||
this->Prop3DAdded(prop3d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContext::OnObjectAdded(uLib::Object* obj) {
|
||||
if (!obj) return;
|
||||
void ObjectsContext::OnObjectAdded(uLib::Object *obj) {
|
||||
if (!obj)
|
||||
return;
|
||||
if (m_Prop3Ds.find(obj) == m_Prop3Ds.end()) {
|
||||
Prop3D* prop3d = this->CreateProp3D(obj);
|
||||
Prop3D *prop3d = this->CreateProp3D(obj);
|
||||
if (prop3d) {
|
||||
m_Prop3Ds[obj] = prop3d;
|
||||
if (auto* p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
|
||||
m_Assembly->AddPart(p3d);
|
||||
if (auto *p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
|
||||
m_Assembly->AddPart(p3d);
|
||||
this->Prop3DAdded(prop3d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContext::OnObjectRemoved(uLib::Object* obj) {
|
||||
if (!obj) return;
|
||||
auto it = m_Prop3Ds.find(obj);
|
||||
if (it != m_Prop3Ds.end()) {
|
||||
// For now we just remove from assembly.
|
||||
// Prop3D::DisconnectRenderer(vtkRenderer*) needs the renderer, but we don't have it here easily.
|
||||
if (auto* p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
|
||||
m_Assembly->RemovePart(p3d);
|
||||
this->Prop3DRemoved(it->second);
|
||||
delete it->second;
|
||||
m_Prop3Ds.erase(it);
|
||||
}
|
||||
void ObjectsContext::OnObjectRemoved(uLib::Object *obj) {
|
||||
if (!obj)
|
||||
return;
|
||||
auto it = m_Prop3Ds.find(obj);
|
||||
if (it != m_Prop3Ds.end()) {
|
||||
// For now we just remove from assembly.
|
||||
// Prop3D::DisconnectRenderer(vtkRenderer*) needs the renderer, but we don't
|
||||
// have it here easily.
|
||||
if (auto *p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
|
||||
m_Assembly->RemovePart(p3d);
|
||||
this->Prop3DRemoved(it->second);
|
||||
delete it->second;
|
||||
m_Prop3Ds.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
Prop3D* ObjectsContext::GetProp3D(uLib::Object* obj) {
|
||||
Prop3D *ObjectsContext::GetProp3D(uLib::Object *obj) {
|
||||
auto it = m_Prop3Ds.find(obj);
|
||||
if (it != m_Prop3Ds.end()) return it->second;
|
||||
if (it != m_Prop3Ds.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjectsContext::Update() {
|
||||
for (auto const& [obj, prop3d] : m_Prop3Ds) {
|
||||
for (auto const &[obj, prop3d] : m_Prop3Ds) {
|
||||
prop3d->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContext::SyncFromVtk() {
|
||||
for (auto const& [obj, prop3d] : m_Prop3Ds) {
|
||||
for (auto const &[obj, prop3d] : m_Prop3Ds) {
|
||||
prop3d->SyncFromVtk();
|
||||
}
|
||||
}
|
||||
|
||||
Prop3D* ObjectsContext::CreateProp3D(uLib::Object* obj) {
|
||||
if (!obj) return nullptr;
|
||||
Prop3D *ObjectsContext::CreateProp3D(uLib::Object *obj) {
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
if (auto* vox = dynamic_cast<uLib::Abstract::VoxImage*>(obj)) {
|
||||
return new VoxImage(*vox);
|
||||
} else if (auto* box = dynamic_cast<uLib::ContainerBox*>(obj)) {
|
||||
if (auto* p3d = dynamic_cast<Prop3D*>(obj)) {
|
||||
return p3d;
|
||||
}
|
||||
|
||||
if (auto *vox = dynamic_cast<uLib::Abstract::VoxImage *>(obj)) {
|
||||
return new VoxImage(vox);
|
||||
} else if (auto *box = dynamic_cast<uLib::ContainerBox *>(obj)) {
|
||||
return new ContainerBox(box);
|
||||
} else if (auto* chamber = dynamic_cast<uLib::DetectorChamber*>(obj)) {
|
||||
} else if (auto *chamber = dynamic_cast<uLib::DetectorChamber *>(obj)) {
|
||||
return new DetectorChamber(chamber);
|
||||
} else if (auto* cylinder = dynamic_cast<uLib::Cylinder*>(obj)) {
|
||||
} else if (auto *cylinder = dynamic_cast<uLib::Cylinder *>(obj)) {
|
||||
return new Cylinder(cylinder);
|
||||
} else if (auto* assembly = dynamic_cast<uLib::Assembly*>(obj)) {
|
||||
} else if (auto *assembly = dynamic_cast<uLib::Assembly *>(obj)) {
|
||||
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 BoxSolid(box);
|
||||
}
|
||||
|
||||
// Fallback if we don't know the exact class but it might be a context itself
|
||||
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
|
||||
return new ObjectsContext(subCtx);
|
||||
if (auto subCtx = dynamic_cast<uLib::ObjectsContext *>(obj)) {
|
||||
return new ObjectsContext(subCtx);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjectsContext::Prop3DAdded(Prop3D* prop3d) {
|
||||
void ObjectsContext::Prop3DAdded(Prop3D *prop3d) {
|
||||
ULIB_SIGNAL_EMIT(ObjectsContext::Prop3DAdded, prop3d);
|
||||
}
|
||||
|
||||
void ObjectsContext::Prop3DRemoved(Prop3D* prop3d) {
|
||||
void ObjectsContext::Prop3DRemoved(Prop3D *prop3d) {
|
||||
ULIB_SIGNAL_EMIT(ObjectsContext::Prop3DRemoved, prop3d);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@ private:
|
||||
uLib::ObjectsContext *m_Context;
|
||||
std::map<uLib::Object*, Prop3D*> m_Prop3Ds;
|
||||
vtkAssembly *m_Assembly;
|
||||
uLib::Connection m_AddedConnection;
|
||||
uLib::Connection m_RemovedConnection;
|
||||
};
|
||||
|
||||
} // namespace Vtk
|
||||
|
||||
@@ -20,6 +20,8 @@ QViewport::QViewport(QWidget* parent)
|
||||
, m_VtkWidget(nullptr)
|
||||
, m_GridButton(nullptr)
|
||||
, m_ProjButton(nullptr)
|
||||
, m_renderTimer(nullptr)
|
||||
, m_renderPending(false)
|
||||
{
|
||||
// Build the layout – zero margins so VTK fills the entire widget
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
@@ -29,8 +31,14 @@ QViewport::QViewport(QWidget* parent)
|
||||
m_VtkWidget = new QVTKOpenGLNativeWidget(this);
|
||||
layout->addWidget(m_VtkWidget);
|
||||
|
||||
// Initialize render timer
|
||||
m_renderTimer = new QTimer(this);
|
||||
m_renderTimer->setSingleShot(true);
|
||||
connect(m_renderTimer, &QTimer::timeout, this, &QViewport::doRender);
|
||||
|
||||
// Grid Toggle Button
|
||||
m_GridButton = new QPushButton(m_VtkWidget);
|
||||
|
||||
m_GridButton->setText("#");
|
||||
m_GridButton->setFixedSize(40, 40);
|
||||
m_GridButton->setToolTip("Toggle Grid");
|
||||
@@ -96,6 +104,9 @@ QViewport::QViewport(QWidget* parent)
|
||||
|
||||
QViewport::~QViewport()
|
||||
{
|
||||
if (m_VtkWidget && m_VtkWidget->renderWindow()) {
|
||||
m_VtkWidget->renderWindow()->RemoveRenderer(this->GetRenderer());
|
||||
}
|
||||
}
|
||||
|
||||
void QViewport::SetupPipeline()
|
||||
@@ -112,10 +123,22 @@ void QViewport::SetupPipeline()
|
||||
|
||||
void QViewport::Render()
|
||||
{
|
||||
if (m_VtkWidget && m_VtkWidget->renderWindow())
|
||||
m_VtkWidget->renderWindow()->Render();
|
||||
if (!m_throttledRendering) {
|
||||
doRender();
|
||||
return;
|
||||
}
|
||||
if (m_renderPending) return;
|
||||
m_renderPending = true;
|
||||
m_renderTimer->start(16);
|
||||
}
|
||||
|
||||
void QViewport::doRender()
|
||||
{
|
||||
m_renderPending = false;
|
||||
if (m_VtkWidget && m_VtkWidget->renderWindow())
|
||||
m_VtkWidget->renderWindow()->Render();
|
||||
}
|
||||
|
||||
vtkRenderWindow* QViewport::GetRenderWindow()
|
||||
{
|
||||
return m_VtkWidget->renderWindow();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QWidget>
|
||||
#include <QVTKOpenGLNativeWidget.h>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
#include <vtkCornerAnnotation.h>
|
||||
#include <vtkOrientationMarkerWidget.h>
|
||||
@@ -39,6 +40,9 @@ public:
|
||||
// Render scene
|
||||
virtual void Render() override;
|
||||
|
||||
void SetThrottledRendering(bool enabled) { m_throttledRendering = enabled; }
|
||||
bool GetThrottledRendering() const { return m_throttledRendering; }
|
||||
|
||||
// Direct access to VTK internals
|
||||
virtual vtkRenderWindow* GetRenderWindow() override;
|
||||
virtual vtkRenderWindowInteractor* GetInteractor() override;
|
||||
@@ -46,20 +50,25 @@ public:
|
||||
|
||||
virtual void OnSelectionChanged(Prop3D* p) override;
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent* event) override;
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void onGridButtonClicked();
|
||||
void onProjButtonClicked();
|
||||
private slots:
|
||||
void onGridButtonClicked();
|
||||
void onProjButtonClicked();
|
||||
void doRender();
|
||||
|
||||
private:
|
||||
void SetupPipeline();
|
||||
private:
|
||||
void SetupPipeline();
|
||||
|
||||
QVTKOpenGLNativeWidget* m_VtkWidget;
|
||||
QPushButton* m_GridButton;
|
||||
QPushButton* m_ProjButton;
|
||||
QTimer* m_renderTimer;
|
||||
bool m_renderPending = false;
|
||||
bool m_throttledRendering = true;
|
||||
};
|
||||
|
||||
QVTKOpenGLNativeWidget* m_VtkWidget;
|
||||
QPushButton* m_GridButton;
|
||||
QPushButton* m_ProjButton;
|
||||
};
|
||||
|
||||
} // namespace Vtk
|
||||
} // namespace uLib
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include "Vtk/Math/vtkCylinder.h"
|
||||
#include "Math/Transform.h"
|
||||
#include "Vtk/Math/vtkAssembly.h"
|
||||
#include "vtkMultiSelectionProp.h"
|
||||
#include <vtkRendererCollection.h>
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
@@ -69,6 +71,7 @@ struct ViewportData {
|
||||
Viewport::Viewport()
|
||||
: pv(new ViewportData())
|
||||
, m_GridAxis(Y)
|
||||
, m_MultiSelectionProp(new MultiSelectionProp())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -100,6 +103,10 @@ Viewport::~Viewport()
|
||||
pv->m_CameraWidget->Off();
|
||||
pv->m_CameraWidget->SetInteractor(nullptr);
|
||||
}
|
||||
if (m_MultiSelectionProp) {
|
||||
delete m_MultiSelectionProp;
|
||||
m_MultiSelectionProp = nullptr;
|
||||
}
|
||||
delete pv;
|
||||
}
|
||||
|
||||
@@ -192,6 +199,11 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
|
||||
pv->m_Renderer->SetLayer(0);
|
||||
}
|
||||
|
||||
// Connect MultiSelectionProp
|
||||
if (m_MultiSelectionProp) {
|
||||
m_MultiSelectionProp->ConnectRenderer(pv->m_Renderer);
|
||||
}
|
||||
|
||||
// Setup Handler Widget
|
||||
if (!std::getenv("CTEST_PROJECT_NAME")) {
|
||||
pv->m_HandlerWidget = vtkSmartPointer<HandlerWidget>::New();
|
||||
@@ -206,8 +218,10 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
|
||||
widgetInteractionCallback->SetClientData(this);
|
||||
widgetInteractionCallback->SetCallback([](vtkObject*, unsigned long, void* clientdata, void*){
|
||||
auto* self = static_cast<Viewport*>(clientdata);
|
||||
for (auto* p : self->m_Prop3Ds) {
|
||||
if (p->IsSelected()) {
|
||||
if (self->m_SelectedProps.size() > 1 && self->m_MultiSelectionProp) {
|
||||
self->m_MultiSelectionProp->SyncFromVtk();
|
||||
} else {
|
||||
for (auto* p : self->m_SelectedProps) {
|
||||
p->SyncFromVtk();
|
||||
}
|
||||
}
|
||||
@@ -222,6 +236,7 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
|
||||
clickCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){
|
||||
auto* iren = static_cast<vtkRenderWindowInteractor*>(caller);
|
||||
auto* self = static_cast<Viewport*>(clientdata);
|
||||
bool multiSelect = iren->GetShiftKey() != 0;
|
||||
|
||||
int* pos = iren->GetEventPosition();
|
||||
self->pv->m_Picker->Pick(pos[0], pos[1], 0, self->pv->m_Renderer);
|
||||
@@ -291,7 +306,7 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
|
||||
}
|
||||
}
|
||||
}
|
||||
self->SelectProp3D(target);
|
||||
self->SelectProp3D(target, multiSelect);
|
||||
});
|
||||
iren->AddObserver(vtkCommand::LeftButtonPressEvent, clickCallback);
|
||||
|
||||
@@ -446,6 +461,11 @@ void Viewport::RegisterProp3D(Prop3D* p, bool isPart) {
|
||||
m_Prop3Ds.push_back(p);
|
||||
p->ConnectRenderer(pv->m_Renderer);
|
||||
|
||||
// Ensure m_MultiSelectionProp also has the same renderers
|
||||
if (m_MultiSelectionProp) {
|
||||
m_MultiSelectionProp->GetRenderers()->AddItem(pv->m_Renderer);
|
||||
}
|
||||
|
||||
// If it's a part of an assembly, we don't want to draw it twice.
|
||||
// Assembly itself already draws its parts.
|
||||
// But we need ConnectRenderer above to allow highliting and property updates.
|
||||
@@ -497,28 +517,59 @@ void Viewport::ObserveContext(ObjectsContext* ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void Viewport::SelectProp3D(Prop3D* prop)
|
||||
void Viewport::SelectProp3D(Prop3D* prop, bool multi)
|
||||
{
|
||||
for (auto* p : m_Prop3Ds) {
|
||||
p->SetSelected(p == prop);
|
||||
if (multi) {
|
||||
if (prop) {
|
||||
auto it = std::find(m_SelectedProps.begin(), m_SelectedProps.end(), prop);
|
||||
if (it != m_SelectedProps.end()) {
|
||||
prop->SetSelected(false);
|
||||
m_SelectedProps.erase(it);
|
||||
} else {
|
||||
prop->SetSelected(true);
|
||||
m_SelectedProps.push_back(prop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto* p : m_SelectedProps) {
|
||||
p->SetSelected(false);
|
||||
}
|
||||
m_SelectedProps.clear();
|
||||
if (prop) {
|
||||
prop->SetSelected(true);
|
||||
m_SelectedProps.push_back(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// Update HandlerWidget
|
||||
if (pv->m_HandlerWidget) {
|
||||
if (prop) {
|
||||
vtkProp3D* prop3d = prop->GetProxyProp();
|
||||
if (m_SelectedProps.empty()) {
|
||||
pv->m_HandlerWidget->SetEnabled(0);
|
||||
pv->m_HandlerWidget->SetProp3D(nullptr);
|
||||
if (m_MultiSelectionProp) m_MultiSelectionProp->SetMembers({});
|
||||
} else if (m_SelectedProps.size() == 1) {
|
||||
Prop3D* selected = m_SelectedProps[0];
|
||||
vtkProp3D* prop3d = selected->GetProxyProp();
|
||||
if (prop3d) {
|
||||
pv->m_HandlerWidget->SetProp3D(prop3d);
|
||||
pv->m_HandlerWidget->SetEnabled(1);
|
||||
pv->m_HandlerWidget->PlaceWidget(prop3d->GetBounds()); //TODO: FIX !
|
||||
pv->m_HandlerWidget->PlaceWidget(prop3d->GetBounds());
|
||||
}
|
||||
if (m_MultiSelectionProp) m_MultiSelectionProp->SetMembers({});
|
||||
} else {
|
||||
pv->m_HandlerWidget->SetEnabled(0);
|
||||
pv->m_HandlerWidget->SetProp3D(nullptr);
|
||||
// Multi-selection
|
||||
if (m_MultiSelectionProp) {
|
||||
m_MultiSelectionProp->SetMembers(m_SelectedProps);
|
||||
vtkProp3D* proxy = m_MultiSelectionProp->GetProxyProp();
|
||||
pv->m_HandlerWidget->SetProp3D(proxy);
|
||||
pv->m_HandlerWidget->SetEnabled(1);
|
||||
pv->m_HandlerWidget->PlaceWidget(proxy->GetBounds());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Render();
|
||||
OnSelectionChanged(prop);
|
||||
OnSelectionChanged(m_SelectedProps.empty() ? nullptr : m_SelectedProps.back());
|
||||
}
|
||||
|
||||
void Viewport::SetGridVisible(bool visible)
|
||||
@@ -657,5 +708,19 @@ void Viewport::UpdateGrid()
|
||||
pv->m_Annotation->SetText(1, gridLabel);
|
||||
}
|
||||
|
||||
void Viewport::GroupSelection(uLib::ObjectsContext* targetCtx) {
|
||||
if (!targetCtx || m_SelectedProps.size() <= 1 || !m_MultiSelectionProp) return;
|
||||
|
||||
// Clone the current multi-selection proxy
|
||||
MultiSelectionProp* group = m_MultiSelectionProp->Clone();
|
||||
|
||||
// Add it to the context
|
||||
targetCtx->AddObject(group);
|
||||
|
||||
// Select the new group and clear multi-selection
|
||||
m_SelectedProps.clear();
|
||||
SelectProp3D(group);
|
||||
}
|
||||
|
||||
} // namespace Vtk
|
||||
} // namespace uLib
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace Vtk {
|
||||
|
||||
struct ViewportData;
|
||||
class HandlerWidget;
|
||||
class MultiSelectionProp;
|
||||
class ObjectsContext;
|
||||
|
||||
/**
|
||||
@@ -49,7 +50,11 @@ public:
|
||||
// Prop3D / prop management
|
||||
void AddProp3D(Prop3D &prop);
|
||||
void RemoveProp3D(Prop3D &prop);
|
||||
void SelectProp3D(Prop3D *prop);
|
||||
/** @brief Selects a specific Prop3D. If multi is true, it toggles selection in a group. */
|
||||
void SelectProp3D(Prop3D* target, bool multi = false);
|
||||
|
||||
/** @brief Creates a persistent Selection Group from the current multi-selection. */
|
||||
void GroupSelection(uLib::ObjectsContext* targetCtx);
|
||||
void addProp(vtkProp *prop);
|
||||
void RemoveProp(vtkProp *prop);
|
||||
|
||||
@@ -91,6 +96,8 @@ protected:
|
||||
struct ViewportData *pv;
|
||||
Axis m_GridAxis;
|
||||
std::vector<Prop3D*> m_Prop3Ds;
|
||||
std::vector<Prop3D*> m_SelectedProps;
|
||||
MultiSelectionProp* m_MultiSelectionProp;
|
||||
std::map<uLib::Object*, Prop3D*> m_ObjectToProp3D;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user