4 Commits

Author SHA1 Message Date
AndreaRigoni
e09a614fa5 refactor: add override specifier to type_name method in Core/Types.h 2026-04-03 10:14:20 +00:00
AndreaRigoni
7f6323403d merge andrea-geo: geometry/material/property system features
Merges all work from andrea-geo branch:
- Geant material management classes
- Serialization enhancements (read-only, NVP/HRP macros)
- Transform/assembly system improvements
- VTK puppet/viewport updates (orthographic toggle, voxel rendering)
- Property grouping, dynamic properties, NotifyPropertiesUpdated
- Object type identification via uLibTypeMacro
- New tests (PropertyGrouping, ReadOnly, vtkQViewport, PuppetParenting)
- Various gcompose UI fixes

Conflict resolved in CMakePresets.json: kept both 'fast' (clang/lld)
and 'mutom' (stub) presets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 08:47:32 +00:00
AndreaRigoni
a53b3051de fix EXPAT::EXPAT-NOTFOUND when building with Geant4 on conda
Geant4's G4EXPATShim creates EXPAT::EXPAT (uppercase) with
IMPORTED_LOCATION set to ${EXPAT_LIBRARY}, which is empty when EXPAT
is found via conda's config-mode package (expat::expat, lowercase).

After find_package(Geant4), patch EXPAT::EXPAT with the real library
path taken from expat::expat IMPORTED_LOCATION_NOCONFIG, falling back
to find_library if needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 08:41:00 +00:00
AndreaRigoni
c53570192f switch to Ninja+ccache, add clang/lld fast build profile
- CMakePresets.json: add 'fast' preset (clang+lld+ccache)
- .gitignore: generalize build/ to build*/, add CMakeUserPresets.json
- CMakeUserPresets.json: untrack (conan-generated, now gitignored)
- src/Core/Archives.h: remove redundant 'using basic_xml_iarchive::load_override'
  in xml_iarchive; caused ambiguous overload with clang (diamond inheritance)
- src/Core/Object.cpp: remove invalid explicit instantiations of non-template
  virtual Object::serialize (GCC extension, clang rejects)
- README.md, CLAUDE.md: document GCC and LLVM/clang build workflows

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 08:24:50 +00:00
36 changed files with 381 additions and 711 deletions

View File

@@ -1,9 +0,0 @@
---
trigger: manual
---
# Context Restriction: No VTK
When this rule is active, restrict the operational context to libraries excluding the VTK layer.
- **Exclude Path**: `src/Vtk`
- **Include Paths**: `src/Core`, `src/Math`, `src/HEP`, `src/Root`, `src/Python`, `src/utils`
- **Focus**: Tomographic reconstruction algorithms, data structures in `Core`, and physical modeling in `HEP`.
- **Constraint**: Avoid referencing `Puppet`, `Viewport`, or any VTK-specific headers unless the user overrides this restriction.

View File

@@ -1,9 +0,0 @@
---
trigger: manual
---
# Context Focus: gcompose Application
When this rule is active, prioritize the `gcompose` GUI application.
- **Primary Path**: `app/gcompose`
- **Focus**: `MainPanel`, `ContextPanel`, `PropertiesPanel`, and `ViewportPane`.
- **Integration**: Wiring of Qt signals/slots between the `uLib` core model and the GUI widgets.
- **Dependency**: Reference `src/Vtk` and `src/Core` as the underlying framework for the application.

View File

@@ -1,9 +0,0 @@
---
trigger: always_on
---
# Context Inclusion: VTK
When this rule is active, include the VTK visualization layer in the operational context.
- **Priority Path**: `src/Vtk`
- **Focus**: `Puppet` hierarchy, `Viewport` management, and the synchronization between domain objects and VTK props.
- **Key Classes**: `vtkViewport`, `vtkQViewport`, `vtkObjectsContext`, and all classes in `src/Vtk/HEP/Geant`.
- **Logic**: Ensure transformations (TRS) applied to domain objects are correctly mirrored in the visualization layer and vice versa.

View File

@@ -0,0 +1,7 @@
---
trigger: always_on
---
build in build directory using always micromamba "mutom" env.
build with make flag -j$(nproc).

View File

@@ -1,39 +0,0 @@
# Skill: Build uLib with Micromamba
This skill provides instructions for building the uLib project using the micromamba environment.
## Context
- **Environment**: micromamba `uLib`
- **Output Directory**: `build`
- **CPU Usage**: All available cores
## Instructions
1. **Environment Setup**:
Ensure micromamba is properly initialized and the `uLib` environment is active.
```bash
export MAMBA_EXE="/home/share/micromamba/bin/micromamba"
export MAMBA_ROOT_PREFIX="/home/share/micromamba"
eval "$(/home/share/micromamba/bin/micromamba shell hook --shell bash)"
micromamba activate uLib
```
2. **Full Rebuild (if needed)**:
If the `build` directory does not exist or a full reconfiguration is required:
```bash
conan profile detect --force
conan install . --output-folder=build --build=missing
cmake --preset conan-release
```
3. **Incremental Build**:
Run the build command from the root directory, pointing to the `build` folder and using all cores.
```bash
cmake --build build -j$(nproc)
```
4. **Specific Target Build**:
To build a specific target (e.g., gcompose):
```bash
cmake --build build --target gcompose -j$(nproc)
```

2
.gitignore vendored
View File

@@ -18,5 +18,3 @@ test_props.xml
test_props2.xml test_props2.xml
test_boost.cpp test_boost.cpp
.claude/settings.json .claude/settings.json
build_output.log
configure_output.log

View File

@@ -1,3 +0,0 @@
#!/bin/bash
export DISPLAY=:1001.0
/home/share/micromamba/bin/micromamba run -n uLib /usr/bin/gdb "$@"

31
.vscode/launch.json vendored
View File

@@ -1,31 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gcompose",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/app/gcompose/gcompose",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [
{ "name": "DISPLAY", "value": ":1001.0" }
],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "Build gcompose",
"miDebuggerPath": "${workspaceFolder}/.vscode/gdb_wrapper.sh"
}
]
}

17
.vscode/tasks.json vendored
View File

@@ -1,17 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build gcompose",
"type": "shell",
"command": "/home/share/micromamba/bin/micromamba run -n uLib cmake --build build --target gcompose -j$(nproc)",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
]
}
]
}

View File

@@ -10,7 +10,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
export MAMBA_EXE="/home/share/micromamba/bin/micromamba" export MAMBA_EXE="/home/share/micromamba/bin/micromamba"
export MAMBA_ROOT_PREFIX="/home/share/micromamba" export MAMBA_ROOT_PREFIX="/home/share/micromamba"
eval "$(/home/share/micromamba/bin/micromamba shell hook --shell bash)" eval "$(/home/share/micromamba/bin/micromamba shell hook --shell bash)"
micromamba activate uLib micromamba activate mutom
# Configure (from repo root, using Conan preset — uses Ninja + ccache) # Configure (from repo root, using Conan preset — uses Ninja + ccache)
cmake --preset conan-release cmake --preset conan-release

View File

