Program Listing for File vtkMRMLLayerDMNodeReferenceObserver.cxx
↰ Return to documentation for file (MRML/vtkMRMLLayerDMNodeReferenceObserver.cxx)
#include "vtkMRMLLayerDMNodeReferenceObserver.h"
// LayerDM includes
#include "vtkMRMLLayerDMObjectEventObserver.h"
// Slicer includes
#include <vtkCollection.h>
#include <vtkMRMLNode.h>
#include <vtkMRMLScene.h>
namespace
{
class vtkMRMLNodeReferenceFacade : public vtkMRMLNode
{
public:
static vtkMRMLNodeReferenceFacade* New();
vtkTypeMacro(vtkMRMLNodeReferenceFacade, vtkMRMLNode);
const char* GetNodeTagName() override { return "NodeRefFacade"; }
vtkMRMLNode* CreateNodeInstance() override;
static std::tuple<vtkMRMLNode*, std::string> GetToNodeAndRoleFromTypeErasedNodeRef(void* callData)
{
const auto ref = CastCallData(callData);
return { ref->GetReferencedNode(), ref->GetReferenceRole() };
}
protected:
vtkMRMLNodeReferenceFacade() = default;
~vtkMRMLNodeReferenceFacade() override = default;
private:
vtkMRMLNodeReferenceFacade(const vtkMRMLNodeReferenceFacade&);
void operator=(const vtkMRMLNodeReferenceFacade&);
static vtkMRMLNodeReference* CastCallData(void* callData) { return static_cast<vtkMRMLNodeReference*>(callData); }
};
vtkMRMLNodeNewMacro(vtkMRMLNodeReferenceFacade);
} // namespace
vtkStandardNewMacro(vtkMRMLLayerDMNodeReferenceObserver);
void vtkMRMLLayerDMNodeReferenceObserver::SetReferenceModifiedCallBack(const CallBackT& modifiedCallback)
{
m_onRefModified = modifiedCallback;
}
void vtkMRMLLayerDMNodeReferenceObserver::SetScene(vtkMRMLScene* scene)
{
if (m_scene == scene)
{
return;
}
this->m_obs->UpdateObserver(m_scene, scene, { vtkMRMLScene::NodeAddedEvent, vtkMRMLScene::NodeRemovedEvent });
this->m_scene = scene;
this->UpdateFromScene();
}
vtkMRMLLayerDMNodeReferenceObserver::vtkMRMLLayerDMNodeReferenceObserver()
: m_obs(vtkSmartPointer<vtkMRMLLayerDMObjectEventObserver>::New())
{
m_obs->SetUpdateCallback(
[this](vtkObject* obj, unsigned long eventId, void* callData)
{
if (obj == this->m_scene)
{
switch (eventId)
{
case vtkMRMLScene::NodeAddedEvent: this->OnNodeAdded(static_cast<vtkMRMLNode*>(callData)); break;
case vtkMRMLScene::NodeRemovedEvent: this->OnNodeRemoved(static_cast<vtkMRMLNode*>(callData)); break;
default: break;
}
}
else
{
auto fromNode = vtkMRMLNode::SafeDownCast(obj);
if (!fromNode)
{
return;
}
const auto [toNode, role] = vtkMRMLNodeReferenceFacade::GetToNodeAndRoleFromTypeErasedNodeRef(callData);
switch (eventId)
{
case vtkMRMLNode::ReferenceAddedEvent: this->OnReferenceAdded(fromNode, toNode, role); break;
case vtkMRMLNode::ReferenceRemovedEvent: this->OnReferenceRemoved(fromNode, toNode, role); break;
case vtkMRMLNode::ReferenceModifiedEvent: this->OnReferenceModified(fromNode, toNode, role); break;
default: break;
}
}
});
}
inline std::set<vtkSmartPointer<vtkMRMLNode>> GetSceneNodes(vtkMRMLScene* scene)
{
if (!scene)
{
return {};
}
std::set<vtkSmartPointer<vtkMRMLNode>> nodes;
for (int iNode = 0; iNode < scene->GetNumberOfNodes(); iNode++)
{
auto node = vtkMRMLNode::SafeDownCast(scene->GetNodes()->GetItemAsObject(iNode));
if (node)
{
nodes.insert(node);
}
}
return nodes;
}
inline std::tuple<std::vector<vtkSmartPointer<vtkMRMLNode>>, std::vector<vtkSmartPointer<vtkMRMLNode>>> GetNodesRemovedAddedFromScene(
vtkMRMLScene* scene,
const std::set<vtkSmartPointer<vtkMRMLNode>>& currentNodes)
{
auto sceneNodes = GetSceneNodes(scene);
std::vector<vtkSmartPointer<vtkMRMLNode>> nodesRemoved, nodesAdded;
for (const auto& sceneNode : sceneNodes)
{
if (currentNodes.find(sceneNode) == currentNodes.end())
{
nodesAdded.emplace_back(sceneNode);
}
}
for (const auto& currentNode : currentNodes)
{
if (sceneNodes.find(currentNode) == sceneNodes.end())
{
nodesRemoved.emplace_back(currentNode);
}
}
return { nodesRemoved, nodesAdded };
}
void vtkMRMLLayerDMNodeReferenceObserver::UpdateFromScene()
{
auto [nodesRemoved, nodesAdded] = GetNodesRemovedAddedFromScene(this->m_scene, this->m_nodes);
for (const auto& node : nodesRemoved)
{
this->OnNodeRemoved(node);
}
for (const auto& node : nodesAdded)
{
this->OnNodeAdded(node);
}
}
void vtkMRMLLayerDMNodeReferenceObserver::OnNodeRemoved(vtkMRMLNode* node)
{
auto eraseKeyInMap = [&](std::map<vtkSmartPointer<vtkMRMLNode>, std::set<RefT>>& map, vtkMRMLNode* keyNode)
{
if (map.find(keyNode) == map.end())
{
return;
}
map.erase(keyNode);
};
// Notify all nodes that references was removed
for (const auto& [toNode, role] : this->GetNodeToReferences(node))
{
this->OnReferenceRemoved(node, toNode, role);
}
// Remove any observer on the node
m_obs->RemoveObserver(node);
// Erase the node from the different maps to avoid any dangling pointers
m_nodes.erase(node);
eraseKeyInMap(m_refFrom, node);
eraseKeyInMap(m_refTo, node);
}
void vtkMRMLLayerDMNodeReferenceObserver::OnNodeAdded(vtkMRMLNode* node)
{
m_nodes.insert(node);
m_obs->UpdateObserver(nullptr, node, { vtkMRMLNode::ReferenceAddedEvent, vtkMRMLNode::ReferenceModifiedEvent, vtkMRMLNode::ReferenceRemovedEvent });
for (const auto& [toNode, role] : GetNodeReferencesFromScene(node))
{
this->OnReferenceAdded(node, toNode, role);
}
}
void vtkMRMLLayerDMNodeReferenceObserver::OnReferenceAdded(vtkMRMLNode* fromNode, vtkMRMLNode* toNode, const std::string& role)
{
m_refTo[fromNode].insert({ toNode, role });
m_refFrom[toNode].insert({ fromNode, role });
this->TriggerReferenceAdded(fromNode, toNode, role);
}
void vtkMRMLLayerDMNodeReferenceObserver::OnReferenceRemoved(vtkMRMLNode* fromNode, vtkMRMLNode* toNode, const std::string& role)
{
auto eraseRefInMap = [&](std::map<vtkSmartPointer<vtkMRMLNode>, std::set<RefT>>& map, vtkMRMLNode* keyNode, vtkMRMLNode* valueNode)
{
if (map.find(keyNode) == map.end())
{
return;
}
map[keyNode].erase({ valueNode, role });
if (map[keyNode].empty())
{
map.erase(keyNode);
}
};
eraseRefInMap(m_refTo, fromNode, toNode);
eraseRefInMap(m_refFrom, toNode, fromNode);
this->TriggerReferenceRemoved(fromNode, toNode, role);
}
void vtkMRMLLayerDMNodeReferenceObserver::RemoveOutdatedReferences(vtkMRMLNode* fromNode)
{
auto sceneRefs = GetNodeReferencesFromScene(fromNode);
for (const auto& ref : GetNodeToReferences(fromNode))
{
if (sceneRefs.find(ref) == sceneRefs.end())
{
this->OnReferenceRemoved(fromNode, std::get<0>(ref), std::get<1>(ref));
}
}
}
void vtkMRMLLayerDMNodeReferenceObserver::OnReferenceModified(vtkMRMLNode* fromNode, vtkMRMLNode* toNode, const std::string& role)
{
this->RemoveOutdatedReferences(fromNode);
this->OnReferenceAdded(fromNode, toNode, role);
}
std::set<vtkMRMLLayerDMNodeReferenceObserver::RefT> vtkMRMLLayerDMNodeReferenceObserver::GetNodeToReferences(vtkMRMLNode* node) const
{
if (const auto it = m_refTo.find(node); it != m_refTo.end())
{
return it->second;
}
return {};
}
std::set<vtkMRMLLayerDMNodeReferenceObserver::RefT> vtkMRMLLayerDMNodeReferenceObserver::GetNodeFromReferences(vtkMRMLNode* node) const
{
if (const auto it = m_refFrom.find(node); it != m_refFrom.end())
{
return it->second;
}
return {};
}
int vtkMRMLLayerDMNodeReferenceObserver::GetReferenceToSize() const
{
return static_cast<int>(m_refTo.size());
}
int vtkMRMLLayerDMNodeReferenceObserver::GetReferenceFromSize() const
{
return static_cast<int>(m_refFrom.size());
}
int vtkMRMLLayerDMNodeReferenceObserver::GetNumberOfNodes() const
{
return static_cast<int>(m_nodes.size());
}
std::set<vtkMRMLLayerDMNodeReferenceObserver::RefT> vtkMRMLLayerDMNodeReferenceObserver::GetNodeReferencesFromScene(vtkMRMLNode* node)
{
if (!node)
{
return {};
}
std::set<RefT> references;
std::vector<std::string> roles;
node->GetNodeReferenceRoles(roles);
for (const auto& role : roles)
{
for (int iNode = 0; iNode < node->GetNumberOfNodeReferences(role.c_str()); iNode++)
{
auto toNode = node->GetNthNodeReference(role.c_str(), iNode);
references.insert({ toNode, role });
}
}
return references;
}
void vtkMRMLLayerDMNodeReferenceObserver::TriggerReferenceAdded(vtkMRMLNode* fromNode, vtkMRMLNode* toNode, const std::string& role) const
{
TriggerCallback(m_onRefModified, fromNode, toNode, role, ReferenceAddedEvent);
}
void vtkMRMLLayerDMNodeReferenceObserver::TriggerReferenceRemoved(vtkMRMLNode* fromNode, vtkMRMLNode* toNode, const std::string& role) const
{
TriggerCallback(m_onRefModified, fromNode, toNode, role, ReferenceRemovedEvent);
}
void vtkMRMLLayerDMNodeReferenceObserver::TriggerCallback(const CallBackT& callback, vtkMRMLNode* fromNode, vtkMRMLNode* toNode, const std::string& role, int eventType)
{
if (!callback || !fromNode || !toNode)
{
return;
}
callback(fromNode, toNode, role, eventType);
}