add vtk Properties
This commit is contained in:
@@ -64,6 +64,7 @@ public:
|
||||
Vector<Signal> sigv;
|
||||
Vector<Slot> slov;
|
||||
std::vector<PropertyBase*> m_Properties;
|
||||
std::vector<PropertyBase*> m_DynamicProperties;
|
||||
};
|
||||
|
||||
// Implementations of Property methods
|
||||
@@ -71,6 +72,13 @@ void Object::RegisterProperty(PropertyBase* prop) {
|
||||
if (prop) d->m_Properties.push_back(prop);
|
||||
}
|
||||
|
||||
void Object::RegisterDynamicProperty(PropertyBase* prop) {
|
||||
if (prop) {
|
||||
d->m_DynamicProperties.push_back(prop);
|
||||
d->m_Properties.push_back(prop);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<PropertyBase*>& Object::GetProperties() const {
|
||||
return d->m_Properties;
|
||||
}
|
||||
@@ -109,7 +117,12 @@ Object::Object() : d(new ObjectPrivate) {}
|
||||
|
||||
Object::Object(const Object ©) : d(new ObjectPrivate(*copy.d)) {}
|
||||
|
||||
Object::~Object() { delete d; }
|
||||
Object::~Object() {
|
||||
for (auto* p : d->m_DynamicProperties) {
|
||||
delete p;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
void Object::DeepCopy(const Object ©) {
|
||||
// should lock to be tread safe //
|
||||
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// PROPERTIES //
|
||||
void RegisterProperty(PropertyBase* prop);
|
||||
void RegisterDynamicProperty(PropertyBase* prop);
|
||||
const std::vector<PropertyBase*>& GetProperties() const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -42,38 +42,50 @@ public:
|
||||
template <typename T>
|
||||
class Property : public PropertyBase {
|
||||
public:
|
||||
Property(Object* owner, const std::string& name, const T& defaultValue = T())
|
||||
: m_owner(owner), m_name(name), m_value(defaultValue) {
|
||||
// PROXY: Use an existing variable as back-end storage
|
||||
Property(Object* owner, const std::string& name, T* valuePtr)
|
||||
: m_owner(owner), m_name(name), m_value(valuePtr), m_own(false) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
}
|
||||
virtual ~Property() {}
|
||||
|
||||
// MANAGED: Create and own internal storage
|
||||
Property(Object* owner, const std::string& name, const T& defaultValue = T())
|
||||
: m_owner(owner), m_name(name), m_value(new T(defaultValue)), m_own(true) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Property() {
|
||||
if (m_own) delete m_value;
|
||||
}
|
||||
|
||||
const std::string& GetName() const override { return m_name; }
|
||||
const char* GetTypeName() const override { return typeid(T).name(); }
|
||||
|
||||
std::string GetValueAsString() const override {
|
||||
try {
|
||||
return boost::lexical_cast<std::string>(m_value);
|
||||
return boost::lexical_cast<std::string>(*m_value);
|
||||
} catch (const boost::bad_lexical_cast&) {
|
||||
std::stringstream ss;
|
||||
ss << m_value;
|
||||
ss << *m_value;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
const T& Get() const { return m_value; }
|
||||
const T& Get() const { return *m_value; }
|
||||
void Set(const T& value) {
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
if (*m_value != value) {
|
||||
*m_value = value;
|
||||
ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -86,7 +98,7 @@ public:
|
||||
// Serialization
|
||||
template <class ArchiveT>
|
||||
void serialize_impl(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp(m_name.c_str(), m_value);
|
||||
ar & boost::serialization::make_nvp(m_name.c_str(), *m_value);
|
||||
}
|
||||
|
||||
void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
@@ -99,7 +111,8 @@ public:
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
T m_value;
|
||||
T* m_value;
|
||||
bool m_own;
|
||||
Object* m_owner;
|
||||
};
|
||||
|
||||
@@ -118,11 +131,91 @@ typedef Property<Bool_t> BoolProperty;
|
||||
/**
|
||||
* @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) \
|
||||
Property<type> name = Property<type>(this, #name, defaultValue);
|
||||
type m_##name = defaultValue; \
|
||||
Property<type> name = Property<type>(this, #name, &m_##name);
|
||||
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
namespace uLib {
|
||||
namespace Archive {
|
||||
class property_register_archive;
|
||||
} // namespace Archive
|
||||
} // namespace uLib
|
||||
|
||||
namespace boost {
|
||||
namespace archive {
|
||||
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;
|
||||
public:
|
||||
friend class boost::archive::detail::interface_oarchive<property_register_archive>;
|
||||
friend class boost::archive::save_access;
|
||||
|
||||
typedef boost::archive::detail::common_oarchive<property_register_archive> detail_common_oarchive;
|
||||
|
||||
property_register_archive(Object* obj) :
|
||||
boost::archive::detail::common_oarchive<property_register_archive>(boost::archive::no_header),
|
||||
m_Object(obj) {}
|
||||
|
||||
// Core logic: encounter HRP -> Create Dynamic Property
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::hrp<T> &t) {
|
||||
if (m_Object) {
|
||||
// We use const_cast because we are just creating a proxy to the member
|
||||
m_Object->RegisterDynamicProperty(
|
||||
new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle standard NVPs by recursing (important for base classes)
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::nvp<T> &t) {
|
||||
this->detail_common_oarchive::save_override(t.const_value());
|
||||
}
|
||||
|
||||
// Ignore everything else
|
||||
template<class T> void save_override(const T &t) {}
|
||||
|
||||
// Required attribute overrides for common_oarchive
|
||||
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::version_type & t) {}
|
||||
void save_override(const boost::archive::class_id_type & t) {}
|
||||
void save_override(const boost::archive::class_id_optional_type & t) {}
|
||||
void save_override(const boost::archive::class_id_reference_type & t) {}
|
||||
void save_override(const boost::archive::class_name_type & t) {}
|
||||
void save_override(const boost::archive::tracking_type & t) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convenience macro to automatically activate and register all HRP members
|
||||
* as uLib properties. Usage: ULIB_ACTIVATE_PROPERTIES(obj)
|
||||
*/
|
||||
#define ULIB_ACTIVATE_PROPERTIES(obj) \
|
||||
{ uLib::Archive::property_register_archive _ar_tmp(&(obj)); (obj).serialize(_ar_tmp, 0); }
|
||||
|
||||
} // namespace Archive
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_PROPERTY_H
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Property.h"
|
||||
#include "Core/Serializable.h"
|
||||
#include "Core/Archives.h"
|
||||
|
||||
@@ -8,13 +10,29 @@ using namespace uLib;
|
||||
struct SimpleObject {
|
||||
int value;
|
||||
std::string name;
|
||||
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int version) {
|
||||
ar & HRP(value);
|
||||
ar & HRP(name);
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicObject : public Object {
|
||||
float width;
|
||||
int height;
|
||||
|
||||
DynamicObject() : width(10.0f), height(20) {
|
||||
// Automatic registration of properties based on serialize/HRP
|
||||
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int version) {
|
||||
ar & HRP(width);
|
||||
ar & HRP(height);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SimpleObject obj;
|
||||
@@ -45,5 +63,21 @@ int main() {
|
||||
}
|
||||
std::cout << ss.str() << std::endl;
|
||||
|
||||
std::cout << "Testing Dynamic Property Creation via ULIB_ACTIVATE_PROPERTIES macro..." << std::endl;
|
||||
DynamicObject dynObj;
|
||||
// (properties were already created in DynamicObject constructor via macro)
|
||||
|
||||
std::cout << "Registered Properties in dynObj:" << std::endl;
|
||||
const auto& props = dynObj.GetProperties();
|
||||
for (auto* p : props) {
|
||||
std::cout << " - [" << p->GetTypeName() << "] " << p->GetName() << " = " << p->GetValueAsString() << std::endl;
|
||||
}
|
||||
|
||||
if (props.size() == 2) {
|
||||
std::cout << "Dynamic Property Creation SUCCESS!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Dynamic Property Creation FAILED (Expected 2, got " << props.size() << ")" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ set( TESTS
|
||||
vtkViewerTest
|
||||
vtkContainerBoxTest
|
||||
vtkHandlerWidget
|
||||
PuppetPropertyTest
|
||||
# vtkVoxImageTest
|
||||
# vtkTriangleMeshTest
|
||||
)
|
||||
|
||||
68
src/Vtk/testing/PuppetPropertyTest.cpp
Normal file
68
src/Vtk/testing/PuppetPropertyTest.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
|
||||
All rights reserved
|
||||
|
||||
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#include "Vtk/uLibVtkInterface.h"
|
||||
#include "Core/Property.h"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "testing-prototype.h"
|
||||
|
||||
using namespace uLib;
|
||||
using namespace Vtk;
|
||||
|
||||
int main() {
|
||||
BEGIN_TESTING(Puppet Property Registration Test);
|
||||
|
||||
std::cout << "Creating Puppet object..." << std::endl;
|
||||
Puppet p;
|
||||
|
||||
// At this point, the Puppet constructor has called ULIB_ACTIVATE_PROPERTIES.
|
||||
// This should have discovered the members used in Puppet::serialize()
|
||||
// and registered them as uLib properties.
|
||||
|
||||
const auto& props = p.GetProperties();
|
||||
std::cout << "Total registered properties: " << props.size() << std::endl;
|
||||
|
||||
// Verify specific properties exist
|
||||
Property<double>* opacityProp = nullptr;
|
||||
Property<double>* colorRProp = nullptr;
|
||||
|
||||
for (auto* prop : props) {
|
||||
std::cout << " - [" << prop->GetTypeName() << "] " << prop->GetName()
|
||||
<< " = " << prop->GetValueAsString() << std::endl;
|
||||
|
||||
if (prop->GetName() == "Opacity") {
|
||||
opacityProp = dynamic_cast<Property<double>*>(prop);
|
||||
}
|
||||
if (prop->GetName() == "ColorR") {
|
||||
colorRProp = dynamic_cast<Property<double>*>(prop);
|
||||
}
|
||||
}
|
||||
|
||||
assert(opacityProp != nullptr && "Opacity property not registered!");
|
||||
assert(colorRProp != nullptr && "ColorR property not registered!");
|
||||
|
||||
// Test modification via uLib Property interface
|
||||
std::cout << "Modifying Opacity via property proxy (0.25)..." << std::endl;
|
||||
*opacityProp = 0.25;
|
||||
|
||||
// Verify the proxy correctly updated the underlying data
|
||||
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 << "All Puppet Property Registration Tests PASSED!" << std::endl;
|
||||
|
||||
END_TESTING;
|
||||
}
|
||||
@@ -60,6 +60,7 @@
|
||||
|
||||
#include "uLibVtkInterface.h"
|
||||
#include "vtkHandlerWidget.h"
|
||||
#include "Core/Property.h"
|
||||
|
||||
|
||||
|
||||
@@ -197,7 +198,12 @@ public:
|
||||
|
||||
|
||||
|
||||
Puppet::Puppet() : d(new PuppetData) { }
|
||||
Puppet::Puppet() : Object(), d(new PuppetData) {
|
||||
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||
for (auto* p : this->GetProperties()) {
|
||||
uLib::Object::connect(p, &uLib::PropertyBase::Updated, this, &Puppet::Update);
|
||||
}
|
||||
}
|
||||
|
||||
Puppet::~Puppet()
|
||||
{
|
||||
@@ -442,6 +448,12 @@ bool Puppet::IsSelected() const
|
||||
|
||||
void Puppet::Update()
|
||||
{
|
||||
vtkProp3DCollection *props = d->m_Assembly->GetParts();
|
||||
props->InitTraversal();
|
||||
for (int i = 0; i < props->GetNumberOfItems(); ++i) {
|
||||
d->ApplyAppearance(props->GetNextProp3D());
|
||||
}
|
||||
|
||||
if (d->m_Selected) {
|
||||
d->UpdateHighlight();
|
||||
}
|
||||
@@ -462,5 +474,19 @@ void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void Puppet::serialize(Archive & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_hrp("ColorR", d->m_Color[0]);
|
||||
ar & boost::serialization::make_hrp("ColorG", d->m_Color[1]);
|
||||
ar & boost::serialization::make_hrp("ColorB", d->m_Color[2]);
|
||||
ar & boost::serialization::make_hrp("Opacity", d->m_Opacity);
|
||||
ar & boost::serialization::make_hrp("Representation", d->m_Representation);
|
||||
}
|
||||
|
||||
void Puppet::save_override(Archive::xml_oarchive & ar, const unsigned int v) { serialize(ar, v); }
|
||||
void Puppet::save_override(Archive::hrt_oarchive & ar, const unsigned int v) { serialize(ar, v); }
|
||||
void Puppet::save_override(Archive::log_archive & ar, const unsigned int v) { serialize(ar, v); }
|
||||
void Puppet::save_override(Archive::text_oarchive & ar, const unsigned int v) { serialize(ar, v); }
|
||||
|
||||
} // namespace Vtk
|
||||
} // namespace uLib
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include "Core/Object.h"
|
||||
|
||||
// vtk classes forward declaration //
|
||||
class vtkProp;
|
||||
@@ -41,7 +42,8 @@ class vtkRenderWindowInteractor;
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
|
||||
class Puppet {
|
||||
class Puppet : public uLib::Object {
|
||||
uLibTypeMacro(Puppet, uLib::Object)
|
||||
public:
|
||||
Puppet();
|
||||
~Puppet();
|
||||
@@ -90,6 +92,14 @@ public:
|
||||
|
||||
virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor);
|
||||
|
||||
// Serialization and Reflection
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int version);
|
||||
void save_override(Archive::xml_oarchive & ar, const unsigned int version);
|
||||
void save_override(Archive::hrt_oarchive & ar, const unsigned int version);
|
||||
void save_override(Archive::log_archive & ar, const unsigned int version);
|
||||
void save_override(Archive::text_oarchive & ar, const unsigned int version);
|
||||
|
||||
protected:
|
||||
void SetProp(vtkProp *prop);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Vtk {
|
||||
/**
|
||||
* @brief vtkObjectsContext manages VTK representations (Puppets) for a collection of uLib::Objects.
|
||||
*/
|
||||
class vtkObjectsContext : public uLib::Object, public Puppet {
|
||||
class vtkObjectsContext : public Puppet {
|
||||
public:
|
||||
virtual const char* GetClassName() const override { return "vtkObjectsContext"; }
|
||||
vtkObjectsContext(uLib::ObjectsContext *context);
|
||||
|
||||
Reference in New Issue
Block a user