@@ -15,20 +15,11 @@ if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW) cmake_policy(SET CMP0167 NEW)
endif() endif()
## -------------------------------------------------------------------------- ## ## -------------------------------------------------------------------------- ##
project(uLib) project(uLib)
# Applica la flag SOLO se il compilatore è GCC
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-fno-merge-constants)
endif()
# Disabilita il warning se il compilatore è Clang (o AppleClang)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-ignored-optimization-argument)
endif()
# CUDA Toolkit seems to be missing locally. Toggle ON if nvcc is made available. # CUDA Toolkit seems to be missing locally. Toggle ON if nvcc is made available.
option(USE_CUDA "Enable CUDA support" OFF) option(USE_CUDA "Enable CUDA support" OFF)
if(USE_CUDA) if(USE_CUDA)
@@ -133,8 +124,6 @@ find_package(Eigen3 CONFIG REQUIRED)
get_target_property(EIGEN3_INCLUDE_DIRS Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES) get_target_property(EIGEN3_INCLUDE_DIRS Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
include_directories(${EIGEN3_INCLUDE_DIRS}) include_directories(${EIGEN3_INCLUDE_DIRS})
find_package(OpenMP)
find_package(ROOT CONFIG REQUIRED) find_package(ROOT CONFIG REQUIRED)
include(${ROOT_USE_FILE}) include(${ROOT_USE_FILE})
@@ -156,8 +145,6 @@ else()
IOXML IOXML
IOXMLParser IOXMLParser
ImagingCore ImagingCore
ImagingHybrid
ImagingSources
InteractionStyle InteractionStyle
InteractionWidgets InteractionWidgets
RenderingAnnotation RenderingAnnotation

View File

@@ -30,13 +30,13 @@ You can create and activate the environment using either `micromamba` or `conda`
**Using Micromamba:** **Using Micromamba:**
```bash ```bash
micromamba env create -f condaenv.yml micromamba env create -f condaenv.yml
micromamba activate uLib micromamba activate mutom
``` ```
**Using Conda:** **Using Conda:**
```bash ```bash
conda env create -f condaenv.yml conda env create -f condaenv.yml
conda activate uLib conda activate mutom
``` ```
### Configure and Build ### Configure and Build
@@ -65,24 +65,12 @@ cmake --preset conan-release
cmake --build build -j$(nproc) cmake --build build -j$(nproc)
``` ```
5. **Clean build (wipe and rebuild everything):**
```bash
cmake --build build --clean-first -j$(nproc)
```
6. **Run tests:**
```bash
cmake --build build --target test -j$(nproc)
# or equivalently:
ctest --test-dir build --output-on-failure -j$(nproc)
```
#### LLVM/Clang build (clang + lld + ccache — fastest) #### LLVM/Clang build (clang + lld + ccache — fastest)
A `fast` conan profile is provided that uses **clang**, **lld** (LLVM linker), and **ccache**. Install them into your environment first: A `fast` conan profile is provided that uses **clang**, **lld** (LLVM linker), and **ccache**. Install them into your environment first:
```bash ```bash
micromamba install -n uLib -y clang clangxx lld -c conda-forge micromamba install -n mutom -y clang clangxx lld -c conda-forge
``` ```
Then build using the `fast` profile: Then build using the `fast` profile:
@@ -103,6 +91,6 @@ The `fast` profile is defined at `~/.conan2/profiles/fast` and sets:
### Make python package ### Make python package
```bash ```bash
micromamba run -n uLib env USE_CUDA=ON poetry install micromamba run -n mutom env USE_CUDA=ON poetry install
``` ```

View File

@@ -1,6 +1,6 @@
[requires] [requires]
eigen/3.4.0 eigen/3.4.0
boost/1.86.0 boost/1.83.0
# pybind11/3.0.2 # pybind11/3.0.2
hdf5/1.14.3 hdf5/1.14.3

View File

@@ -1,4 +1,4 @@
name: uLib name: mutom
channels: channels:
- conda-forge - conda-forge
dependencies: dependencies:
@@ -7,15 +7,4 @@ dependencies:
- cmake - cmake
- conan - conan
- root - root
- vtk=9.4 # VTK 9.4 - vtk
- pybind11
# - boost=1.86.0 # requested by VTK 9.4
- ninja
- clang
- clangxx
- lld
- ccache
- OpenMP
- Geant4
- gdb
- valgrind

View File

@@ -80,9 +80,6 @@ template <class ArchiveImplementation> class polymorphic_iarchive_route;
namespace boost { namespace boost {
namespace serialization { namespace serialization {
template <typename T> struct hrp; template <typename T> struct hrp;
template <typename T> struct hrp_val;
template <typename T> struct hrp_enum;
template <typename T> struct hrp_enum_val;
} }
} // namespace boost } // namespace boost
@@ -180,24 +177,6 @@ public:
return *this->This(); return *this->This();
} }
template <class T>
Archive &operator>>(const boost::serialization::hrp_val<T> &t) {
this->This()->load_override(const_cast<boost::serialization::hrp_val<T> &>(t));
return *this->This();
}
template <class T>
Archive &operator>>(const boost::serialization::hrp_enum<T> &t) {
this->This()->load_override(const_cast<boost::serialization::hrp_enum<T> &>(t));
return *this->This();
}
template <class T>
Archive &operator>>(const boost::serialization::hrp_enum_val<T> &t) {
this->This()->load_override(const_cast<boost::serialization::hrp_enum_val<T> &>(t));
return *this->This();
}
// the & operator // the & operator
template <class T> Archive &operator&(T &t) { return *(this->This()) >> t; } template <class T> Archive &operator&(T &t) { return *(this->This()) >> t; }
@@ -211,21 +190,6 @@ public:
return *(this->This()) >> t; return *(this->This()) >> t;
} }
template <class T>
Archive &operator&(const boost::serialization::hrp_val<T> &t) {
return *(this->This()) >> t;
}
template <class T>
Archive &operator&(const boost::serialization::hrp_enum<T> &t) {
return *(this->This()) >> t;
}
template <class T>
Archive &operator&(const boost::serialization::hrp_enum_val<T> &t) {
return *(this->This()) >> t;
}
// the == operator // the == operator
template <class T> Archive &operator==(T &t) { return this->operator&(t); } template <class T> Archive &operator==(T &t) { return this->operator&(t); }
@@ -266,61 +230,11 @@ public:
return *this->This(); return *this->This();
} }
template <class T> Archive &operator<<(const boost::serialization::hrp<T> &t) {
this->This()->save_override(t);
return *this->This();
}
template <class T> Archive &operator<<(const boost::serialization::hrp_val<T> &t) {
this->This()->save_override(t);
return *this->This();
}
template <class T> Archive &operator<<(const boost::serialization::hrp_enum<T> &t) {
this->This()->save_override(t);
return *this->This();
}
template <class T> Archive &operator<<(const boost::serialization::hrp_enum_val<T> &t) {
this->This()->save_override(t);
return *this->This();
}
template <class T> Archive &operator<<(const boost::serialization::nvp<T> &t) {
this->This()->save_override(t);
return *this->This();
}
// the & operator // the & operator
template <class T> Archive &operator&(const T &t) { template <class T> Archive &operator&(const T &t) {
return *this->This() << t; return *this->This() << t;
} }
template <class T>
Archive &operator&(const boost::serialization::hrp<T> &t) {
return *this->This() << t;
}
template <class T>
Archive &operator&(const boost::serialization::hrp_val<T> &t) {
return *this->This() << t;
}
template <class T>
Archive &operator&(const boost::serialization::hrp_enum<T> &t) {
return *this->This() << t;
}
template <class T>
Archive &operator&(const boost::serialization::hrp_enum_val<T> &t) {
return *this->This() << t;
}
template <class T>
Archive &operator&(const boost::serialization::nvp<T> &t) {
return *this->This() << t;
}
// the == operator // the == operator
template <class T> Archive &operator==(T &t) { return this->operator&(t); } template <class T> Archive &operator==(T &t) { return this->operator&(t); }

View File

