From deb391f78cbb57c4756564e785be24e008ac5275 Mon Sep 17 00:00:00 2001 From: Yuzheng Zhou Date: Fri, 16 Aug 2013 10:46:25 -0400 Subject: [PATCH] BUG: 3241 Fix transform not applied to volume when scene is read from a file When a scene is loaded, vtkMRMLScene::UpdateNodeReferences doesn't handle event registration. Except for vtkCommand::ModifiedEvent, any other events need to be registered via OnNodeReferenceAdded API. Fixes #3241 --- Libs/MRML/Core/Testing/CMakeLists.txt | 2 + ...ransformableNodeOnNodeReferenceAddTest.cxx | 127 ++++++++++++++++++ Libs/MRML/Core/vtkMRMLTransformableNode.cxx | 17 +++ Libs/MRML/Core/vtkMRMLTransformableNode.h | 3 + 4 files changed, 149 insertions(+) create mode 100644 Libs/MRML/Core/Testing/vtkMRMLTransformableNodeOnNodeReferenceAddTest.cxx diff --git a/Libs/MRML/Core/Testing/CMakeLists.txt b/Libs/MRML/Core/Testing/CMakeLists.txt index a2ee92ccc26..451f27e6573 100644 --- a/Libs/MRML/Core/Testing/CMakeLists.txt +++ b/Libs/MRML/Core/Testing/CMakeLists.txt @@ -75,6 +75,7 @@ create_test_sourcelist(Tests ${KIT}CxxTests.cxx vtkMRMLStorageNodeTest1.cxx vtkMRMLTensorVolumeNodeTest1.cxx vtkMRMLTransformableNodeReferenceSaveImportTest.cxx + vtkMRMLTransformableNodeOnNodeReferenceAddTest.cxx vtkMRMLTransformNodeTest1.cxx vtkMRMLTransformStorageNodeTest1.cxx vtkMRMLTransformableNodeTest1.cxx @@ -171,6 +172,7 @@ simple_test( vtkMRMLStorableNodeTest1 ) simple_test( vtkMRMLStorageNodeTest1 ) simple_test( vtkMRMLTensorVolumeNodeTest1 ) simple_test( vtkMRMLTransformableNodeReferenceSaveImportTest ) +simple_test( vtkMRMLTransformableNodeOnNodeReferenceAddTest ) simple_test( vtkMRMLTransformableNodeTest1 ) simple_test( vtkMRMLTransformNodeTest1 ) simple_test( vtkMRMLTransformStorageNodeTest1 ) diff --git a/Libs/MRML/Core/Testing/vtkMRMLTransformableNodeOnNodeReferenceAddTest.cxx b/Libs/MRML/Core/Testing/vtkMRMLTransformableNodeOnNodeReferenceAddTest.cxx new file mode 100644 index 00000000000..a978809b7a3 --- /dev/null +++ b/Libs/MRML/Core/Testing/vtkMRMLTransformableNodeOnNodeReferenceAddTest.cxx @@ -0,0 +1,127 @@ +/*=auto========================================================================= + + Program: 3D Slicer + + Copyright (c) Kitware Inc. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Yuzheng Zhou, Kitware Inc. + and was partially funded by NIH grant 3P41RR013218-12S1 + +=========================================================================auto=*/ + +// MRML includes +#include "vtkMRMLCoreTestingMacros.h" +#include "vtkMRMLLinearTransformNode.h" +#include "vtkMRMLTransformableNode.h" +#include "vtkMRMLScalarVolumeNode.h" +#include "vtkMRMLScene.h" + +// VTK includes +#include +#include +#include + +namespace +{ + +void populateScene(vtkMRMLScene* scene); + +} // end of anonymous namespace + +//--------------------------------------------------------------------------- +int vtkMRMLTransformableNodeOnNodeReferenceAddTest(int , char * [] ) +{ + // Save a scene containing a viewnode and a sceneview node. + vtkNew scene; + populateScene(scene.GetPointer()); + + scene->SetSaveToXMLString(1); + scene->Commit(); + std::string xmlScene = scene->GetSceneXMLString(); + std::cout << xmlScene << std::endl; + + // Load the saved scene + vtkNew scene2; + + scene2->SetLoadFromXMLString(1); + scene2->SetSceneXMLString(xmlScene); + scene2->Import(); + + // Check transform node IDs + vtkMRMLNode* transformNode = + scene2->GetNthNodeByClass(0, "vtkMRMLLinearTransformNode"); + if (!transformNode || strcmp(transformNode->GetID(), "vtkMRMLLinearTransformNode1") != 0) + { + std::cerr << __LINE__ << ": import failed." << std::endl + << " transform node ID is " << transformNode->GetID() + << " instead of vtkMRMLLinearTransformNode1." << std::endl; + + return EXIT_FAILURE; + } + + // Check references + vtkMRMLTransformableNode* trnsformableNode = + vtkMRMLTransformableNode::SafeDownCast(scene2->GetNthNodeByClass(0, "vtkMRMLTransformableNode")); + + if (strcmp(trnsformableNode->GetTransformNodeID(), + transformNode->GetID()) != 0) + { + std::cerr << __LINE__ << ": import failed." << std::endl + << " Transformable node references are not updated. " + << "Transform node ID reference is " + << trnsformableNode->GetTransformNodeID() + << " instead of " << transformNode->GetID() << std::endl; + return EXIT_FAILURE; + } + + // Test vtkMRMLTransformableNode::OnNodeReferenceAdded() + vtkMRMLLinearTransformNode* linearTransformNode = vtkMRMLLinearTransformNode::SafeDownCast(scene2->GetNthNodeByClass(0, "vtkMRMLLinearTransformNode")); + vtkMRMLScalarVolumeNode* volumeNode = vtkMRMLScalarVolumeNode::SafeDownCast(scene2->GetNthNodeByClass(0, "vtkMRMLScalarVolumeNode")); + vtkNew callback; + volumeNode->AddObserver(vtkCommand::AnyEvent, callback.GetPointer()); + + linearTransformNode->GetMatrixTransformToParent()->Modified(); + if (!callback->GetErrorString().empty() || + callback->GetNumberOfModified() != 0 || + callback->GetNumberOfEvents(vtkMRMLTransformNode::TransformModifiedEvent) != 1) + { + std::cerr << "vtkMRMLTransformableNode::OnNodeReferenceAdded failed." + << callback->GetErrorString().c_str() << " " + << "Number of ModifiedEvent: " << callback->GetNumberOfModified() << " " + << "Number of TransformModifiedEvent: " + << callback->GetNumberOfEvents(vtkMRMLTransformNode::TransformModifiedEvent) + << std::endl; + return EXIT_FAILURE; + } + callback->ResetNumberOfEvents(); + return EXIT_SUCCESS; +} + +namespace +{ + +//--------------------------------------------------------------------------- +void populateScene(vtkMRMLScene* scene) +{ + + vtkNew transformableableNode; + scene->AddNode(transformableableNode.GetPointer()); + + vtkNew transformNode; + scene->AddNode(transformNode.GetPointer()); + + transformableableNode->SetAndObserveTransformNodeID(transformNode->GetID()); + +} + + +} diff --git a/Libs/MRML/Core/vtkMRMLTransformableNode.cxx b/Libs/MRML/Core/vtkMRMLTransformableNode.cxx index 6d76353cde6..f7635ba0484 100644 --- a/Libs/MRML/Core/vtkMRMLTransformableNode.cxx +++ b/Libs/MRML/Core/vtkMRMLTransformableNode.cxx @@ -13,6 +13,7 @@ Version: $Revision: 1.14 $ =========================================================================auto=*/ // MRML includes +#include "vtkEventBroker.h" #include "vtkMRMLLinearTransformNode.h" #include "vtkMRMLScene.h" @@ -137,6 +138,22 @@ void vtkMRMLTransformableNode::SetAndObserveTransformNodeID(const char *transfor this->InvokeEvent(vtkMRMLTransformableNode::TransformModifiedEvent, NULL); } +//---------------------------------------------------------------------------- +void vtkMRMLTransformableNode::OnNodeReferenceAdded(vtkMRMLNodeReference *reference) +{ + Superclass::OnNodeReferenceAdded(reference); + + // When a scene is loaded, vtkMRMLScene::UpdateNodeReferences doesn't handle + // event registration. Except for vtkCommand::ModifiedEvent, any other events + // need to be registered via OnNodeReferenceAdded API. + if (std::string(reference->GetReferenceRole()) == this->GetTransformNodeReferenceRole() + && reference->ReferencedNode && reference->ReferencingNode) + { + vtkEventBroker::GetInstance()->AddObservation(reference->ReferencedNode, + vtkMRMLTransformableNode::TransformModifiedEvent, reference->ReferencingNode, this->MRMLCallbackCommand); + } +} + //--------------------------------------------------------------------------- void vtkMRMLTransformableNode::ProcessMRMLEvents ( vtkObject *caller, unsigned long event, diff --git a/Libs/MRML/Core/vtkMRMLTransformableNode.h b/Libs/MRML/Core/vtkMRMLTransformableNode.h index f266fbdc31c..c1442a30e5e 100644 --- a/Libs/MRML/Core/vtkMRMLTransformableNode.h +++ b/Libs/MRML/Core/vtkMRMLTransformableNode.h @@ -105,6 +105,9 @@ class VTK_MRML_EXPORT vtkMRMLTransformableNode : public vtkMRMLNode virtual const char* GetTransformNodeReferenceRole(); virtual const char* GetTransformNodeReferenceMRMLAttributeName(); + /// Called when a node reference ID is added (list size increased). + virtual void OnNodeReferenceAdded(vtkMRMLNodeReference *reference); + private: char* TransformNodeIDInternal; vtkSetStringMacro(TransformNodeIDInternal);