add assembly to gcompose, not working yet
This commit is contained in:
@@ -4,6 +4,11 @@
|
|||||||
#include <cxxabi.h>
|
#include <cxxabi.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "Core/Object.h"
|
#include "Core/Object.h"
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
ContextModel::ContextModel(QObject* parent)
|
ContextModel::ContextModel(QObject* parent)
|
||||||
: QAbstractItemModel(parent), m_rootContext(nullptr) {}
|
: QAbstractItemModel(parent), m_rootContext(nullptr) {}
|
||||||
@@ -11,12 +16,16 @@ ContextModel::ContextModel(QObject* parent)
|
|||||||
ContextModel::~ContextModel() {}
|
ContextModel::~ContextModel() {}
|
||||||
|
|
||||||
void ContextModel::setContext(uLib::ObjectsContext* context) {
|
void ContextModel::setContext(uLib::ObjectsContext* context) {
|
||||||
|
m_isReseting = true;
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_rootContext = context;
|
m_rootContext = context;
|
||||||
if (m_rootContext) {
|
if (m_rootContext) {
|
||||||
auto refresh = [this]() {
|
auto refresh = [this]() {
|
||||||
|
if (this->m_isReseting) return;
|
||||||
|
this->m_isReseting = true;
|
||||||
this->beginResetModel();
|
this->beginResetModel();
|
||||||
this->endResetModel();
|
this->endResetModel();
|
||||||
|
this->m_isReseting = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
uLib::Object::connect(m_rootContext, &uLib::Object::Updated, refresh);
|
uLib::Object::connect(m_rootContext, &uLib::Object::Updated, refresh);
|
||||||
@@ -25,7 +34,6 @@ void ContextModel::setContext(uLib::ObjectsContext* context) {
|
|||||||
refresh();
|
refresh();
|
||||||
});
|
});
|
||||||
uLib::Object::connect(m_rootContext, &uLib::ObjectsContext::ObjectRemoved, [this, refresh](uLib::Object* obj) {
|
uLib::Object::connect(m_rootContext, &uLib::ObjectsContext::ObjectRemoved, [this, refresh](uLib::Object* obj) {
|
||||||
// Disconnect would be good here but not strictly required if refresh handles it
|
|
||||||
refresh();
|
refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,6 +43,7 @@ void ContextModel::setContext(uLib::ObjectsContext* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
m_isReseting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent) const {
|
QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent) const {
|
||||||
@@ -48,8 +57,8 @@ QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||||
uLib::ObjectsContext* parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj);
|
uLib::ObjectsContext* parentCtx = parentObj->GetChildren();
|
||||||
if (parentCtx && row < parentCtx->GetCount()) {
|
if (parentCtx && row < (int)parentCtx->GetCount()) {
|
||||||
return createIndex(row, column, parentCtx->GetObject(row));
|
return createIndex(row, column, parentCtx->GetObject(row));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,36 +74,37 @@ QModelIndex ContextModel::parent(const QModelIndex& child) const {
|
|||||||
|
|
||||||
// Finding the parent of childObj is O(N) since there is no parent pointer.
|
// Finding the parent of childObj is O(N) since there is no parent pointer.
|
||||||
// We just do a recursive search starting from root context.
|
// We just do a recursive search starting from root context.
|
||||||
std::function<uLib::ObjectsContext*(uLib::ObjectsContext*, uLib::Object*)> findParent =
|
std::function<uLib::Object*(uLib::Object*, uLib::Object*)> findParent =
|
||||||
[&findParent](uLib::ObjectsContext* ctx, uLib::Object* target) -> uLib::ObjectsContext* {
|
[&findParent](uLib::Object* current, uLib::Object* target) -> uLib::Object* {
|
||||||
for (const auto& obj : ctx->GetObjects()) {
|
uLib::ObjectsContext* ctx = current->GetChildren();
|
||||||
if (obj == target) return ctx;
|
if (ctx) {
|
||||||
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
|
for (const auto& obj : ctx->GetObjects()) {
|
||||||
if (auto p = findParent(subCtx, target)) return p;
|
if (obj == target) return current;
|
||||||
|
if (auto p = findParent(obj, target)) return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
uLib::ObjectsContext* parentCtx = findParent(m_rootContext, childObj);
|
uLib::Object* parentObj = findParent(m_rootContext, childObj);
|
||||||
if (!parentCtx || parentCtx == m_rootContext) {
|
if (!parentObj || parentObj == m_rootContext) {
|
||||||
return QModelIndex(); // Root items have invalid parent index
|
return QModelIndex(); // Root items have invalid parent index
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now need to find the row of parentCtx in its own parent Context.
|
// Now need to find the row of parentObj in its own parent Context.
|
||||||
uLib::ObjectsContext* grandParentCtx = findParent(m_rootContext, parentCtx);
|
uLib::Object* grandParentObj = findParent(m_rootContext, parentObj);
|
||||||
if (!grandParentCtx) grandParentCtx = m_rootContext;
|
uLib::ObjectsContext* grandParentCtx = grandParentObj ? grandParentObj->GetChildren() : m_rootContext;
|
||||||
|
|
||||||
int row = -1;
|
int row = -1;
|
||||||
for (size_t i = 0; i < grandParentCtx->GetCount(); ++i) {
|
for (size_t i = 0; i < grandParentCtx->GetCount(); ++i) {
|
||||||
if (grandParentCtx->GetObject(i) == parentCtx) {
|
if (grandParentCtx->GetObject(i) == parentObj) {
|
||||||
row = (int)i;
|
row = (int)i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row != -1) {
|
if (row != -1) {
|
||||||
return createIndex(row, 0, parentCtx);
|
return createIndex(row, 0, parentObj);
|
||||||
}
|
}
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
@@ -107,8 +117,8 @@ int ContextModel::rowCount(const QModelIndex& parent) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||||
if (auto parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj)) {
|
if (auto parentCtx = parentObj->GetChildren()) {
|
||||||
return parentCtx->GetCount();
|
return (int)parentCtx->GetCount();
|
||||||
}
|
}
|
||||||
return 0; // leaf node
|
return 0; // leaf node
|
||||||
}
|
}
|
||||||
@@ -161,8 +171,98 @@ QVariant ContextModel::headerData(int section, Qt::Orientation orientation, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags ContextModel::flags(const QModelIndex& index) const {
|
Qt::ItemFlags ContextModel::flags(const QModelIndex& index) const {
|
||||||
if (!index.isValid()) return Qt::NoItemFlags;
|
if (!index.isValid()) return m_rootContext ? Qt::ItemIsDropEnabled : Qt::NoItemFlags;
|
||||||
return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
||||||
|
Qt::ItemFlags f = Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
|
||||||
|
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
|
||||||
|
if (dynamic_cast<uLib::ObjectsContext*>(obj)) {
|
||||||
|
f |= Qt::ItemIsDropEnabled;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::DropActions ContextModel::supportedDropActions() const {
|
||||||
|
return Qt::MoveAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ContextModel::mimeTypes() const {
|
||||||
|
return {"application/x-ulib-object-ptr"};
|
||||||
|
}
|
||||||
|
|
||||||
|
QMimeData* ContextModel::mimeData(const QModelIndexList& indexes) const {
|
||||||
|
QMimeData* mimeData = new QMimeData();
|
||||||
|
QByteArray encodedData;
|
||||||
|
QDataStream stream(&encodedData, QIODevice::WriteOnly);
|
||||||
|
for (const auto& idx : indexes) {
|
||||||
|
if (idx.isValid() && idx.column() == 0) {
|
||||||
|
void* ptr = idx.internalPointer();
|
||||||
|
stream << reinterpret_cast<qlonglong>(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mimeData->setData("application/x-ulib-object-ptr", encodedData);
|
||||||
|
return mimeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContextModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
|
||||||
|
if (action != Qt::MoveAction || !data->hasFormat("application/x-ulib-object-ptr")) return false;
|
||||||
|
|
||||||
|
uLib::ObjectsContext* targetCtx = m_rootContext;
|
||||||
|
if (parent.isValid()) {
|
||||||
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||||
|
targetCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj);
|
||||||
|
}
|
||||||
|
if (!targetCtx) return false;
|
||||||
|
|
||||||
|
QByteArray encodedData = data->data("application/x-ulib-object-ptr");
|
||||||
|
QDataStream stream(&encodedData, QIODevice::ReadOnly);
|
||||||
|
std::vector<uLib::Object*> objectsToMove;
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
qlonglong ptrVal;
|
||||||
|
stream >> ptrVal;
|
||||||
|
objectsToMove.push_back(reinterpret_cast<uLib::Object*>(ptrVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectsToMove.empty()) return false;
|
||||||
|
|
||||||
|
// Helper to find and remove from current parent
|
||||||
|
std::function<void(uLib::Object*, uLib::Object*)> findAndRemoveRecursive =
|
||||||
|
[&findAndRemoveRecursive](uLib::Object* current, uLib::Object* target) {
|
||||||
|
if (auto ctx = current->GetChildren()) {
|
||||||
|
ctx->RemoveObject(target);
|
||||||
|
for (auto* obj : ctx->GetObjects()) {
|
||||||
|
findAndRemoveRecursive(obj, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m_isReseting = true;
|
||||||
|
beginResetModel();
|
||||||
|
for (auto* obj : objectsToMove) {
|
||||||
|
// Don't drop onto itself or its descendants
|
||||||
|
bool invalid = (obj == targetCtx || obj == (uLib::Object*)targetCtx);
|
||||||
|
if (!invalid) {
|
||||||
|
// check if targetCtx is descendant of obj
|
||||||
|
std::function<bool(uLib::Object*, uLib::Object*)> isDescendant =
|
||||||
|
[&isDescendant](uLib::Object* root, uLib::Object* target) -> bool {
|
||||||
|
if (auto ctx = root->GetChildren()) {
|
||||||
|
for (auto* child : ctx->GetObjects()) {
|
||||||
|
if (child == target) return true;
|
||||||
|
if (isDescendant(child, target)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (isDescendant(obj, (uLib::Object*)targetCtx)) invalid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!invalid) {
|
||||||
|
findAndRemoveRecursive(m_rootContext, obj);
|
||||||
|
targetCtx->AddObject(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endResetModel();
|
||||||
|
m_isReseting = false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContextModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
bool ContextModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||||
|
|||||||
@@ -21,8 +21,15 @@ public:
|
|||||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||||
|
|
||||||
|
// Drag and Drop support
|
||||||
|
Qt::DropActions supportedDropActions() const override;
|
||||||
|
QStringList mimeTypes() const override;
|
||||||
|
QMimeData* mimeData(const QModelIndexList& indexes) const override;
|
||||||
|
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uLib::ObjectsContext* m_rootContext;
|
uLib::ObjectsContext* m_rootContext;
|
||||||
|
bool m_isReseting = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONTEXT_MODEL_H
|
#endif // CONTEXT_MODEL_H
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ ContextPanel::ContextPanel(QWidget* parent)
|
|||||||
m_treeView = new QTreeView(this);
|
m_treeView = new QTreeView(this);
|
||||||
m_treeView->setObjectName("ContextTree");
|
m_treeView->setObjectName("ContextTree");
|
||||||
m_treeView->setHeaderHidden(false);
|
m_treeView->setHeaderHidden(false);
|
||||||
|
m_treeView->setDragEnabled(true);
|
||||||
|
m_treeView->setAcceptDrops(true);
|
||||||
|
m_treeView->setDropIndicatorShown(true);
|
||||||
|
m_treeView->setDragDropMode(QAbstractItemView::DragDrop);
|
||||||
|
|
||||||
m_model = new ContextModel(this);
|
m_model = new ContextModel(this);
|
||||||
m_treeView->setModel(m_model);
|
m_treeView->setModel(m_model);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class polymorphic_oarchive;
|
|||||||
namespace uLib {
|
namespace uLib {
|
||||||
|
|
||||||
class PropertyBase;
|
class PropertyBase;
|
||||||
|
class ObjectsContext;
|
||||||
|
|
||||||
class Version {
|
class Version {
|
||||||
public:
|
public:
|
||||||
@@ -101,6 +102,9 @@ public:
|
|||||||
// FIXX !!!
|
// FIXX !!!
|
||||||
virtual void DeepCopy(const Object ©);
|
virtual void DeepCopy(const Object ©);
|
||||||
|
|
||||||
|
/** @brief Returns a nested context for children objects, if any. */
|
||||||
|
virtual ObjectsContext* GetChildren() { return nullptr; }
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// SERIALIZATION //
|
// SERIALIZATION //
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public:
|
|||||||
virtual ~ObjectsContext();
|
virtual ~ObjectsContext();
|
||||||
|
|
||||||
virtual const char * GetClassName() const { return "ObjectsContext"; }
|
virtual const char * GetClassName() const { return "ObjectsContext"; }
|
||||||
|
virtual ObjectsContext* GetChildren() override { return this; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds an object to the context.
|
* @brief Adds an object to the context.
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ Assembly::Assembly()
|
|||||||
m_BBoxMin(Vector3f::Zero()),
|
m_BBoxMin(Vector3f::Zero()),
|
||||||
m_BBoxMax(Vector3f::Zero()),
|
m_BBoxMax(Vector3f::Zero()),
|
||||||
m_ShowBoundingBox(false),
|
m_ShowBoundingBox(false),
|
||||||
m_GroupSelection(true) {}
|
m_GroupSelection(true) {
|
||||||
|
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||||
|
}
|
||||||
|
|
||||||
Assembly::Assembly(const Assembly ©)
|
Assembly::Assembly(const Assembly ©)
|
||||||
: ObjectsContext(copy),
|
: ObjectsContext(copy),
|
||||||
@@ -35,13 +37,25 @@ Assembly::Assembly(const Assembly ©)
|
|||||||
m_ShowBoundingBox(copy.m_ShowBoundingBox),
|
m_ShowBoundingBox(copy.m_ShowBoundingBox),
|
||||||
m_GroupSelection(copy.m_GroupSelection) {}
|
m_GroupSelection(copy.m_GroupSelection) {}
|
||||||
|
|
||||||
Assembly::~Assembly() {}
|
Assembly::~Assembly() {
|
||||||
|
for (auto const& [obj, conn] : m_ChildConnections) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
m_ChildConnections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void Assembly::AddObject(Object *obj) {
|
void Assembly::AddObject(Object *obj) {
|
||||||
if (auto *at = dynamic_cast<AffineTransform *>(obj)) {
|
if (auto *at = dynamic_cast<AffineTransform *>(obj)) {
|
||||||
at->SetParent(this);
|
at->SetParent(this);
|
||||||
}
|
}
|
||||||
ObjectsContext::AddObject(obj);
|
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)
|
||||||
|
});
|
||||||
|
this->ComputeBoundingBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Assembly::RemoveObject(Object *obj) {
|
void Assembly::RemoveObject(Object *obj) {
|
||||||
@@ -49,7 +63,15 @@ void Assembly::RemoveObject(Object *obj) {
|
|||||||
if (at->GetParent() == this)
|
if (at->GetParent() == this)
|
||||||
at->SetParent(nullptr);
|
at->SetParent(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto itConn = m_ChildConnections.find(obj);
|
||||||
|
if (itConn != m_ChildConnections.end()) {
|
||||||
|
itConn->second.disconnect();
|
||||||
|
m_ChildConnections.erase(itConn);
|
||||||
|
}
|
||||||
|
|
||||||
ObjectsContext::RemoveObject(obj);
|
ObjectsContext::RemoveObject(obj);
|
||||||
|
this->ComputeBoundingBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Assembly::ComputeBoundingBox() {
|
void Assembly::ComputeBoundingBox() {
|
||||||
@@ -64,12 +86,11 @@ void Assembly::ComputeBoundingBox() {
|
|||||||
m_BBoxMin = Vector3f(inf, inf, inf);
|
m_BBoxMin = Vector3f(inf, inf, inf);
|
||||||
m_BBoxMax = Vector3f(-inf, -inf, -inf);
|
m_BBoxMax = Vector3f(-inf, -inf, -inf);
|
||||||
|
|
||||||
Matrix4f invAsm = this->GetWorldMatrix().inverse();
|
|
||||||
|
|
||||||
for (Object *obj : objects) {
|
for (Object *obj : objects) {
|
||||||
if (auto *box = dynamic_cast<ContainerBox *>(obj)) {
|
if (auto *box = dynamic_cast<ContainerBox *>(obj)) {
|
||||||
// ContainerBox: wm is matrix from unit cube [0,1] to assembly base
|
// ContainerBox: wm is matrix from unit cube [0,1] to local space
|
||||||
Matrix4f m = invAsm * box->GetWorldMatrix();
|
// Since it is parented to 'this', GetMatrix() is sufficient.
|
||||||
|
Matrix4f m = box->GetMatrix();
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
float x = (i & 1) ? 1.0f : 0.0f;
|
float x = (i & 1) ? 1.0f : 0.0f;
|
||||||
float y = (i & 2) ? 1.0f : 0.0f;
|
float y = (i & 2) ? 1.0f : 0.0f;
|
||||||
@@ -82,7 +103,7 @@ void Assembly::ComputeBoundingBox() {
|
|||||||
}
|
}
|
||||||
} else if (auto *cyl = dynamic_cast<Cylinder *>(obj)) {
|
} else if (auto *cyl = dynamic_cast<Cylinder *>(obj)) {
|
||||||
// Cylinder: centered [-1, 1] radial, [-0.5, 0.5] height
|
// Cylinder: centered [-1, 1] radial, [-0.5, 0.5] height
|
||||||
Matrix4f m = invAsm * cyl->GetWorldMatrix();
|
Matrix4f m = cyl->GetMatrix();
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
float x = (i & 1) ? 1.0f : -1.0f;
|
float x = (i & 1) ? 1.0f : -1.0f;
|
||||||
float y = (i & 2) ? 0.5f : -0.5f;
|
float y = (i & 2) ? 0.5f : -0.5f;
|
||||||
@@ -98,7 +119,7 @@ void Assembly::ComputeBoundingBox() {
|
|||||||
subAsm->ComputeBoundingBox();
|
subAsm->ComputeBoundingBox();
|
||||||
Vector3f subMin, subMax;
|
Vector3f subMin, subMax;
|
||||||
subAsm->GetBoundingBox(subMin, subMax);
|
subAsm->GetBoundingBox(subMin, subMax);
|
||||||
Matrix4f m = invAsm * subAsm->GetWorldMatrix();
|
Matrix4f m = subAsm->GetMatrix();
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
float x = (i & 1) ? subMax(0) : subMin(0);
|
float x = (i & 1) ? subMax(0) : subMin(0);
|
||||||
float y = (i & 2) ? subMax(1) : subMin(1);
|
float y = (i & 2) ? subMax(1) : subMin(1);
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ public:
|
|||||||
Assembly(const Assembly ©);
|
Assembly(const Assembly ©);
|
||||||
virtual ~Assembly();
|
virtual ~Assembly();
|
||||||
|
|
||||||
|
template <class ArchiveT>
|
||||||
|
void serialize(ArchiveT & ar, const unsigned int version) {
|
||||||
|
ar & boost::serialization::make_nvp("AffineTransform", boost::serialization::base_object<AffineTransform>(*this));
|
||||||
|
ar & boost::serialization::make_hrp("GroupSelection", m_GroupSelection);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void AddObject(Object* obj) override;
|
virtual void AddObject(Object* obj) override;
|
||||||
virtual void RemoveObject(Object* obj) override;
|
virtual void RemoveObject(Object* obj) override;
|
||||||
|
|
||||||
@@ -93,7 +99,7 @@ signals:
|
|||||||
if (m_InUpdated) return; // break signal recursion
|
if (m_InUpdated) return; // break signal recursion
|
||||||
m_InUpdated = true;
|
m_InUpdated = true;
|
||||||
this->ComputeBoundingBox();
|
this->ComputeBoundingBox();
|
||||||
ULIB_SIGNAL_EMIT(Assembly::Updated);
|
ULIB_SIGNAL_EMIT(Object::Updated);
|
||||||
m_InUpdated = false;
|
m_InUpdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +109,7 @@ private:
|
|||||||
bool m_ShowBoundingBox;
|
bool m_ShowBoundingBox;
|
||||||
bool m_GroupSelection;
|
bool m_GroupSelection;
|
||||||
bool m_InUpdated = false;
|
bool m_InUpdated = false;
|
||||||
|
std::map<Object*, Connection> m_ChildConnections;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace uLib
|
} // namespace uLib
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ signals:
|
|||||||
/** Signal emitted when properties change */
|
/** Signal emitted when properties change */
|
||||||
virtual void Updated() override {
|
virtual void Updated() override {
|
||||||
this->Sync();
|
this->Sync();
|
||||||
ULIB_SIGNAL_EMIT(ContainerBox::Updated);
|
ULIB_SIGNAL_EMIT(Object::Updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ signals:
|
|||||||
/** Signal emitted when properties change */
|
/** Signal emitted when properties change */
|
||||||
virtual void Updated() override {
|
virtual void Updated() override {
|
||||||
this->Sync();
|
this->Sync();
|
||||||
ULIB_SIGNAL_EMIT(Cylinder::Updated);
|
ULIB_SIGNAL_EMIT(Object::Updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
#include "Math/TriangleMesh.h"
|
#include "Math/TriangleMesh.h"
|
||||||
#include "Math/QuadMesh.h"
|
#include "Math/QuadMesh.h"
|
||||||
#include "Math/VoxImage.h"
|
#include "Math/VoxImage.h"
|
||||||
|
#include "Math/Assembly.h"
|
||||||
#include "Math/StructuredData.h"
|
#include "Math/StructuredData.h"
|
||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
|
|
||||||
ULIB_REGISTER_OBJECT(ContainerBox)
|
ULIB_REGISTER_OBJECT(ContainerBox)
|
||||||
ULIB_REGISTER_OBJECT(Cylinder)
|
ULIB_REGISTER_OBJECT(Cylinder)
|
||||||
|
ULIB_REGISTER_OBJECT(Assembly)
|
||||||
ULIB_REGISTER_OBJECT(CylindricalGeometry)
|
ULIB_REGISTER_OBJECT(CylindricalGeometry)
|
||||||
ULIB_REGISTER_OBJECT(SphericalGeometry)
|
ULIB_REGISTER_OBJECT(SphericalGeometry)
|
||||||
ULIB_REGISTER_OBJECT(TriangleMesh)
|
ULIB_REGISTER_OBJECT(TriangleMesh)
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ signals:
|
|||||||
/** Signal emitted when properties change */
|
/** Signal emitted when properties change */
|
||||||
virtual void Updated() override {
|
virtual void Updated() override {
|
||||||
this->Sync();
|
this->Sync();
|
||||||
ULIB_SIGNAL_EMIT(AffineTransform::Updated);
|
ULIB_SIGNAL_EMIT(Object::Updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ void Assembly::InstallPipe() {
|
|||||||
m_BBoxActor->GetProperty()->SetColor(1.0, 0.85, 0.0); // gold wireframe
|
m_BBoxActor->GetProperty()->SetColor(1.0, 0.85, 0.0); // gold wireframe
|
||||||
m_BBoxActor->GetProperty()->SetLineWidth(1.5);
|
m_BBoxActor->GetProperty()->SetLineWidth(1.5);
|
||||||
m_BBoxActor->GetProperty()->SetOpacity(0.6);
|
m_BBoxActor->GetProperty()->SetOpacity(0.6);
|
||||||
|
m_BBoxActor->PickableOff();
|
||||||
m_BBoxActor->SetVisibility(m_Content ? m_Content->GetShowBoundingBox() : false);
|
m_BBoxActor->SetVisibility(m_Content ? m_Content->GetShowBoundingBox() : false);
|
||||||
|
|
||||||
m_VtkAsm->AddPart(m_BBoxActor);
|
m_VtkAsm->AddPart(m_BBoxActor);
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public:
|
|||||||
virtual void SyncFromVtk() override;
|
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_Content; }
|
||||||
|
virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_Content; }
|
||||||
|
|
||||||
/** @brief Called when the model signals an update (model→VTK push). */
|
/** @brief Called when the model signals an update (model→VTK push). */
|
||||||
void contentUpdate();
|
void contentUpdate();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ set(TESTS
|
|||||||
vtkViewerTest
|
vtkViewerTest
|
||||||
vtkHandlerWidget
|
vtkHandlerWidget
|
||||||
PuppetPropertyTest
|
PuppetPropertyTest
|
||||||
|
PuppetParentingTest
|
||||||
# vtkVoxImageTest
|
# vtkVoxImageTest
|
||||||
# vtkTriangleMeshTest
|
# vtkTriangleMeshTest
|
||||||
)
|
)
|
||||||
|
|||||||
106
src/Vtk/testing/PuppetParentingTest.cpp
Normal file
106
src/Vtk/testing/PuppetParentingTest.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 <cstdlib>
|
||||||
|
#include <Core/ObjectsContext.h>
|
||||||
|
#include <Math/Assembly.h>
|
||||||
|
#include <Math/ContainerBox.h>
|
||||||
|
#include <Vtk/uLibVtkViewer.h>
|
||||||
|
#include <Vtk/vtkObjectsContext.h>
|
||||||
|
#include <Vtk/Math/vtkAssembly.h>
|
||||||
|
#include <Vtk/Math/vtkContainerBox.h>
|
||||||
|
#include <vtkAssembly.h>
|
||||||
|
#include <vtkProp3D.h>
|
||||||
|
#include <vtkRenderer.h>
|
||||||
|
#include <vtkMatrix4x4.h>
|
||||||
|
#include "testing-prototype.h"
|
||||||
|
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
BEGIN_TESTING(Puppet Parenting Test);
|
||||||
|
|
||||||
|
ObjectsContext globalContext;
|
||||||
|
Vtk::Viewer viewer;
|
||||||
|
|
||||||
|
// Create the display context, linked to the model context.
|
||||||
|
// It will automatically create visual puppets for each model object.
|
||||||
|
Vtk::vtkObjectsContext viewerContext(&globalContext);
|
||||||
|
viewerContext.ConnectRenderer(viewer.GetRenderer());
|
||||||
|
|
||||||
|
// 1. Create a model Assembly
|
||||||
|
auto* assembly = new Assembly();
|
||||||
|
assembly->SetInstanceName("ParentAssembly");
|
||||||
|
globalContext.AddObject(assembly);
|
||||||
|
|
||||||
|
// Verify assembly puppet exists in the viewer context
|
||||||
|
Vtk::Puppet* assemblyPuppet = viewerContext.GetPuppet(assembly);
|
||||||
|
ASSERT_NOT_NULL(assemblyPuppet);
|
||||||
|
|
||||||
|
// cast to Vtk::Assembly to access child context
|
||||||
|
auto* vtkAss = dynamic_cast<Vtk::Assembly*>(assemblyPuppet);
|
||||||
|
ASSERT_NOT_NULL(vtkAss);
|
||||||
|
|
||||||
|
// 2. Create a child Box and add it to the Assembly
|
||||||
|
auto* box1 = new ContainerBox(Vector3f(10, 10, 10));
|
||||||
|
box1->SetInstanceName("ChildBox1");
|
||||||
|
box1->SetPosition(Vector3f(20, 0, 0));
|
||||||
|
assembly->AddObject(box1);
|
||||||
|
|
||||||
|
// Verify child puppet was created in the assembly's child context
|
||||||
|
Vtk::vtkObjectsContext* childVtkCtx = vtkAss->GetChildrenContext();
|
||||||
|
ASSERT_NOT_NULL(childVtkCtx);
|
||||||
|
|
||||||
|
Vtk::Puppet* box1Puppet = childVtkCtx->GetPuppet(box1);
|
||||||
|
ASSERT_NOT_NULL(box1Puppet);
|
||||||
|
|
||||||
|
// 3. Move the parent and verify the child follows
|
||||||
|
assembly->SetPosition(Vector3f(100, 0, 0));
|
||||||
|
assembly->Update();
|
||||||
|
|
||||||
|
// In VTK assemblies, the child's absolute matrix should reflect the parent's transform
|
||||||
|
vtkProp3D* box1Prop = vtkProp3D::SafeDownCast(box1Puppet->GetProp());
|
||||||
|
ASSERT_NOT_NULL(box1Prop);
|
||||||
|
|
||||||
|
vtkMatrix4x4* boxMatrix = box1Prop->GetMatrix();
|
||||||
|
// Origin (0,0,0) + local(20,0,0) + assembly(100,0,0) = world(120,0,0) ?
|
||||||
|
// Actually, box1->GetPosition() is (20,0,0).
|
||||||
|
// The puppet ApplyTransform sets the prop orientation and position.
|
||||||
|
|
||||||
|
std::cout << "Checking transformation chain..." << std::endl;
|
||||||
|
// std::cout << *boxMatrix << std::endl;
|
||||||
|
|
||||||
|
// Verify relative positioning
|
||||||
|
double* pos = box1Prop->GetPosition();
|
||||||
|
ASSERT_EQUAL(pos[0], 20.0);
|
||||||
|
|
||||||
|
// The absolute world position can be checked via GetMatrix elements
|
||||||
|
// boxMatrix->GetElement(0, 3) should be 120.0 if the vtkAssembly nesting is working
|
||||||
|
// but vtkAssembly::GetMatrix() usually returns the LOCAL matrix unless called on the top property context?
|
||||||
|
// Actually vtkProp3D::GetMatrix() is the local matrix.
|
||||||
|
|
||||||
|
// 4. Add another child
|
||||||
|
auto* box2 = new ContainerBox(Vector3f(5, 5, 5));
|
||||||
|
box2->SetInstanceName("ChildBox2");
|
||||||
|
box2->SetPosition(Vector3f(-20, 0, 0));
|
||||||
|
assembly->AddObject(box2);
|
||||||
|
|
||||||
|
Vtk::Puppet* box2Puppet = childVtkCtx->GetPuppet(box2);
|
||||||
|
ASSERT_NOT_NULL(box2Puppet);
|
||||||
|
|
||||||
|
// Render if not in batch environment
|
||||||
|
if (!std::getenv("CTEST_PROJECT_NAME")) {
|
||||||
|
viewer.GetRenderer()->ResetCamera();
|
||||||
|
viewer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
END_TESTING;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user