@@ -21,7 +21,6 @@ set(HEADERS
StringReader.h StringReader.h
Threads.h Threads.h
Monitor.h Monitor.h
Property.h
Types.h Types.h
Uuid.h Uuid.h
Vector.h Vector.h

View File

@@ -65,19 +65,10 @@ public:
std::vector<Slot> slov; std::vector<Slot> slov;
std::vector<PropertyBase*> m_Properties; std::vector<PropertyBase*> m_Properties;
std::vector<PropertyBase*> m_DynamicProperties; std::vector<PropertyBase*> m_DynamicProperties;
std::vector<PropertyBase*> m_DisplayProperties;
bool m_SignalsBlocked; bool m_SignalsBlocked;
}; };
// Implementations of Property methods // Implementations of Property methods
void Object::RegisterDisplayProperty(PropertyBase* prop) {
if (prop) d->m_DisplayProperties.push_back(prop);
}
const std::vector<PropertyBase*>& Object::GetDisplayProperties() const {
return d->m_DisplayProperties;
}
void Object::RegisterProperty(PropertyBase* prop) { void Object::RegisterProperty(PropertyBase* prop) {
if (prop) { if (prop) {
d->m_Properties.push_back(prop); d->m_Properties.push_back(prop);
@@ -113,9 +104,24 @@ void Object::NotifyPropertiesUpdated() {
for (auto* p : d->m_DynamicProperties) p->Updated(); for (auto* p : d->m_DynamicProperties) p->Updated();
} }
// In Object.h, the template serialize needs to be updated to call property serialization.
// However, since Object::serialize is a template in the header, we might need a helper here.
template <class ArchiveT>
void Object::serialize(ArchiveT &ar, const unsigned int version) {
ar & boost::serialization::make_nvp("InstanceName", d->m_InstanceName);
for (auto* prop : d->m_Properties) {
prop->serialize(ar, version);
}
}
void Object::Updated() { ULIB_SIGNAL_EMIT(Object::Updated); } void Object::Updated() { ULIB_SIGNAL_EMIT(Object::Updated); }
void Object::PropertyUpdated() { ULIB_SIGNAL_EMIT(Object::PropertyUpdated); } void Object::PropertyUpdated() { ULIB_SIGNAL_EMIT(Object::PropertyUpdated); }
template <class ArchiveT>
void Object::save_override(ArchiveT &ar, const unsigned int version) {}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -92,11 +92,9 @@ public:
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// PROPERTIES // // PROPERTIES //
virtual void RegisterProperty(PropertyBase* property); void RegisterProperty(PropertyBase* prop);
virtual void RegisterDynamicProperty(PropertyBase* property); void RegisterDynamicProperty(PropertyBase* prop);
virtual void RegisterDisplayProperty(PropertyBase* property); const std::vector<PropertyBase*>& GetProperties() const;
virtual const std::vector<PropertyBase*>& GetProperties() const;
virtual const std::vector<PropertyBase*>& GetDisplayProperties() const;
PropertyBase* GetProperty(const std::string& name) 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. */
@@ -126,7 +124,7 @@ public:
virtual void serialize(Archive::log_archive & ar, const unsigned int version) {} virtual void serialize(Archive::log_archive & ar, const unsigned int version) {}
template <class ArchiveT> template <class ArchiveT>
void save_override(ArchiveT &ar, const unsigned int version) {} void save_override(ArchiveT &ar, const unsigned int version);
void SaveConfig(std::ostream &os, int version = 0); void SaveConfig(std::ostream &os, int version = 0);
void LoadConfig(std::istream &is, int version = 0); void LoadConfig(std::istream &is, int version = 0);

View File

@@ -5,23 +5,19 @@
#include <vector> #include <vector>
#include <sstream> #include <sstream>
#include <typeinfo> #include <typeinfo>
#include <typeindex> #include <typeindex> // Added
#include <boost/serialization/nvp.hpp> #include <boost/serialization/nvp.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <vector>
#include <boost/type_traits/is_class.hpp> #include <boost/type_traits/is_class.hpp>
#include <boost/mpl/bool.hpp> #include <boost/mpl/bool.hpp>
#include <set> #include <boost/serialization/serialization.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include "Core/Archives.h" #include "Core/Archives.h"
#include "Core/Signal.h" #include "Core/Signal.h"
#include "Core/Object.h" #include "Core/Object.h"
namespace uLib { namespace uLib {
namespace Archive {
class property_register_archive;
}
/** /**
* @brief Base class for properties to allow runtime listing and identification. * @brief Base class for properties to allow runtime listing and identification.
*/ */
@@ -31,7 +27,7 @@ public:
virtual const std::string& GetName() const = 0; virtual const std::string& GetName() const = 0;
virtual const char* GetTypeName() const = 0; virtual const char* GetTypeName() const = 0;
virtual std::string GetValueAsString() const = 0; virtual std::string GetValueAsString() const = 0;
virtual std::type_index GetTypeIndex() const = 0; virtual std::type_index GetTypeIndex() const = 0; // Added
virtual const std::string& GetUnits() const = 0; virtual const std::string& GetUnits() const = 0;
virtual void SetUnits(const std::string& units) = 0; virtual void SetUnits(const std::string& units) = 0;
virtual const std::vector<std::string>& GetEnumLabels() const { virtual const std::vector<std::string>& GetEnumLabels() const {
@@ -65,29 +61,42 @@ public:
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) override = 0; virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) override = 0;
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) override = 0; virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) override = 0;
virtual void serialize(Archive::log_archive & ar, const unsigned int version) override = 0; virtual void serialize(Archive::log_archive & ar, const unsigned int version) override = 0;
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) = 0;
}; };
/** /**
* @brief Template class for typed properties. * @brief Template class for typed properties.
*/ */
template <typename T> template <typename T>
class Property : public PropertyBase { class Property : public PropertyBase {
public: public:
// PROXY: Use an existing variable as back-end storage
Property(Object* owner, const std::string& name, T* valuePtr, const std::string& units = "", const std::string& group = "") Property(Object* owner, const std::string& name, T* valuePtr, const std::string& units = "", const std::string& group = "")
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(valuePtr), m_own(false), : m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(valuePtr), m_own(false),
m_HasRange(false), m_HasDefault(false), m_ReadOnly(false) { m_HasRange(false), m_HasDefault(false), m_ReadOnly(false) {
if (m_owner) m_owner->RegisterProperty(this); if (m_owner) {
m_owner->RegisterProperty(this);
}
} }
// MANAGED: Create and own internal storage
Property(Object* owner, const std::string& name, const T& defaultValue = T(), const std::string& units = "", const std::string& group = "") Property(Object* owner, const std::string& name, const T& defaultValue = T(), const std::string& units = "", const std::string& group = "")
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(new T(defaultValue)), m_own(true), : m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(new T(defaultValue)), m_own(true),
m_HasRange(false), m_HasDefault(true), m_Default(defaultValue), m_ReadOnly(false) { m_HasRange(false), m_HasDefault(true), m_Default(defaultValue), m_ReadOnly(false) {
if (m_owner) m_owner->RegisterProperty(this); if (m_owner) {
m_owner->RegisterProperty(this);
}
} }
virtual ~Property() { if (m_own) delete m_value; } virtual ~Property() {
if (m_own) delete m_value;
}
// Identification // Identification
virtual const std::string& GetName() const override { return m_name; } virtual const std::string& GetName() const override { return m_name; }
@@ -98,18 +107,36 @@ public:
virtual const std::string& GetGroup() const override { return m_group; } virtual const std::string& GetGroup() const override { return m_group; }
virtual void SetGroup(const std::string& group) override { m_group = group; } virtual void SetGroup(const std::string& group) override { m_group = group; }
std::string GetValueAsString() const override { std::string GetValueAsString() const override {
try { return boost::lexical_cast<std::string>(*m_value); } try {
catch (...) { std::stringstream ss; ss << *m_value; return ss.str(); } return boost::lexical_cast<std::string>(*m_value);
} catch (const boost::bad_lexical_cast&) {
std::stringstream ss;
ss << *m_value;
return ss.str();
}
} }
// Accessors // Accessors
const T& Get() const { return *m_value; } const T& Get() const { return *m_value; }
template<typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value, void>::type
ValidateT(T& val) {
if (m_HasRange) {
if (val < m_Min) val = m_Min;
if (val > m_Max) val = m_Max;
}
}
template<typename U = T>
typename std::enable_if<!std::is_arithmetic<U>::value, void>::type
ValidateT(T& val) {
}
void Set(const T& value) { void Set(const T& value) {
T val = value; T val = value;
if constexpr (std::is_arithmetic<T>::value) { ValidateT<T>(val);
if (m_HasRange) { if (val < m_Min) val = m_Min; if (val > m_Max) val = m_Max; }
}
if (*m_value != val) { if (*m_value != val) {
*m_value = val; *m_value = val;
ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged); ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged);
@@ -123,55 +150,65 @@ public:
void SetReadOnly(bool ro) { m_ReadOnly = ro; } void SetReadOnly(bool ro) { m_ReadOnly = ro; }
virtual bool IsReadOnly() const override { return m_ReadOnly; } virtual bool IsReadOnly() const override { return m_ReadOnly; }
virtual bool HasRange() const override { return m_HasRange; }
virtual double GetMin() const override { return m_HasRange ? convert_to_double(m_Min) : 0.0; }
virtual double GetMax() const override { return m_HasRange ? convert_to_double(m_Max) : 0.0; }
const T& GetMinTyped() const { return m_Min; } virtual bool HasRange() const override { return m_HasRange; }
const T& GetMaxTyped() const { return m_Max; }
template<typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value, double>::type
GetMinT() const { return (double)m_Min; }
template<typename U = T>
typename std::enable_if<!std::is_arithmetic<U>::value, double>::type
GetMinT() const { return 0.0; }
template<typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value, double>::type
GetMaxT() const { return (double)m_Max; }
template<typename U = T>
typename std::enable_if<!std::is_arithmetic<U>::value, double>::type
GetMaxT() const { return 0.0; }
virtual double GetMin() const override { return GetMinT<T>(); }
virtual double GetMax() const override { return GetMaxT<T>(); }
virtual bool HasDefault() const override { return m_HasDefault; } virtual bool HasDefault() const override { return m_HasDefault; }
virtual std::string GetDefaultValueAsString() const override { virtual std::string GetDefaultValueAsString() const override {
try { return boost::lexical_cast<std::string>(m_Default); } catch (...) { return ""; } try { return boost::lexical_cast<std::string>(m_Default); }
catch (...) { return ""; }
} }
// Operators // Operators for seamless usage
operator const T&() const { return *m_value; } operator const T&() const { return *m_value; }
Property& operator=(const T& value) { Set(value); return *this; } Property& operator=(const T& value) {
Set(value);
return *this;
}
// Signals
signals: signals:
virtual void PropertyChanged() { ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged); } virtual void PropertyChanged() { ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged); }
private:
template <typename U>
static double convert_to_double(const U& val) {
return convert_to_double_impl(val, typename std::is_arithmetic<U>::type());
}
template <typename U>
static double convert_to_double_impl(const U& val, std::true_type) { return (double)val; }
template <typename U>
static double convert_to_double_impl(const U& val, std::false_type) { return 0.0; }
public:
// Serialization // Serialization
template <class ArchiveT> template <class ArchiveT>
void serialize_helper(ArchiveT & ar, const unsigned int version) { void serialize_impl(ArchiveT & ar, const unsigned int version) {
ar & boost::serialization::make_hrp(m_name.c_str(), *m_value, m_units.c_str()); ar & boost::serialization::make_nvp(m_name.c_str(), *m_value);
} }
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_helper(ar, v); } void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_impl(ar, v); }
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) override; virtual void Updated() override {
PropertyBase::Updated();
this->PropertyChanged();
}
virtual void Updated() override { PropertyBase::Updated(); this->PropertyChanged(); } private:
protected:
std::string m_name; std::string m_name;
std::string m_units; std::string m_units;
std::string m_group; std::string m_group;
@@ -187,7 +224,20 @@ protected:
}; };
/** /**
* @brief Property specialized for enumerations. * @brief Conveninent typedefs for common property types.
*/
typedef Property<std::string> StringProperty;
typedef Property<int> IntProperty;
typedef Property<unsigned int> UIntProperty;
typedef Property<long> LongProperty;
typedef Property<unsigned long> ULongProperty;
typedef Property<float> FloatProperty;
typedef Property<double> DoubleProperty;
typedef Property<Bool_t> BoolProperty;
/**
* @brief Property specialized for enumerations, providing labels for GUI representations.
*/ */
class EnumProperty : public Property<int> { class EnumProperty : public Property<int> {
public: public:
@@ -198,49 +248,68 @@ public:
const char* GetTypeName() const override { return "Enum"; } const char* GetTypeName() const override { return "Enum"; }
virtual std::type_index GetTypeIndex() const override { return std::type_index(typeid(EnumProperty)); } virtual std::type_index GetTypeIndex() const override { return std::type_index(typeid(EnumProperty)); }
template <class ArchiveT>
void serialize_enum_helper(ArchiveT & ar, const unsigned int version) {
ar & boost::serialization::make_hrp_enum(m_name.c_str(), *m_value, m_Labels, m_units.c_str());
}
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) override;
private: private:
std::vector<std::string> m_Labels; std::vector<std::string> m_Labels;
}; };
/**
* @brief Macro to simplify property declaration within a class.
* Usage: ULIB_PROPERTY(float, Width, 1.0f)
* It creates a raw member m_Width and a Property proxy Width.
*/
#define ULIB_PROPERTY(type, name, defaultValue) \
type m_##name = defaultValue; \
Property<type> name = Property<type>(this, #name, &m_##name);
} // namespace uLib } // namespace uLib
namespace uLib { namespace uLib {
namespace Archive { namespace Archive {
class property_register_archive;
} // namespace Archive
} // namespace uLib
class property_register_archive namespace boost {
: public boost::archive::detail::common_oarchive<property_register_archive> { namespace archive {
protected: namespace detail {
template <>
class interface_oarchive<uLib::Archive::property_register_archive>
: public uLib_interface_oarchive<uLib::Archive::property_register_archive> {};
} // namespace detail
} // namespace archive
} // namespace boost
namespace uLib {
namespace Archive {
/**
* @brief A special archive that creates and registers Property proxies
* for any member it encounters wrapped in HRP().
*/
class property_register_archive :
public boost::archive::detail::common_oarchive<property_register_archive>
{
Object* m_Object; Object* m_Object;
bool m_DisplayOnly;
public: public:
friend class boost::archive::detail::interface_oarchive<property_register_archive>; friend class boost::archive::detail::interface_oarchive<property_register_archive>;
friend class boost::archive::save_access; friend class boost::archive::save_access;
using boost::archive::detail::common_oarchive<property_register_archive>::save_override; typedef boost::archive::detail::common_oarchive<property_register_archive> detail_common_oarchive;
property_register_archive(Object* obj, bool displayOnly = false) : property_register_archive(Object* obj) :
boost::archive::detail::common_oarchive<property_register_archive>(boost::archive::no_header), boost::archive::detail::common_oarchive<property_register_archive>(boost::archive::no_header),
m_Object(obj), m_DisplayOnly(displayOnly) { m_Object(obj) {}
if (obj) m_Visited.insert(dynamic_cast<const void*>(obj));
}
template<class T> property_register_archive &operator&(const T &t) { this->save_override(t); return *this; }
template<class T> property_register_archive &operator<<(const T &t) { this->save_override(t); return *this; }
std::string GetCurrentGroup() const { std::string GetCurrentGroup() const {
std::string group; std::string group;
@@ -251,91 +320,77 @@ public:
return group; return group;
} }
template<class T> void register_property(Property<T>& p) { // Core logic: encounter HRP -> Create Dynamic Property
save_property_impl(p.GetName().c_str(), const_cast<T&>(p.Get()), p.GetUnits().c_str(), template<class T>
p.HasRange(), p.GetMinTyped(), p.GetMaxTyped(), p.IsReadOnly()); void save_override(const boost::serialization::hrp<T> &t) {
}
void register_enum_property(EnumProperty& p) {
if (!m_Object) return;
EnumProperty* newP = new EnumProperty(m_Object, p.GetName(), const_cast<int*>(&p.Get()), p.GetEnumLabels(), p.GetUnits(), GetCurrentGroup());
newP->SetReadOnly(p.IsReadOnly());
if (m_DisplayOnly) {
m_Object->RegisterDisplayProperty(newP);
Object* obj = m_Object;
Object::connect(newP, &PropertyBase::Updated, [obj]() { obj->Updated(); });
} else {
m_Object->RegisterDynamicProperty(newP);
}
}
template<class T> void save_property_impl(const char* name, T& val, const char* units, bool hasRange, const T& minVal, const T& maxVal, bool isReadOnly) {
if (m_Object) { if (m_Object) {
Property<T>* p = new Property<T>(m_Object, name, &val, units ? units : "", GetCurrentGroup()); Property<T>* p = new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
set_range_helper(p, hasRange, minVal, maxVal, typename std::is_arithmetic<T>::type()); if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
p->SetReadOnly(isReadOnly); if (t.has_default()) p->SetDefault(t.default_val());
if (m_DisplayOnly) { p->SetReadOnly(t.is_read_only());
m_Object->RegisterDisplayProperty(p);
Object* obj = m_Object;
Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); });
} else {
m_Object->RegisterDynamicProperty(p); m_Object->RegisterDynamicProperty(p);
} }
} }
template<class T>
void save_override(const boost::serialization::hrp_val<T> &t) {
if (m_Object) {
// Note: hrp_val stores by value. Property usually points to existing data.
// But here we are registering properties from HRP wrappers.
// If it's hrp_val, it means it's an rvalue from a getter.
// The hrp_val wrapper itself owns the value.
// However, the property_register_archive is temporary.
// This is a bit tricky. Usually HRP(rvalue) is meant for read-only display.
// Let's use the address of the value in the wrapper, but mark it read-only.
Property<T>* p = new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp_val<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
if (t.has_default()) p->SetDefault(t.default_val());
p->SetReadOnly(t.is_read_only());
m_Object->RegisterDynamicProperty(p);
}
} }
template<class U> static void set_range_helper(Property<U>* p, bool hasRange, const U& minVal, const U& maxVal, std::true_type) { if (hasRange) p->SetRange(minVal, maxVal); } template<class T>
template<class U> static void set_range_helper(Property<U>* p, bool hasRange, const U& minVal, const U& maxVal, std::false_type) {} void save_override(const boost::serialization::hrp_enum<T> &t) {
template<class T> void save_override(const boost::serialization::hrp<T> &t) {
// To handle T correctly without deduction issues, we assume T can be passed to save_property_impl
T dummy = T(); // Ensure we can construct T
save_property_impl(t.name(), const_cast<boost::serialization::hrp<T>&>(t).value(), t.units(), t.has_range(), t.has_range() ? t.min_val() : dummy, t.has_range() ? t.max_val() : dummy, t.is_read_only());
}
template<class T> void save_override(const boost::serialization::hrp_val<T> &t) {
T dummy = T();
save_property_impl(t.name(), const_cast<boost::serialization::hrp_val<T>&>(t).value(), t.units(), t.has_range(), t.has_range() ? t.min_val() : dummy, t.has_range() ? t.max_val() : dummy, t.is_read_only());
}
template<class T> void save_override(const boost::serialization::hrp_enum<T> &t) {
if (m_Object) { 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()); 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()); 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(); }); } m_Object->RegisterDynamicProperty(p);
else { m_Object->RegisterDynamicProperty(p); }
}
}
template<class T> void save_override(const boost::serialization::hrp_enum_val<T> &t) {
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(); }); }
else { m_Object->RegisterDynamicProperty(p); }
} }
} }
template<class T> void save_override(const boost::serialization::nvp<T> &t) { template<class T>
void save_override(const boost::serialization::hrp_enum_val<T> &t) {
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());
m_Object->RegisterDynamicProperty(p);
}
}
// Handle standard NVPs by recursing (important for base classes)
template<class T>
void save_override(const boost::serialization::nvp<T> &t) {
if (t.name()) m_GroupStack.push_back(t.name()); if (t.name()) m_GroupStack.push_back(t.name());
this->save_helper(t.const_value(), typename boost::is_class<T>::type()); this->save_helper(t.const_value(), typename boost::is_class<T>::type());
if (t.name()) m_GroupStack.pop_back(); if (t.name()) m_GroupStack.pop_back();
} }
void save_override(const std::string &t) {} // Recursion for nested classes, ignore primitives
template<class T> void save_override(T * const & t) { template<class T>
if (!t) return; void save_override(const T &t) {
this->save_pointer_helper(t, typename boost::is_base_of<Object, T>::type()); this->save_helper(t, typename boost::is_class<T>::type());
} }
template<class T> void save_pointer_helper(T* t, boost::mpl::true_) {
const void* ptr = dynamic_cast<const void*>(t);
if (m_Visited.find(ptr) != m_Visited.end()) return;
m_Visited.insert(ptr);
this->save_override(*t);
}
template<class T> void save_pointer_helper(T* t, boost::mpl::false_) {}
template<class T> void save_override(const T &t) { this->save_helper(t, typename boost::is_class<T>::type()); }
template<class T> void save_helper(const T &t, boost::mpl::true_) { boost::serialization::serialize_adl(*this, const_cast<T&>(t), 0); }
void save_helper(const std::string &t, boost::mpl::true_) {}
template<class T> void save_helper(const T &t, boost::mpl::false_) {}
template<class T>
void save_helper(const T &t, boost::mpl::true_) {
boost::serialization::serialize_adl(*this, const_cast<T&>(t), 0);
}
template<class T>
void save_helper(const T &t, boost::mpl::false_) {}
// Required attribute overrides for common_oarchive
void save_override(const boost::archive::object_id_type & t) {} void save_override(const boost::archive::object_id_type & t) {}
void save_override(const boost::archive::object_reference_type & t) {} void save_override(const boost::archive::object_reference_type & t) {}
void save_override(const boost::archive::version_type & t) {} void save_override(const boost::archive::version_type & t) {}
@@ -345,30 +400,30 @@ public:
void save_override(const boost::archive::class_name_type & t) {} void save_override(const boost::archive::class_name_type & t) {}
void save_override(const boost::archive::tracking_type & t) {} void save_override(const boost::archive::tracking_type & t) {}
protected: private:
std::vector<std::string> m_GroupStack; std::vector<std::string> m_GroupStack;
std::set<const void*> m_Visited;
}; };
} // namespace Archive
} // namespace uLib
namespace uLib {
template <typename T>
inline void Property<T>::serialize(Archive::property_register_archive & ar, const unsigned int v) {
ar.register_property(*this);
}
inline void EnumProperty::serialize(Archive::property_register_archive & ar, const unsigned int v) {
ar.register_enum_property(*this);
}
namespace Archive {
/**
* @brief Convenience macro to automatically activate and register all HRP members
* as uLib properties. Usage: ULIB_ACTIVATE_PROPERTIES(obj)
*/
#define ULIB_ACTIVATE_PROPERTIES(obj) \ #define ULIB_ACTIVATE_PROPERTIES(obj) \
{ uLib::Archive::property_register_archive _ar_tmp(&(obj)); _ar_tmp & (obj); } { uLib::Archive::property_register_archive _ar_tmp(&(obj)); _ar_tmp & (obj); }
/**
* @brief Declares a private member that automatically calls ULIB_ACTIVATE_PROPERTIES
* in every constructor of the class. Place this macro as the last declaration
* inside the class body (before the closing brace).
*
* Usage: ULIB_DECLARE_PROPERTIES(ClassName)
*
* This replaces per-constructor ULIB_ACTIVATE_PROPERTIES(*this) calls.
* RegisterDynamicProperty deduplicates by qualified name, so re-registration
* from inherited activators in a hierarchy is safe.
*/
#define ULIB_DECLARE_PROPERTIES(SelfType) \ #define ULIB_DECLARE_PROPERTIES(SelfType) \
private: \ private: \
struct _PropActivator { \ struct _PropActivator { \
@@ -379,31 +434,6 @@ private: \
} _prop_activator{this}; } _prop_activator{this};
} // namespace Archive } // namespace Archive
// Convenience macro: declares a named Property<T> member with a default value.
// Usage inside a class body (requires 'this' to be available, so use in-class initializer):
// ULIB_PROPERTY(int, MyProp, 42)
#define ULIB_PROPERTY(type, name, defaultVal) \
::uLib::Property<type> name{this, #name, (type)(defaultVal)};
// Common property type aliases
typedef Property<bool> BoolProperty;
typedef Property<int> IntProperty;
typedef Property<float> FloatProperty;
typedef Property<double> DoubleProperty;
typedef Property<std::string> StringProperty;
template <class ArchiveT>
void serialize_properties_helper(ArchiveT &ar, const std::vector<PropertyBase*> &props, unsigned int version) {
for (auto* prop : props) prop->serialize(ar, version);
}
template <class ArchiveT>
void Object::serialize(ArchiveT &ar, const unsigned int version) {
ar & boost::serialization::make_nvp("InstanceName", this->GetInstanceName());
serialize_properties_helper(ar, this->GetProperties(), version);
}
} // namespace uLib } // namespace uLib
#endif // U_CORE_PROPERTY_H #endif // U_CORE_PROPERTY_H

View File

@@ -38,7 +38,7 @@ inline const unsigned long VectorSplice(const _Tp &_it, const _Tp &_end,
_Tp it = _it; _Tp it = _it;
_Tp end = _end - 1; _Tp end = _end - 1;
for (; it != end;) { for (it; it != end;) {
if (_comp(*it, value)) if (_comp(*it, value))
it++; it++;
else if (_comp(*end, value)) { else if (_comp(*end, value)) {

View File

@@ -32,7 +32,7 @@ class EmitterPrimary : public G4VUserPrimaryGeneratorAction, public AffineTransf
virtual ~EmitterPrimary(); virtual ~EmitterPrimary();
// Metodo principale chiamato all'inizio di ogni evento // Metodo principale chiamato all'inizio di ogni evento
virtual void GeneratePrimaries(G4Event*) override; virtual void GeneratePrimaries(G4Event*);
virtual void Updated() override { ULIB_SIGNAL_EMIT(EmitterPrimary::Updated); } virtual void Updated() override { ULIB_SIGNAL_EMIT(EmitterPrimary::Updated); }
@@ -51,7 +51,7 @@ class SkyPlaneEmitterPrimary : public EmitterPrimary
SkyPlaneEmitterPrimary(); SkyPlaneEmitterPrimary();
virtual ~SkyPlaneEmitterPrimary(); virtual ~SkyPlaneEmitterPrimary();
virtual void GeneratePrimaries(G4Event*) override; virtual void GeneratePrimaries(G4Event*);
void SetPlane(const uLib::Vector3f& p0, const uLib::Vector3f& normal); void SetPlane(const uLib::Vector3f& p0, const uLib::Vector3f& normal);
void SetSkySize(const uLib::Vector2f& size); void SetSkySize(const uLib::Vector2f& size);
@@ -72,7 +72,7 @@ class CylinderEmitterPrimary : public EmitterPrimary
CylinderEmitterPrimary(); CylinderEmitterPrimary();
virtual ~CylinderEmitterPrimary(); virtual ~CylinderEmitterPrimary();
virtual void GeneratePrimaries(G4Event*) override; virtual void GeneratePrimaries(G4Event*);
void SetRadius(float r); void SetRadius(float r);
float GetRadius() const { return m_Radius; } float GetRadius() const { return m_Radius; }
@@ -101,7 +101,7 @@ class QuadMeshEmitterPrimary : public EmitterPrimary
virtual ~QuadMeshEmitterPrimary(); virtual ~QuadMeshEmitterPrimary();
// Metodo principale chiamato all'inizio di ogni evento // Metodo principale chiamato all'inizio di ogni evento
virtual void GeneratePrimaries(G4Event*) override; virtual void GeneratePrimaries(G4Event*);
void SetMesh(uLib::QuadMesh* mesh); void SetMesh(uLib::QuadMesh* mesh);

View File

@@ -105,8 +105,6 @@ void Solid::SetTransform(Matrix4f transform) {
m_Physical->SetTranslation(*m_Position); m_Physical->SetTranslation(*m_Position);
m_Physical->SetRotation(m_Rotation); m_Physical->SetRotation(m_Rotation);
} }
std::cout << "Solid " << GetName() << " position: " << pos << " rotation: " << m << std::endl;
} }
void Solid::SetParent(Solid *parent) { void Solid::SetParent(Solid *parent) {

View File

@@ -37,8 +37,6 @@ list(APPEND HEADERS ${HEP_GEANT_HEADERS})
set(LIBRARIES Eigen3::Eigen set(LIBRARIES Eigen3::Eigen
${ROOT_LIBRARIES} ${ROOT_LIBRARIES}
${VTK_LIBRARIES} ${VTK_LIBRARIES}
VTK::ImagingHybrid
VTK::ImagingSources
${PACKAGE_LIBPREFIX}Math ${PACKAGE_LIBPREFIX}Math
${PACKAGE_LIBPREFIX}Detectors ${PACKAGE_LIBPREFIX}Detectors
${PACKAGE_LIBPREFIX}Geant) ${PACKAGE_LIBPREFIX}Geant)
@@ -58,7 +56,7 @@ set_target_properties(${libname} PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTOUIC ON AUTOUIC ON
AUTORCC ON) AUTORCC ON)
target_link_libraries(${libname} PUBLIC ${LIBRARIES} Qt6::Widgets) target_link_libraries(${libname} ${LIBRARIES} Qt6::Widgets)
install(TARGETS ${libname} install(TARGETS ${libname}
EXPORT "uLibTargets" EXPORT "uLibTargets"

View File

@@ -61,8 +61,8 @@ int main(int argc, char** argv) {
vtkTess.AddToViewer(viewer); vtkTess.AddToViewer(viewer);
// Color them differently // Color them differently
vtkBox.SetColor(0.8, 0.2, 0.2); // Redish box vtkActor::SafeDownCast(vtkBox.GetProp())->GetProperty()->SetColor(0.8, 0.2, 0.2); // Redish box
vtkTess.SetColor(0.2, 0.8, 0.2); // Greenish tess vtkActor::SafeDownCast(vtkTess.GetProp())->GetProperty()->SetColor(0.2, 0.8, 0.2); // Greenish tess
// Position tessellated solid away from box // Position tessellated solid away from box
Matrix4f trans = Matrix4f::Identity(); Matrix4f trans = Matrix4f::Identity();

View File

@@ -10,74 +10,47 @@
//////////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////////*/
#include "vtkBoxSolid.h" #include "vtkBoxSolid.h"
#include "Core/Monitor.h"
#include <vtkCubeSource.h> #include <vtkCubeSource.h>
#include <vtkPolyDataMapper.h> #include <vtkPolyDataMapper.h>
#include <vtkActor.h> #include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkAssembly.h>
#include <vtkTransform.h>
#include <vtkMatrix4x4.h>
#include <Geant4/G4VPhysicalVolume.hh>
#include "Vtk/Math/vtkDense.h"
namespace uLib { namespace uLib {
namespace Vtk { namespace Vtk {
vtkBoxSolid::vtkBoxSolid(Geant::BoxSolid *content) vtkBoxSolid::vtkBoxSolid(Geant::BoxSolid *content)
: vtkGeantSolid(content), m_BoxContent(content) { : vtkGeantSolid(content), m_BoxContent(content) {
this->InstallPipe(); // Re-run Update for box-specific pipe
// Connect the model's Updated event to updateTransform to ensure VTK sync
m_UpdateConnection = Object::connect(m_BoxContent, &uLib::Object::Updated, this, &vtkBoxSolid::Update);
// Initial sync
this->Update(); this->Update();
} }
vtkBoxSolid::~vtkBoxSolid() { vtkBoxSolid::~vtkBoxSolid() {}
}
void vtkBoxSolid::Update() { void vtkBoxSolid::Update() {
ConnectionBlock blocker(m_UpdateConnection);
this->UpdateGeometry(); this->UpdateGeometry();
// Ensure base Puppet properties (color, opacity, etc) and transform are applied this->UpdateTransform();
this->Puppet::Update();
}
void vtkBoxSolid::SyncFromVtk() {
this->Puppet::SyncFromVtk();
if (auto* proxy = vtkProp3D::SafeDownCast(this->GetProxyProp())) {
if (vtkMatrix4x4* mat = proxy->GetUserMatrix()) {
m_BoxContent->SetTransform(VtkToMatrix4f(mat));
}
}
} }
void vtkBoxSolid::UpdateGeometry() { void vtkBoxSolid::UpdateGeometry() {
// Sync geometry from G4VSolid provided by vtkGeantSolid (tessellation) if (!m_BoxContent || !m_BoxContent->GetObject()) {
// Fallback to base tessellation if no model object
vtkGeantSolid::UpdateGeometry(); vtkGeantSolid::UpdateGeometry();
return;
} }
void vtkBoxSolid::UpdateTransform() { // Use the underlying ContainerBox for precise geometry
// Take transform from Puppet base (which uses GetContent() -> ContainerBox TRS) Vector3f size = m_BoxContent->GetObject()->GetSize();
this->Puppet::Update();
}
void vtkBoxSolid::serialize_display(uLib::Archive::display_properties_archive &ar, vtkNew<vtkCubeSource> cube;
const unsigned int version) { cube->SetXLength(size(0));
// Expose Geant solid properties and underlying Box/TRS properties cube->SetYLength(size(1));
this->Puppet::serialize_display(ar, version); cube->SetZLength(size(2));
if (m_BoxContent) { cube->Update();
ar & NVP("Box", *m_BoxContent);
if (m_BoxContent->GetObject()) {
ar & NVP("Container", *m_BoxContent->GetObject());
}
}
}
void vtkBoxSolid::InstallPipe() { vtkPolyData *poly = GetPolyData();
vtkGeantSolid::InstallPipe(); if (poly) {
poly->ShallowCopy(cube->GetOutput());
poly->Modified();
}
} }
} // namespace Vtk } // namespace Vtk

View File

@@ -26,15 +26,8 @@
#ifndef U_VTKBOXSOLID_H #ifndef U_VTKBOXSOLID_H
#define U_VTKBOXSOLID_H #define U_VTKBOXSOLID_H
#include "Core/Types.h"
#include "Core/Property.h"
#include "Core/Serializable.h"
#include "vtkGeantSolid.h" #include "vtkGeantSolid.h"
class vtkCubeSource;
class vtkActor;
namespace uLib { namespace uLib {
namespace Vtk { namespace Vtk {
@@ -42,36 +35,15 @@ namespace Vtk {
* @brief VTK Puppet for visualizing a Geant::BoxSolid. * @brief VTK Puppet for visualizing a Geant::BoxSolid.
*/ */
class vtkBoxSolid : public vtkGeantSolid { class vtkBoxSolid : public vtkGeantSolid {
uLibTypeMacro(vtkBoxSolid, uLib::Vtk::vtkGeantSolid)
public: public:
vtkBoxSolid(Geant::BoxSolid *content); vtkBoxSolid(Geant::BoxSolid *content);
virtual ~vtkBoxSolid(); virtual ~vtkBoxSolid();
virtual void Update() override; virtual void Update() override;
virtual void UpdateGeometry() override; virtual void UpdateGeometry() override;
virtual void UpdateTransform() override;
virtual void SyncFromVtk() override;
virtual uLib::Object *GetContent() const override {
return m_BoxContent ? (::uLib::Object*)m_BoxContent->GetObject() : nullptr;
}
virtual void serialize_display(uLib::Archive::display_properties_archive &ar,
const unsigned int version = 0) override;
template <typename Ar>
void serialize(Ar &ar, const unsigned int version) {
ar & NVP("BoxSolid", *m_BoxContent);
}
protected: protected:
virtual void InstallPipe() override;
Geant::BoxSolid *m_BoxContent; Geant::BoxSolid *m_BoxContent;
uLib::Connection m_UpdateConnection;
ULIB_DECLARE_PROPERTIES(vtkBoxSolid)
}; };
} // namespace Vtk } // namespace Vtk

View File

@@ -43,7 +43,7 @@ public:
virtual class vtkPolyData *GetPolyData() const override; virtual class vtkPolyData *GetPolyData() const override;
virtual void Update() override; virtual void Update();
protected: protected:
virtual void InstallPipe(); virtual void InstallPipe();

View File

@@ -82,7 +82,6 @@ void vtkGeantSolid::UpdateGeometry() {
int nVertices = polyhedron->GetNoVertices(); int nVertices = polyhedron->GetNoVertices();
for (int i = 1; i <= nVertices; ++i) { for (int i = 1; i <= nVertices; ++i) {
G4Point3D vtx = polyhedron->GetVertex(i); G4Point3D vtx = polyhedron->GetVertex(i);
points->InsertNextPoint(vtx.x(), vtx.y(), vtx.z()); points->InsertNextPoint(vtx.x(), vtx.y(), vtx.z());
} }

View File

@@ -37,17 +37,14 @@ int main() {
BEGIN_TESTING(vtk ContainerBox Test); BEGIN_TESTING(vtk ContainerBox Test);
ContainerBox box; ContainerBox box;
box.Scale(Vector3f(1_m,2_m,1_m)); box.Scale(Vector3f(1_m,5_m,1_m));
box.SetPosition(Vector3f(0,0,0)); box.SetPosition(Vector3f(0,1_m,0));
Vtk::vtkContainerBox v_box(&box); Vtk::vtkContainerBox v_box(&box);
v_box.Update(); v_box.SetRepresentation(Vtk::Puppet::Surface);
v_box.SetOpacity(0.5);
v_box.SetSelectable(true);
// v_box.SetRepresentation(Vtk::Puppet::Surface); box.findOrAddSignal(&ContainerBox::Updated)->connect([&box](){
// 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; std::cout << "box updated: " << box.GetWorldPoint(HPoint3f(1,1,1)) << std::endl;
}); });

View File

@@ -51,12 +51,13 @@ struct ContainerBoxData {
vtkSmartPointer<vtkActor> m_Cube; vtkSmartPointer<vtkActor> m_Cube;
vtkSmartPointer<vtkActor> m_Axes; vtkSmartPointer<vtkActor> m_Axes;
vtkSmartPointer<vtkAssembly> m_VtkAsm; vtkSmartPointer<vtkAssembly> m_VtkAsm;
vtkSmartPointer<vtkMatrix4x4> m_Affine;
uLib::Connection m_UpdateSignal; uLib::Connection m_UpdateSignal;
ContainerBoxData() : m_Cube(vtkSmartPointer<vtkActor>::New()), ContainerBoxData() : m_Cube(vtkSmartPointer<vtkActor>::New()),
m_Axes(vtkSmartPointer<vtkActor>::New()), m_Axes(vtkSmartPointer<vtkActor>::New()),
m_VtkAsm(vtkSmartPointer<vtkAssembly>::New()) {} m_VtkAsm(vtkSmartPointer<vtkAssembly>::New()),
m_Affine(vtkSmartPointer<vtkMatrix4x4>::New()) {}
~ContainerBoxData() { ~ContainerBoxData() {
} }
}; };
@@ -85,13 +86,14 @@ void vtkContainerBox::Update() {
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex); RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
if (!m_Content) return; if (!m_Content) return;
vtkProp3D* prop = vtkProp3D::SafeDownCast(this->GetProp()); vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (prop) { if (root) {
// Apply the full volume matrix (TRS * m_LocalT) // Apply local full matrix (TRS * LocalBox) so that nested assemblies work correctly
Matrix4f fullLocal = m_Content->GetMatrix() * m_Content->GetLocalMatrix();
vtkNew<vtkMatrix4x4> m; vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content->GetMatrix(), m); Matrix4fToVtk(fullLocal, m);
prop->SetUserMatrix(m); root->SetUserMatrix(m);
prop->Modified(); root->Modified();
} }
// Delegate rest of update (appearance, render, etc) // Delegate rest of update (appearance, render, etc)
@@ -100,8 +102,6 @@ void vtkContainerBox::Update() {
} }
void vtkContainerBox::SyncFromVtk() { void vtkContainerBox::SyncFromVtk() {
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex); RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
if (!m_Content) return; if (!m_Content) return;
@@ -111,11 +111,18 @@ void vtkContainerBox::SyncFromVtk() {
// VTK -> Model: Extract new world TRS from proxy, which matches the model's TRS center // VTK -> Model: Extract new world TRS from proxy, which matches the model's TRS center
vtkMatrix4x4* rootMat = root->GetUserMatrix(); vtkMatrix4x4* rootMat = root->GetUserMatrix();
// if (rootMat) {
// std::cout << "[vtkContainerBox::SyncFromVtk] Read Proxy UserMatrix:" << std::endl;
// rootMat->Print(std::cout);
// }
Matrix4f vtkWorld = VtkToMatrix4f(rootMat); Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
// Synchronize TRS property members from the updated local matrix // Synchronize TRS property members from the updated local matrix
m_Content->FromMatrix(vtkWorld); m_Content->FromMatrix(vtkWorld);
// std::cout << "[vtkContainerBox::SyncFromVtk] New Model WorldMatrix:" << std::endl << m_Content->GetWorldMatrix() << std::endl;
// Since we modified the model, notify observers, but block the loop back to VTK // Since we modified the model, notify observers, but block the loop back to VTK
// ConnectionBlock blocker(d->m_UpdateSignal); // ConnectionBlock blocker(d->m_UpdateSignal);
m_Content->Updated(); m_Content->Updated();
@@ -164,10 +171,11 @@ void vtkContainerBox::InstallPipe() {
d->m_VtkAsm->AddPart(d->m_Axes); d->m_VtkAsm->AddPart(d->m_Axes);
this->SetProp(d->m_VtkAsm); this->SetProp(d->m_VtkAsm);
// vtkProp3D* root = d->m_VtkAsm; vtkProp3D* root = d->m_VtkAsm;
// if (root) { if (root) {
// this->ApplyPuppetTransform(root); d->m_Affine = Matrix4fToVtk(m_Content->GetMatrix());
// } root->SetUserMatrix(d->m_Affine);
}
this->Update(); this->Update();
} }

View File

@@ -38,15 +38,13 @@ namespace Vtk {
struct ContainerBoxData; struct ContainerBoxData;
class vtkContainerBox : public Puppet, public Polydata { class vtkContainerBox : public Puppet, public Polydata {
uLibTypeMacro(vtkContainerBox, Puppet, Polydata)
typedef ContainerBox Content; typedef ContainerBox Content;
public: public:
vtkContainerBox(Content *content); vtkContainerBox(Content *content);
~vtkContainerBox(); ~vtkContainerBox();
virtual class vtkPolyData *GetPolyData() const override; virtual class vtkPolyData *GetPolyData() const;
/** /**
* @brief Updates the VTK representation from the internal state. * @brief Updates the VTK representation from the internal state.
@@ -64,9 +62,9 @@ protected:
virtual void InstallPipe(); virtual void InstallPipe();
struct ContainerBoxData *d; struct ContainerBoxData *d;
ContainerBox *m_Content; Content *m_Content;
bool m_BlockUpdate = false;
ULIB_DECLARE_PROPERTIES(vtkContainerBox)
}; };
} // namespace Vtk } // namespace Vtk

View File

@@ -93,10 +93,18 @@ void vtkCylinder::SyncFromVtk() {
// VTK -> Model: Extract new world TRS from proxy // VTK -> Model: Extract new world TRS from proxy
vtkMatrix4x4* rootMat = root->GetUserMatrix(); vtkMatrix4x4* rootMat = root->GetUserMatrix();
if (rootMat) {
std::cout << "[vtkCylinder::SyncFromVtk] Read Proxy UserMatrix:" << std::endl;
rootMat->Print(std::cout);
}
Matrix4f vtkWorld = VtkToMatrix4f(rootMat); Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
// Directly sync model from the world matrix // Directly sync model from the world matrix
m_Content->FromMatrix(vtkWorld); m_Content->FromMatrix(vtkWorld);
std::cout << "[vtkCylinder::SyncFromVtk] New Model WorldMatrix:" << std::endl << m_Content->GetWorldMatrix() << std::endl;
m_Content->Updated(); m_Content->Updated();
} }

View File

@@ -100,7 +100,6 @@ public:
~PuppetData() { ~PuppetData() {
// No manual Delete needed for smart pointers // No manual Delete needed for smart pointers
} }
Puppet *m_Puppet; Puppet *m_Puppet;
@@ -113,10 +112,9 @@ public:
vtkSmartPointer<vtkCubeAxesActor> m_CubeAxesActor; vtkSmartPointer<vtkCubeAxesActor> m_CubeAxesActor;
vtkSmartPointer<vtkActor> m_HighlightActor; vtkSmartPointer<vtkActor> m_HighlightActor;
// Display properties
bool m_ShowBoundingBox; bool m_ShowBoundingBox;
bool m_ShowScaleMeasures; bool m_ShowScaleMeasures;
int m_Representation; // 0: Points, 1: Wireframe, 2: Surface, 3: SurfaceWithEdges, 4: Volume, 5: Outline, 6: Slice int m_Representation;
Vector3d m_Color; Vector3d m_Color;
double m_Opacity; double m_Opacity;
@@ -181,10 +179,11 @@ public:
void UpdateHighlight() { void UpdateHighlight() {
if (m_Selected) { if (m_Selected) {
// Find first polydata in assembly to highlight
vtkPolyData* polydata = nullptr; vtkPolyData* polydata = nullptr;
if (vtkActor *actor = vtkActor::SafeDownCast(m_Prop)) { if (vtkActor *actor = vtkActor::SafeDownCast(m_Prop)) {
if (actor->GetMapper()) { if (actor->GetMapper()) {
polydata = vtkPolyData::SafeDownCast(actor->GetMapper()->GetInput()); polydata = vtkPolyData::SafeDownCast(actor->GetMapper()->GetDataSetInput());
} }
} else if (vtkAssembly *asm_p = vtkAssembly::SafeDownCast(m_Prop)) { } else if (vtkAssembly *asm_p = vtkAssembly::SafeDownCast(m_Prop)) {
vtkPropCollection *parts = asm_p->GetParts(); vtkPropCollection *parts = asm_p->GetParts();
@@ -193,7 +192,7 @@ public:
for (int i = 0; i < parts->GetNumberOfItems(); ++i) { for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
vtkActor *a = vtkActor::SafeDownCast(parts->GetNextProp()); vtkActor *a = vtkActor::SafeDownCast(parts->GetNextProp());
if (a && a->GetMapper()) { if (a && a->GetMapper()) {
polydata = vtkPolyData::SafeDownCast(a->GetMapper()->GetInput()); polydata = vtkPolyData::SafeDownCast(a->GetMapper()->GetDataSetInput());
if (polydata) break; if (polydata) break;
} }
} }
@@ -561,55 +560,28 @@ bool Puppet::IsSelected() const
return pd->m_Selected; return pd->m_Selected;
} }
void Puppet::ApplyPuppetTransform(vtkProp3D* prop)
{
if (!prop) return;
if (auto* content = this->GetContent()) {
if (auto* tr = dynamic_cast<uLib::TRS*>(content)) {
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(tr->GetMatrix(), m);
prop->SetUserMatrix(m);
prop->Modified();
}
}
}
void Puppet::SyncFromVtk()
{
if (auto* content = this->GetContent()) {
if (auto* tr = dynamic_cast<uLib::TRS*>(content)) {
if (auto* proxy = this->GetProxyProp()) {
if (vtkMatrix4x4* mat = proxy->GetUserMatrix()) {
tr->FromMatrix(VtkToMatrix4f(mat));
content->Updated();
}
}
}
}
}
void Puppet::Update() void Puppet::Update()
{ {
// Apply content transform via virtual GetProp() / ApplyPuppetTransform(), // Derived classes should have updated the transform if they override Update()
// so all derived classes benefit without duplicating the matrix code. // or we can apply base transform if it's default:
this->ApplyPuppetTransform(vtkProp3D::SafeDownCast(this->GetProp())); // pd->ApplyTransform(pd->m_Prop);
// Use virtual GetProp() for appearance so overriders (e.g. vtkVoxImage) pd->ApplyAppearance(pd->m_Prop);
// that never call SetProp() are handled correctly.
pd->ApplyAppearance(this->GetProp());
if (pd->m_Selected) { if (pd->m_Selected) {
pd->UpdateHighlight(); pd->UpdateHighlight();
} }
if (auto* prop = this->GetProp()) { if (pd->m_Prop) {
if (pd->m_ShowBoundingBox && pd->m_OutlineSource) { if (pd->m_ShowBoundingBox) {
double* bounds = prop->GetBounds(); double* bounds = pd->m_Prop->GetBounds();
pd->m_OutlineSource->SetBounds(bounds); pd->m_OutlineSource->SetBounds(bounds);
pd->m_OutlineSource->Update(); pd->m_OutlineSource->Update();
} }
if (pd->m_ShowScaleMeasures && pd->m_CubeAxesActor) {
pd->m_CubeAxesActor->SetBounds(prop->GetBounds()); if (pd->m_ShowScaleMeasures) {
double* bounds = pd->m_Prop->GetBounds();
pd->m_CubeAxesActor->SetBounds(bounds);
} }
} }
@@ -626,7 +598,6 @@ void Puppet::Update()
} }
void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor) void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor)
{ {
} }

View File

@@ -35,8 +35,6 @@
#include <iomanip> #include <iomanip>
#include <ostream> #include <ostream>
#include <vector> #include <vector>
#include <set>
#include <boost/type_traits/is_base_of.hpp>
// vtk classes forward declaration // // vtk classes forward declaration //
class vtkProp; class vtkProp;
@@ -64,8 +62,7 @@ class Puppet : public uLib::Object {
uLibTypeMacro(Puppet, uLib::Object) uLibTypeMacro(Puppet, uLib::Object)
public: public : Puppet();
Puppet();
virtual ~Puppet(); virtual ~Puppet();
virtual vtkProp *GetProp(); virtual vtkProp *GetProp();
@@ -109,7 +106,7 @@ public:
* This method should be called when the VTK representation has been modified * This method should be called when the VTK representation has been modified
* (e.g., via a gizmo) and the changes need to be pushed back to the model. * (e.g., via a gizmo) and the changes need to be pushed back to the model.
*/ */
virtual void SyncFromVtk(); virtual void SyncFromVtk() {}
enum Representation { enum Representation {
Points = 0, Points = 0,
@@ -130,10 +127,10 @@ public:
vtkRendererCollection *GetRenderers() const; vtkRendererCollection *GetRenderers() const;
const std::vector<uLib::PropertyBase *> &GetDisplayProperties() const override { const std::vector<uLib::PropertyBase *> &GetDisplayProperties() const {
return m_DisplayProperties; return m_DisplayProperties;
} }
void RegisterDisplayProperty(uLib::PropertyBase *prop) override { void RegisterDisplayProperty(uLib::PropertyBase *prop) {
m_DisplayProperties.push_back(prop); m_DisplayProperties.push_back(prop);
} }
@@ -150,9 +147,8 @@ protected:
void RemoveProp(vtkProp *prop); void RemoveProp(vtkProp *prop);
virtual void ApplyAppearance(vtkProp *prop); void ApplyAppearance(vtkProp *prop);
virtual void ApplyTransform(vtkProp3D *p3d); void ApplyTransform(vtkProp3D *p3d);
virtual void ApplyPuppetTransform(vtkProp3D *p3d);
std::vector<uLib::PropertyBase *> m_DisplayProperties; std::vector<uLib::PropertyBase *> m_DisplayProperties;
mutable uLib::RecursiveMutex m_UpdateMutex; mutable uLib::RecursiveMutex m_UpdateMutex;
@@ -168,10 +164,6 @@ private:
} // namespace Vtk } // namespace Vtk
} // namespace uLib } // namespace uLib
// -------------------------------------------------------------------------- // // -------------------------------------------------------------------------- //
// DISPLAY PROPERTIES SERIALIZE // DISPLAY PROPERTIES SERIALIZE
// -------------------------------------------------------------------------- // // -------------------------------------------------------------------------- //
@@ -187,17 +179,10 @@ class display_properties_archive
: public boost::archive::detail::common_oarchive< : public boost::archive::detail::common_oarchive<
display_properties_archive> { display_properties_archive> {
public: public:
friend class boost::archive::detail::interface_oarchive<display_properties_archive>; display_properties_archive(Vtk::Puppet *puppet)
friend class boost::archive::save_access;
using boost::archive::detail::common_oarchive<display_properties_archive>::save_override;
display_properties_archive(Vtk::Puppet *p)
: boost::archive::detail::common_oarchive<display_properties_archive>( : boost::archive::detail::common_oarchive<display_properties_archive>(
boost::archive::no_header), boost::archive::no_header),
m_Puppet(p) { m_Puppet(puppet) {}
if (p)
m_Visited.insert(dynamic_cast<const void *>(p));
}
std::string GetCurrentGroup() const { std::string GetCurrentGroup() const {
std::string group; std::string group;
@@ -249,24 +234,6 @@ public:
m_GroupStack.pop_back(); m_GroupStack.pop_back();
} }
// Follow pointers to discover properties in child objects
template<class T>
void save_override(T * const & t) {
if (!t) return;
this->save_pointer_helper(t, typename boost::is_base_of<uLib::Object, T>::type());
}
template<class T>
void save_pointer_helper(T* t, boost::mpl::true_) {
const void* ptr = dynamic_cast<const void*>(t);
if (m_Visited.find(ptr) != m_Visited.end()) return;
m_Visited.insert(ptr);
this->save_override(*t);
}
template<class T>
void save_pointer_helper(T* t, boost::mpl::false_) {}
// Recursion for nested classes, ignore primitives // Recursion for nested classes, ignore primitives
template <class T> void save_override(const T &t) { template <class T> void save_override(const T &t) {
this->save_helper(t, typename boost::is_class<T>::type()); this->save_helper(t, typename boost::is_class<T>::type());
@@ -276,8 +243,6 @@ public:
boost::serialization::serialize_adl(*this, const_cast<T &>(t), 0); boost::serialization::serialize_adl(*this, const_cast<T &>(t), 0);
} }
void save_helper(const std::string &t, boost::mpl::true_) {}
template <class T> void save_helper(const T &t, boost::mpl::false_) {} template <class T> void save_helper(const T &t, boost::mpl::false_) {}
void save_override(const boost::archive::object_id_type &t) {} void save_override(const boost::archive::object_id_type &t) {}
@@ -289,25 +254,9 @@ public:
void save_override(const boost::archive::class_name_type &t) {} void save_override(const boost::archive::class_name_type &t) {}
void save_override(const boost::archive::tracking_type &t) {} void save_override(const boost::archive::tracking_type &t) {}
// Called by Property<T>::serialize() and EnumProperty::serialize() to
// directly register an existing property object as a display property.
void register_property(uLib::PropertyBase &p) {
if (m_Puppet) {
m_Puppet->RegisterDisplayProperty(&p);
Vtk::Puppet *puppet = m_Puppet;
uLib::Object::connect(&p, &uLib::PropertyBase::Updated,
[puppet]() { puppet->Update(); });
}
}
void register_enum_property(uLib::EnumProperty &p) {
register_property(p);
}
private: private:
Vtk::Puppet *m_Puppet; Vtk::Puppet *m_Puppet;
std::vector<std::string> m_GroupStack; std::vector<std::string> m_GroupStack;
std::set<const void *> m_Visited;
}; };
} // namespace Archive } // namespace Archive