diff --git a/Modules/Simulation/Testing/files.cmake b/Modules/Simulation/Testing/files.cmake index 3f5716e326..68a6fdc6b2 100644 --- a/Modules/Simulation/Testing/files.cmake +++ b/Modules/Simulation/Testing/files.cmake @@ -1,6 +1,7 @@ set(MODULE_TESTS + mitkSimulationTemplateTest.cpp ) set(MODULE_CUSTOM_TESTS mitkSimulationTest.cpp ) diff --git a/Modules/Simulation/Testing/mitkSimulationTemplateTest.cpp b/Modules/Simulation/Testing/mitkSimulationTemplateTest.cpp index 929b37aa61..608ad1b712 100644 --- a/Modules/Simulation/Testing/mitkSimulationTemplateTest.cpp +++ b/Modules/Simulation/Testing/mitkSimulationTemplateTest.cpp @@ -1,87 +1,315 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include #include #include #include -int mitkSimulationTemplateTest(int argc, char* argv[]) +#define _MITK_TEST_FOR_EXCEPTION(STATEMENT, EXCEPTION, MESSAGE) \ + MITK_TEST_OUTPUT_NO_ENDL(<< MESSAGE) \ + try \ + { \ + STATEMENT; \ + MITK_TEST_OUTPUT(<< " [FAILED]") \ + mitk::TestManager::GetInstance()->TestFailed(); \ + } \ + catch (const EXCEPTION& e) \ + { \ + MITK_TEST_OUTPUT(<< "\n " << e.GetDescription() << " [PASSED]") \ + mitk::TestManager::GetInstance()->TestPassed(); \ + } + +static mitk::DataNode::Pointer CreateDataNode(mitk::SimulationTemplate::Pointer simulationTemplate, bool setProperties = false) { - MITK_TEST_BEGIN("mitkSimulationTemplateTest") + if (simulationTemplate.IsNull()) + mitkThrow() << "Invalid argument (null pointer!)"; + + mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); + dataNode->SetData(simulationTemplate); - MITK_TEST_CONDITION_REQUIRED(argc == 2, "Test if command line has argument.") + if (setProperties) + simulationTemplate->SetProperties(dataNode); - MITK_TEST_OUTPUT(<< "Register SimulationObjectFactory.") - mitk::RegisterSimulationObjectFactory(); + return dataNode; +} +static void Parse_InputIsEmpty_ReturnsTrue() +{ mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); - MITK_TEST_CONDITION_REQUIRED(simulationTemplate.IsNotNull(), "Create simulation template.") + MITK_TEST_CONDITION(simulationTemplate->Parse(""), "Parse_InputIsEmpty_ReturnsTrue") +} - std::string contents = simulationTemplate->CreateSimulation(); - MITK_TEST_CONDITION(contents.empty(), "Try to create simulation from template.") +static void Parse_AlreadyInitialized_ReturnsFalse() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse(""); + MITK_TEST_CONDITION(!simulationTemplate->Parse(""), "Parse_AlreadyInitialized_ReturnsFalse") +} - bool boolResult = simulationTemplate->SetProperties(NULL); - MITK_TEST_CONDITION(!boolResult, "Try to set properties of non-existent data node.") +static void Parse_EOFBeforeClosingBrace_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id='A' default='1'"), mitk::Exception, "Parse_EOFBeforeClosingBrace_ThrowsException") +} - boolResult = simulationTemplate->SetProperties(mitk::DataNode::New()); - MITK_TEST_CONDITION(!boolResult, "Try to set properties of empty data node.") +static void Parse_OpeningBraceBeforeClosingBrace_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id='A' default='1' {id='B' default='2'}}"), mitk::Exception, "Parse_OpeningBraceBeforeClosingBrace_ThrowsException") +} + +static void Parse_TooShortToBeReference_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{}"), mitk::Exception, "Parse_TooShortToBeReference_ThrowsException") +} - boolResult = simulationTemplate->Parse(""); - MITK_TEST_CONDITION(boolResult, "Parse empty simulation template."); +static void Parse_NumberOfSingleQuotationMarksIsOdd_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{'Flinders's cat'}"), mitk::Exception, "Parse_NumberOfSingleQuotationMarksIsOdd_ThrowsException") +} + +static void Parse_ReferenceDoesNotEndCorrectly_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{'Flinders\\'s cat}"), mitk::Exception, "Parse_ReferenceDoesNotEndCorrectly_ThrowsException") +} + +static void Parse_ReferenceIsEmpty_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{''}"), mitk::Exception, "Parse_ReferenceIsEmpty_ThrowsException") +} - boolResult = simulationTemplate->Parse(""); - MITK_TEST_CONDITION(!boolResult, "Try to parse already initialized simulation template.") +static void Parse_TooShortToBeTemplate_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id=}"), mitk::Exception, "Parse_TooShortToBeTemplate_ThrowsException") +} - boolResult = simulationTemplate->SetProperties(mitk::DataNode::New()); - MITK_TEST_CONDITION(!boolResult, "Try to set properties of empty data node again.") +static void Parse_IdIsEmpty_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id=''}"), mitk::Exception, "Parse_IdIsEmpty_ThrowsException") +} - contents = simulationTemplate->CreateSimulation(); - MITK_TEST_CONDITION(contents.empty(), "Create empty simulation.") +static void Parse_UnknownType_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id='Trim' type='Cat'}"), mitk::Exception, "Parse_UnknownType_ThrowsException") +} - simulationTemplate = mitk::SimulationTemplate::New(); - MITK_TEST_CONDITION_REQUIRED(simulationTemplate.IsNotNull(), "Create another simulation template.") +static void Parse_ValueIsAmbiguous_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id='Matthew' id='Flinders'}"), mitk::Exception, "Parse_ValueIsAmbiguous_ThrowsException") +} - contents = ""; - boolResult = simulationTemplate->Parse(contents); - MITK_TEST_CONDITION(boolResult, "Parse static simulation template.") +static void Parse_IdNotFound_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{type='int' value='42'}"), mitk::Exception, "Parse_IdNotFound_ThrowsException") +} - std::string contents2 = simulationTemplate->CreateSimulation(); - MITK_TEST_CONDITION(contents == contents2, "Create simulation.") +static void Parse_NoAssignmentToValue_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id default=''}"), mitk::Exception, "Parse_NoAssignmentToValue_ThrowsException") +} - simulationTemplate = mitk::SimulationTemplate::New(); - MITK_TEST_CONDITION_REQUIRED(simulationTemplate.IsNotNull(), "Create another simulation template.") +static void Parse_ValueIsNotEnclosedWithSingleQuotationMarks_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id=Flinders\\'s Cat}"), mitk::Exception, "Parse_ValueIsNotEnclosedWithSingleQuotationMarks_ThrowsException") +} - contents = "{'F'}|{id='A'}|{id = 'B' type='string'}|{id ='C' type=\t\t\n\t'int'}|{id= 'D' type='float'}|{id='E' default='E'}|{id='F' type='int', default='1'}|{id='G' type='float' default='0.5'}|{'E'}"; - boolResult = simulationTemplate->Parse(contents); - MITK_TEST_CONDITION(boolResult, "Parse simulation template.") +static void Parse_TypeMismatch_ThrowsException() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + _MITK_TEST_FOR_EXCEPTION(simulationTemplate->Parse("{id='Answer' type='int' default='forty-two'}"), mitk::Exception, "Parse_TypeMismatch_ThrowsException") +} - mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); - dataNode->SetData(simulationTemplate); - boolResult = simulationTemplate->SetProperties(dataNode); - MITK_TEST_CONDITION(boolResult, "Set properties of corresponding data node.") +static void Parse_ValueContainsEscapedSingleQuotationMark_IsUnescapedAfterParsing() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Flinders\\'s Cat' default='Trim'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + std::string value; + dataNode->GetStringProperty("Flinders's Cat", value); + MITK_TEST_CONDITION(value == "Trim", "Parse_ValueContainsEscapedSingleQuotationMark_IsUnescapedAfterParsing") +} + +static void Parse_Float_ParsedAsFloatProperty() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Pi' type='float' default='3.14'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + float value; + MITK_TEST_CONDITION(dataNode->GetFloatProperty("Pi", value) && mitk::Equal(value, 3.14f), "Parse_Float_ParsedAsFloatProperty") +} + +static void Parse_Int_ParsedAsIntProperty() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Answer' type='int' default='42'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + int value; + MITK_TEST_CONDITION(dataNode->GetIntProperty("Answer", value) && value == 42, "Parse_Int_ParsedAsIntProperty") +} + +static void Parse_String_ParsedAsStringProperty() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Cat' type='string' default='Trim'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + std::string value; + MITK_TEST_CONDITION(dataNode->GetStringProperty("Cat", value) && value == "Trim", "Parse_String_ParsedAsStringProperty") +} + +static void Parse_FloatDefaultValue_IsZero() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Zero' type='float'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + float value; + MITK_TEST_CONDITION(dataNode->GetFloatProperty("Zero", value) && mitk::Equal(value, 0.0f), "Parse_FloatDefaultValue_IsZero") +} + +static void Parse_IntDefaultValue_IsZero() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Zero' type='int'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + int value; + MITK_TEST_CONDITION(dataNode->GetIntProperty("Zero", value) && value == 0, "Parse_IntDefaultValue_IsZero") +} + +static void Parse_StringDefaultValue_IsEmpty() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Empty' type='string'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + std::string value; + MITK_TEST_CONDITION(dataNode->GetStringProperty("Empty", value) && value.empty(), "Parse_StringDefaultValue_IsEmpty") +} + +static void Parse_NoType_ParsedAsString() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Captain' default='Flinders'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate, true); + std::string value; + MITK_TEST_CONDITION(dataNode->GetStringProperty("Captain", value) && value == "Flinders", "Parse_NoType_ParsedAsString") +} + +static void SetProperties_InputIsNull_ReturnsFalse() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + MITK_TEST_CONDITION(!simulationTemplate->SetProperties(NULL), "SetProperties_InputIsNull_ReturnsFalse") +} + +static void SetProperties_NotInitialized_ReturnsFalse() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate); + MITK_TEST_CONDITION(!simulationTemplate->SetProperties(dataNode), "SetProperties_NotInitialized_ReturnsFalse") +} + +static void SetProperties_InputIsNotOwner_ReturnsFalse() +{ + mitk::SimulationTemplate::Pointer simulationTemplate1 = mitk::SimulationTemplate::New(); + simulationTemplate1->Parse("{id='A' default='1'}"); + mitk::SimulationTemplate::Pointer simulationTemplate2 = mitk::SimulationTemplate::New(); + simulationTemplate2->Parse("{id='B' default='2'}"); + mitk::DataNode::Pointer dataNode1 = CreateDataNode(simulationTemplate1); + MITK_TEST_CONDITION(!simulationTemplate2->SetProperties(dataNode1), "SetProperties_InputIsNotOwner_ReturnsFalse") +} + +static void SetProperties_ContainsTemplateAndReference_SetsPropertyAndReturnsTrue() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Atoll' default='Bora'} {'Atoll'}"); + mitk::DataNode::Pointer dataNode = CreateDataNode(simulationTemplate); + bool boolResult = simulationTemplate->SetProperties(dataNode); + std::size_t numProperties = dataNode->GetPropertyList()->GetMap()->size(); + std::string stringResult; + dataNode->GetStringProperty("Atoll", stringResult); + MITK_TEST_CONDITION(boolResult && numProperties == 1 && stringResult == "Bora", "SetProperties_ContainsTemplateAndReference_SetsPropertyAndReturnsTrue") +} + +static void CreateSimulation_NotInitialized_ReturnsEmptyString() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + MITK_TEST_CONDITION(simulationTemplate->CreateSimulation().empty(), "CreateSimulation_NotInitialized_ReturnsEmptyString") +} + +static void CreateSimulation_ContainsInvalidReference_ReturnsEmptyString() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{'Invalid'}"); + MITK_TEST_CONDITION(simulationTemplate->CreateSimulation().empty(), "CreateSimulation_ContainsInvalidReference_ReturnsEmptyString") +} + +static void CreateSimulation_ContainsTemplateAndReference_ReturnsExpectedString() +{ + mitk::SimulationTemplate::Pointer simulationTemplate = mitk::SimulationTemplate::New(); + simulationTemplate->Parse("{id='Atoll' default='Bora'} {'Atoll'}"); + MITK_TEST_CONDITION(simulationTemplate->CreateSimulation() == "Bora Bora", "CreateSimulation_ContainsTemplateAndReference_ReturnsExpectedString") +} + +int mitkSimulationTemplateTest(int, char* []) +{ + mitk::RegisterSimulationObjectFactory(); + + MITK_TEST_BEGIN("mitkSimulationTemplateTest") + + Parse_InputIsEmpty_ReturnsTrue(); + Parse_AlreadyInitialized_ReturnsFalse(); + Parse_EOFBeforeClosingBrace_ThrowsException(); + Parse_OpeningBraceBeforeClosingBrace_ThrowsException(); + Parse_TooShortToBeReference_ThrowsException(); + Parse_NumberOfSingleQuotationMarksIsOdd_ThrowsException(); + Parse_ReferenceDoesNotEndCorrectly_ThrowsException(); + Parse_ReferenceIsEmpty_ThrowsException(); + Parse_TooShortToBeTemplate_ThrowsException(); + Parse_IdIsEmpty_ThrowsException(); + Parse_UnknownType_ThrowsException(); + Parse_ValueIsAmbiguous_ThrowsException(); + Parse_IdNotFound_ThrowsException(); + Parse_NoAssignmentToValue_ThrowsException(); + Parse_ValueIsNotEnclosedWithSingleQuotationMarks_ThrowsException(); + Parse_TypeMismatch_ThrowsException(); + Parse_ValueContainsEscapedSingleQuotationMark_IsUnescapedAfterParsing(); + Parse_Float_ParsedAsFloatProperty(); + Parse_Int_ParsedAsIntProperty(); + Parse_String_ParsedAsStringProperty(); + Parse_FloatDefaultValue_IsZero(); + Parse_IntDefaultValue_IsZero(); + Parse_StringDefaultValue_IsEmpty(); + Parse_NoType_ParsedAsString(); - dataNode->SetStringProperty("A", "A"); - dataNode->SetIntProperty("F", 2); - contents = simulationTemplate->CreateSimulation(); - MITK_TEST_CONDITION(contents == "2|A||0|0|E|2|0.5|E", "Create Simulation") + SetProperties_InputIsNull_ReturnsFalse(); + SetProperties_NotInitialized_ReturnsFalse(); + SetProperties_InputIsNotOwner_ReturnsFalse(); + SetProperties_ContainsTemplateAndReference_SetsPropertyAndReturnsTrue(); - // TODOs - // - Ambiguous IDs - // - Syntax errors + CreateSimulation_NotInitialized_ReturnsEmptyString(); + CreateSimulation_ContainsInvalidReference_ReturnsEmptyString(); + CreateSimulation_ContainsTemplateAndReference_ReturnsExpectedString(); MITK_TEST_END() } diff --git a/Modules/Simulation/Testing/mitkSimulationTest.cpp b/Modules/Simulation/Testing/mitkSimulationTest.cpp index 53df112b09..aeeb9c07ff 100644 --- a/Modules/Simulation/Testing/mitkSimulationTest.cpp +++ b/Modules/Simulation/Testing/mitkSimulationTest.cpp @@ -1,293 +1,292 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include -#include template static T lexical_cast(const std::string& string) { std::istringstream sstream(string); T value; sstream >> value; if (sstream.fail()) { MITK_ERROR << "Lexical cast failed for '" << string << "'!"; exit(EXIT_FAILURE); } return value; } static mitk::Simulation::Pointer LoadSimulation(const std::string& filename) { mitk::DataNode::Pointer dataNode = mitk::IOUtil::LoadDataNode(filename); mitk::Simulation::Pointer simulation = dynamic_cast(dataNode->GetData()); if (simulation.IsNull()) mitkThrow() << "Could not load '" << filename << "'!"; return simulation; } static bool IsActiveSimulation(mitk::Simulation::Pointer simulation) { if (simulation.IsNull()) mitkThrow() << "Invalid argument (null pointer)!"; sofa::simulation::Simulation::SPtr sofaSimulation = simulation->GetSimulation(); mitk::SimulationDrawTool* drawTool = simulation->GetDrawTool(); sofa::simulation::Simulation* activeSimulation = sofa::simulation::getSimulation(); sofa::core::visual::DrawTool*& activeDrawTool = sofa::core::visual::VisualParams::defaultInstance()->drawTool(); return sofaSimulation == activeSimulation && drawTool == activeDrawTool; } static bool NoActiveSimulation() { sofa::simulation::Simulation* activeSimulation = sofa::simulation::getSimulation(); sofa::core::visual::DrawTool*& activeDrawTool = sofa::core::visual::VisualParams::defaultInstance()->drawTool(); return activeSimulation == NULL && activeDrawTool == NULL; } static void DrawSimulation(mitk::Simulation::Pointer simulation, bool updateContext = true) { if (simulation.IsNull()) mitkThrow() << "Invalid argument (null pointer)!"; sofa::simulation::Node::SPtr rootNode = simulation->GetRootNode(); if (!rootNode) mitkThrow() << "Invalid argument (simulation is not initialized)!"; simulation->SetAsActiveSimulation(); if (updateContext) rootNode->execute(sofa::core::ExecParams::defaultInstance()); sofa::simulation::Simulation::SPtr sofaSimulation = simulation->GetSimulation(); sofaSimulation->updateVisual(rootNode.get()); sofaSimulation->draw(sofa::core::visual::VisualParams::defaultInstance(), rootNode.get()); } static vtkIdType GetNumberOfPolys(mitk::Surface::Pointer surface, unsigned int t = 0) { vtkIdType numPolys = 0; if (surface.IsNotNull()) { vtkPolyData* polyData = surface->GetVtkPolyData(); if (polyData != NULL) numPolys = polyData->GetNumberOfPolys(); } return numPolys; } -static void SetActiveSimulation_SimulationIsNull_NoActiveSimulation() +static void SetActiveSimulation_InputIsNull_NoActiveSimulation() { mitk::Simulation::SetActiveSimulation(NULL); - MITK_TEST_CONDITION(NoActiveSimulation(), "SetActiveSimulation_SimulationIsNull_NoActiveSimulation") + MITK_TEST_CONDITION(NoActiveSimulation(), "SetActiveSimulation_InputIsNull_NoActiveSimulation") } -static void SetAsActiveSimulation_SimulationIsNotInitialized_IsActiveSimulation() +static void SetAsActiveSimulation_NotInitialized_IsActiveSimulation() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); simulation->SetAsActiveSimulation(); - MITK_TEST_CONDITION(IsActiveSimulation(simulation), "SetAsActiveSimulation_SimulationIsNotInitialized_IsActiveSimulation") + MITK_TEST_CONDITION(IsActiveSimulation(simulation), "SetAsActiveSimulation_NotInitialized_IsActiveSimulation") } -static void SetAsActiveSimulation_SimulationIsInitialized_IsActiveSimulation(const std::string& filename) +static void SetAsActiveSimulation_Initialized_IsActiveSimulation(const std::string& filename) { mitk::Simulation::Pointer simulation = LoadSimulation(filename); simulation->SetAsActiveSimulation(); - MITK_TEST_CONDITION(IsActiveSimulation(simulation), "SetAsActiveSimulation_SimulationIsInitialized_IsActiveSimulation") + MITK_TEST_CONDITION(IsActiveSimulation(simulation), "SetAsActiveSimulation_Initialized_IsActiveSimulation") } -static void GetRootNode_SimulationIsNotInitialized_ReturnsNull() +static void GetRootNode_NotInitialized_ReturnsNull() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); - MITK_TEST_CONDITION(!simulation->GetRootNode(), "GetRootNode_SimulationIsNotInitialized_ReturnsNull") + MITK_TEST_CONDITION(!simulation->GetRootNode(), "GetRootNode_NotInitialized_ReturnsNull") } -static void GetRootNode_SimulationIsInitialized_ReturnsRootNode(const std::string& filename) +static void GetRootNode_Initialized_ReturnsRootNode(const std::string& filename) { mitk::Simulation::Pointer simulation = LoadSimulation(filename); - MITK_TEST_CONDITION(simulation->GetRootNode(), "GetRootNode_SimulationIsInitialized_ReturnsRootNode") + MITK_TEST_CONDITION(simulation->GetRootNode(), "GetRootNode_Initialized_ReturnsRootNode") } -static void GetDefaultDT_SimulationIsNotInitialized_ReturnsZero() +static void GetDefaultDT_NotInitialized_ReturnsZero() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); - MITK_TEST_CONDITION(mitk::Equal(simulation->GetDefaultDT(), 0.0), "GetDefaultDT_SimulationIsNotInitialized_ReturnsZero") + MITK_TEST_CONDITION(mitk::Equal(simulation->GetDefaultDT(), 0.0), "GetDefaultDT_NotInitialized_ReturnsZero") } -static void GetDefaultDT_SimulationIsInitialized_ReturnsDefaultDT(const std::string& filename, double dt) +static void GetDefaultDT_Initialized_ReturnsDefaultDT(const std::string& filename, double dt) { mitk::Simulation::Pointer simulation = LoadSimulation(filename); - MITK_TEST_CONDITION(mitk::Equal(simulation->GetDefaultDT(), dt), "GetDefaultDT_SimulationIsInitialized_ReturnsDefaultDT") + MITK_TEST_CONDITION(mitk::Equal(simulation->GetDefaultDT(), dt), "GetDefaultDT_Initialized_ReturnsDefaultDT") } static void GetDrawTool_Always_ReturnsDrawTool() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); MITK_TEST_CONDITION(simulation->GetDrawTool() != NULL, "GetDrawTool_Always_ReturnsDrawTool") } static void GetSimulation_Always_ReturnsSimulation() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); MITK_TEST_CONDITION(simulation->GetSimulation(), "GetSimulation_Always_ReturnsSimulation") } static void SetRootNode_InputIsNull_RootNodeIsNull() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); simulation->SetRootNode(NULL); MITK_TEST_CONDITION(!simulation->GetRootNode(), "SetRootNode_InputIsNull_RootNodeIsNull") } static void SetRootNode_InputIsValid_RootNodeEqualsInput() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); sofa::simulation::Node::SPtr rootNode = sofa::core::objectmodel::SPtr_dynamic_cast(sofa::core::objectmodel::New()); simulation->SetRootNode(rootNode.get()); MITK_TEST_CONDITION(simulation->GetRootNode() == rootNode, "SetRootNode_InputIsValid_RootNodeEqualsInput") } static void SetDefaultDT_InputIsPositive_DefaultDTEqualsInput() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); const double dt = 0.1; simulation->SetDefaultDT(dt); MITK_TEST_CONDITION(mitk::Equal(simulation->GetDefaultDT(), dt), "SetDefaultDT_InputIsPositive_DefaultDTEqualsInput") } static void SetDefaultDT_InputIsNegative_DefaultDTIsZero() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); simulation->SetDefaultDT(-0.1); MITK_TEST_CONDITION(mitk::Equal(simulation->GetDefaultDT(), 0.0), "SetDefaultDT_InputIsNegative_DefaultDTIsZero") } -static void TakeSnapshot_SimulationIsNotInitialized_ReturnsNull() +static void TakeSnapshot_NotInitialized_ReturnsNull() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); MITK_TEST_CONDITION(simulation->TakeSnapshot().IsNull(), "TakeSnapshot_SimulationIsNotInitialized_ReturnsNull"); } static void TakeSnapshot_SimulationWasDrawn_ReturnsSurface(const std::string& filename, vtkIdType numPolys) { mitk::Simulation::Pointer simulation = LoadSimulation(filename); DrawSimulation(simulation); mitk::Surface::Pointer snapshot = simulation->TakeSnapshot(); MITK_TEST_CONDITION(GetNumberOfPolys(snapshot) == numPolys, "TakeSnapshot_SimulationWasDrawn_ReturnsSurface"); } -static void AppendSnapshot_SimulationIsNotInitialized_ReturnsFalse() +static void AppendSnapshot_NotInitialized_ReturnsFalse() { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); mitk::Surface::Pointer record = mitk::Surface::New(); - MITK_TEST_CONDITION(!simulation->AppendSnapshot(record), "AppendSnapshot_SimulationIsNotInitialized_ReturnsFalse"); + MITK_TEST_CONDITION(!simulation->AppendSnapshot(record), "AppendSnapshot_NotInitialized_ReturnsFalse"); } static void AppendSnapshot_InputIsNull_ReturnsFalse(const std::string& filename) { mitk::Simulation::Pointer simulation = mitk::Simulation::New(); MITK_TEST_CONDITION(!simulation->AppendSnapshot(NULL), "AppendSnapshot_InputIsNull_ReturnsFalse"); } static void AppendSnapshot_InputIsEmpty_AppendsSnapshotAndReturnsTrue(const std::string& filename, vtkIdType numPolys) { mitk::Simulation::Pointer simulation = LoadSimulation(filename); DrawSimulation(simulation); mitk::Surface::Pointer record = mitk::Surface::New(); MITK_TEST_CONDITION(simulation->AppendSnapshot(record) && GetNumberOfPolys(record) == numPolys, "AppendSnapshot_InputIsEmpty_AppendsSnapshotAndReturnsTrue") } static void AppendSnapshot_InputIsNotEmpty_AppendsSnapshotAndReturnsTrue(const std::string& filename, vtkIdType numPolys) { mitk::Simulation::Pointer simulation = LoadSimulation(filename); DrawSimulation(simulation); mitk::Surface::Pointer record = mitk::Surface::New(); simulation->AppendSnapshot(record); MITK_TEST_CONDITION(simulation->AppendSnapshot(record) && GetNumberOfPolys(record, 1) == numPolys, "AppendSnapshot_InputIsNotEmpty_AppendsSnapshotAndReturnsTrue") } int mitkSimulationTest(int argc, char* argv[]) { if (argc != 4) { MITK_ERROR << "Invalid argument count!\n" << "Usage: mitkSimulationTest
\n" << " Path to simulation scene\n" << "
Default time step\n" << " Total polygon count of all visual models"; return EXIT_FAILURE; } const std::string filename = argv[1]; const double dt = lexical_cast(argv[2]); const vtkIdType numPolys = lexical_cast(argv[3]); mitk::RegisterSimulationObjectFactory(); MITK_TEST_BEGIN("mitkSimulationTest") - SetActiveSimulation_SimulationIsNull_NoActiveSimulation(); - SetAsActiveSimulation_SimulationIsNotInitialized_IsActiveSimulation(); - SetAsActiveSimulation_SimulationIsInitialized_IsActiveSimulation(filename); + SetActiveSimulation_InputIsNull_NoActiveSimulation(); + SetAsActiveSimulation_NotInitialized_IsActiveSimulation(); + SetAsActiveSimulation_Initialized_IsActiveSimulation(filename); - GetRootNode_SimulationIsNotInitialized_ReturnsNull(); - GetRootNode_SimulationIsInitialized_ReturnsRootNode(filename); + GetRootNode_NotInitialized_ReturnsNull(); + GetRootNode_Initialized_ReturnsRootNode(filename); - GetDefaultDT_SimulationIsNotInitialized_ReturnsZero(); - GetDefaultDT_SimulationIsInitialized_ReturnsDefaultDT(filename, dt); + GetDefaultDT_NotInitialized_ReturnsZero(); + GetDefaultDT_Initialized_ReturnsDefaultDT(filename, dt); GetDrawTool_Always_ReturnsDrawTool(); GetSimulation_Always_ReturnsSimulation(); SetRootNode_InputIsNull_RootNodeIsNull(); SetRootNode_InputIsValid_RootNodeEqualsInput(); SetDefaultDT_InputIsPositive_DefaultDTEqualsInput(); SetDefaultDT_InputIsNegative_DefaultDTIsZero(); - TakeSnapshot_SimulationIsNotInitialized_ReturnsNull(); + TakeSnapshot_NotInitialized_ReturnsNull(); TakeSnapshot_SimulationWasDrawn_ReturnsSurface(filename, numPolys); - AppendSnapshot_SimulationIsNotInitialized_ReturnsFalse(); + AppendSnapshot_NotInitialized_ReturnsFalse(); AppendSnapshot_InputIsNull_ReturnsFalse(filename); AppendSnapshot_InputIsEmpty_AppendsSnapshotAndReturnsTrue(filename, numPolys); AppendSnapshot_InputIsNotEmpty_AppendsSnapshotAndReturnsTrue(filename, numPolys); MITK_TEST_END() } diff --git a/Modules/Simulation/mitkSimulationTemplate.cpp b/Modules/Simulation/mitkSimulationTemplate.cpp index 8877c30c63..ff17db6d05 100644 --- a/Modules/Simulation/mitkSimulationTemplate.cpp +++ b/Modules/Simulation/mitkSimulationTemplate.cpp @@ -1,321 +1,416 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSimulationTemplate.h" #include #include +#include typedef std::vector > TemplateIndex; typedef std::vector > VariableContents; static TemplateIndex CreateTemplateIndex(const std::string& contents) { TemplateIndex templateIndex; std::string::size_type begin = 0; - begin = contents.find_first_of('{', 0); + begin = contents.find('{', 0); while (begin != std::string::npos) { - std::string::size_type end = contents.find_first_of('}', begin); + std::string::size_type end = contents.find('}', begin); if (end == std::string::npos) - mitkThrow() << "Expected closing brace before end of file!"; + mitkThrow() << "Could not create template index: Expected closing brace before end of file!"; + + if (contents.substr(begin + 1, end).find('{') != std::string::npos) + mitkThrow() << "Could not create template index: Expected closing brace before opening brace!"; templateIndex.push_back(std::make_pair(begin, ++end - begin)); - begin = contents.find_first_of('{', end); + begin = contents.find('{', end); } return templateIndex; } static std::vector ParseStaticContents(const std::string& contents, const TemplateIndex& templateIndex) { std::vector staticContents; std::string::size_type index = 0; for (TemplateIndex::const_iterator it = templateIndex.begin(); it != templateIndex.end(); ++it) { staticContents.push_back(contents.substr(index, it->first - index)); index = it->first + it->second; } staticContents.push_back(contents.substr(index)); return staticContents; } +static std::size_t CountSingleQuotationMarks(const std::string& string) +{ + if (string.empty()) + return 0; + + const std::string::size_type length = string.length(); + std::size_t n = string[0] == '\'' ? 1 : 0; + + for (std::string::size_type i = 1; i != length; ++i) + { + if (string[i] == '\'' && string[i - 1] != '\\') + ++n; + } + + return n; +} + +static bool IsEven(std::size_t number) +{ + return number % 2 == 0; +} + +static bool ValueExists(const std::string& templ, const std::string& valueName) +{ + std::string::size_type index = templ.find(valueName); + std::string::size_type definiteIndex = std::string::npos; + + while (index != std::string::npos) + { + if (IsEven(CountSingleQuotationMarks(templ.substr(0, index)))) + { + definiteIndex = index; + break; + } + + index = templ.find(valueName, index + 1); + } + + return definiteIndex != std::string::npos; +} + +static std::string UnescapeSingleQuotationMarks(const std::string& escapedString) +{ + std::string::size_type length = escapedString.length(); + std::string::size_type max_i = length - 2; + + std::vector unescapedString; + unescapedString.reserve(length); + + for (std::string::size_type i = 0; i != length; ++i) + { + char c = escapedString[i]; + + if (c == '\\' && i <= max_i && escapedString[i + 1] == '\'') + continue; + + unescapedString.push_back(c); + } + + return std::string(unescapedString.begin(), unescapedString.end()); +} + static std::string ParseValue(const std::string& templ, const std::string& valueName) { const char* spaces = " \t\n"; + std::string::size_type index = templ.find(valueName); + std::string::size_type definiteIndex = std::string::npos; - if (index != std::string::npos) + while (index != std::string::npos) { - index += valueName.length(); - index = templ.find_first_not_of(spaces, index); - - if (index != std::string::npos && templ[index] == '=') + if (IsEven(CountSingleQuotationMarks(templ.substr(0, index)))) { - ++index; - index = templ.find_first_not_of(spaces, index); + if (definiteIndex != std::string::npos) + mitkThrow() << "Could not parse " << templ << ": " << valueName << " is ambiguous!"; - if (index != std::string::npos && templ[index] == '\'') - { - ++index; - std::string::size_type length = templ.find_first_of('\'', index); - - if (length != std::string::npos) - { - length -= index; - return templ.substr(index, length); - } - } + definiteIndex = index; } + + index = templ.find(valueName, index + 1); } - return ""; + if (definiteIndex == std::string::npos) + mitkThrow() << "Could not parse " << templ << ": " << valueName << " not found!"; + + index = templ.find_first_not_of(spaces, definiteIndex + valueName.length()); + + if (index == std::string::npos || templ[index] != '=') + mitkThrow() << "Could not parse " << templ << ": Expected assignment to " << valueName << "!"; + + index = templ.find_first_not_of(spaces, index + 1); + + if (index == std::string::npos || templ[index] != '\'') + mitkThrow() << "Could not parse " << templ << ": Value of " << valueName << " is not enclosed within single quotation marks!"; + + std::string::size_type index2 = std::string::npos; + std::string::size_type length = templ.length(); + + for (std::string::size_type i = ++index; i != length; ++i) + { + if (templ[i] == '\'' && templ[i - 1] != '\\') + { + index2 = i; + break; + } + } + + return UnescapeSingleQuotationMarks(templ.substr(index, index2 - index)); } -template T FromString(const std::string& string) +template +static T lexical_cast(const std::string& string) { - std::istringstream stream(string); - + std::istringstream sstream(string); T value; - stream >> value; + + sstream >> value; + + if (sstream.fail()) + mitkThrow() << "Lexical cast failed for '" << string << "'!"; return value; } static std::pair ParseReference(const std::string& ref) { - std::string id = "{ref}"; - mitk::StringProperty::Pointer property = mitk::StringProperty::New(ref.substr(2, ref.length() - 4)); + std::string id = "{}"; + std::string string = ref.substr(2, ref.length() - 4); - return std::make_pair(id, property); + if (string.empty()) + mitkThrow() << "Could not parse " << ref << ": Reference is empty!"; + + return std::make_pair(id, mitk::StringProperty::New(string)); } static std::pair ParseTemplate(const std::string& templ) { - if (templ.length() > 4 && templ[1] == '\'') + std::string::size_type length = templ.length(); + + if (length < 4) // {''} + mitkThrow() << "Could not parse " << templ << ": Too short to be reference or template!"; + + if(!IsEven(CountSingleQuotationMarks(templ))) + mitkThrow() << "Could not parse " << templ << ": Number of single quotation marks is odd!"; + + if(templ[1] == '\'') { + if (templ[length - 2] != '\'') + mitkThrow() << "Could not parse " << templ << ": Reference does not end with '}!"; + return ParseReference(templ); } - else - { - std::string id = ParseValue(templ, "id"); - if (!id.empty()) - { - std::string type = ParseValue(templ, "type"); + if (length < 7) // {id=''} + mitkThrow() << "Could not parse " << templ << ": Too short to be template!"; - if (type.empty()) - type = "string"; + std::string id = ParseValue(templ, "id"); - mitk::BaseProperty::Pointer property; - std::string defaultValue = ParseValue(templ, "default"); + if (id.empty()) + mitkThrow() << "Could not parse " << templ << ": Id is empty!"; - if (type == "float") - { - float value = !defaultValue.empty() - ? FromString(defaultValue) - : 0.0; + std::string type = ValueExists(templ, "type") + ? ParseValue(templ, "type") + : "string"; - property = mitk::FloatProperty::New(value); - } - else if (type == "int") - { - int value = !defaultValue.empty() - ? FromString(defaultValue) - : 0.0; + std::string defaultValue = ValueExists(templ, "default") + ? ParseValue(templ, "default") + : ""; - property = mitk::IntProperty::New(value); - } - else if (type == "string") - { - std::string value = !defaultValue.empty() - ? defaultValue - : ""; + mitk::BaseProperty::Pointer property; - property = mitk::StringProperty::New(value); - } + if (type == "float") + { + float value = !defaultValue.empty() + ? lexical_cast(defaultValue) + : 0.0; - if (property.IsNotNull()) - return std::make_pair(id, property); - } + property = mitk::FloatProperty::New(value); } + else if (type == "int") + { + int value = !defaultValue.empty() + ? lexical_cast(defaultValue) + : 0.0; - std::string emptyString; - mitk::BaseProperty::Pointer nullPointer; + property = mitk::IntProperty::New(value); + } + else if (type == "string") + { + std::string value = !defaultValue.empty() + ? defaultValue + : ""; + + property = mitk::StringProperty::New(value); + } - return std::make_pair(emptyString, nullPointer); + if (property.IsNull()) + mitkThrow() << "Could not parse " << templ << ": Unknown type '" << type << "'!"; + + return std::make_pair(id, property); } static VariableContents ParseVariableContents(const std::string& contents, const TemplateIndex& templateIndex) { VariableContents variableContents; for (TemplateIndex::const_iterator it = templateIndex.begin(); it != templateIndex.end(); ++it) { std::string templ = contents.substr(it->first, it->second); - std::pair variableContent = ParseTemplate(templ); - - if (variableContent.first.empty() || variableContent.second.IsNull()) - mitkThrow() << "Could not parse " << templ << "!"; - - variableContents.push_back(variableContent); + variableContents.push_back(ParseTemplate(templ)); } return variableContents; } template class FirstEqualTo { public: FirstEqualTo(const T1& value) : m_Value(value) { } bool operator()(const std::pair& pair) const { return pair.first == m_Value; } private: T1 m_Value; }; mitk::SimulationTemplate::SimulationTemplate() : m_IsInitialized(false) { } mitk::SimulationTemplate::~SimulationTemplate() { } std::string mitk::SimulationTemplate::CreateSimulation() const { if (!m_IsInitialized) { - MITK_ERROR << "Simulation template is not initialized!"; + MITK_DEBUG << "Simulation template is not initialized!"; return ""; } std::string contents; for (VariableContents::size_type i = 0; i < m_VariableContents.size(); ++i) { contents += m_StaticContents[i]; - if (m_VariableContents[i].first == "{ref}") + if (m_VariableContents[i].first == "{}") { VariableContents::const_iterator it = std::find_if(m_VariableContents.begin(), m_VariableContents.end(), FirstEqualTo(m_VariableContents[i].second->GetValueAsString())); if (it == m_VariableContents.end()) { - MITK_ERROR << "Template '" << m_VariableContents[i].second << "' not found!"; + MITK_DEBUG << "Template '" << m_VariableContents[i].second << "' not found!"; return ""; } contents += it->second->GetValueAsString(); } else { contents += m_VariableContents[i].second->GetValueAsString(); } } contents += m_StaticContents.back(); return contents; } bool mitk::SimulationTemplate::Parse(const std::string& contents) { if (m_IsInitialized) { - MITK_ERROR << "Simulation template is already initialized!"; + MITK_DEBUG << "Simulation template is already initialized!"; return false; } TemplateIndex templateIndex = CreateTemplateIndex(contents); std::vector staticContents = ParseStaticContents(contents, templateIndex); VariableContents variableContents = ParseVariableContents(contents, templateIndex); std::swap(m_StaticContents, staticContents); std::swap(m_VariableContents, variableContents); m_IsInitialized = true; return true; } bool mitk::SimulationTemplate::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::SimulationTemplate::SetProperties(mitk::DataNode::Pointer dataNode) const { if (dataNode.IsNull()) { - MITK_ERROR << "Data node does not exist!"; + MITK_DEBUG << "Data node does not exist!"; return false; } if (!m_IsInitialized) { - MITK_ERROR << "Simulation template is not initialized!"; + MITK_DEBUG << "Simulation template is not initialized!"; return false; } if (dynamic_cast(dataNode->GetData()) != this) { - MITK_ERROR << "Data node does not own this simulation template!"; + MITK_DEBUG << "Data node does not own this simulation template!"; return false; } for(VariableContents::const_iterator it = m_VariableContents.begin(); it != m_VariableContents.end(); ++it) { - if (it->first != "{ref}") + if (it->first != "{}") dataNode->SetProperty(it->first.c_str(), it->second.GetPointer()); } return true; } void mitk::SimulationTemplate::SetRequestedRegion(const itk::DataObject*) { } void mitk::SimulationTemplate::SetRequestedRegionToLargestPossibleRegion() { } void mitk::SimulationTemplate::UpdateOutputInformation() { if (this->GetSource().IsNotNull()) this->GetSource()->UpdateOutputInformation(); } bool mitk::SimulationTemplate::VerifyRequestedRegion() { return true; } diff --git a/Plugins/org.mitk.gui.qt.simulation/documentation/UserManual/org_mitk_gui_qt_simulation.dox b/Plugins/org.mitk.gui.qt.simulation/documentation/UserManual/org_mitk_gui_qt_simulation.dox index 012887c77c..d2a5a4846f 100644 --- a/Plugins/org.mitk.gui.qt.simulation/documentation/UserManual/org_mitk_gui_qt_simulation.dox +++ b/Plugins/org.mitk.gui.qt.simulation/documentation/UserManual/org_mitk_gui_qt_simulation.dox @@ -1,103 +1,108 @@ /** \page org_mitk_gui_qt_simulation The Simulation Plugin \image html org_mitk_gui_qt_simulation.png "Icon of the Simulation Plugin." Available sections: - \ref org_mitk_gui_qt_simulationOverview - \ref org_mitk_gui_qt_simulationUsage - \ref org_mitk_gui_qt_simulationAdvancedUsage - \ref org_mitk_gui_qt_simulationSOFAPlugins - \ref org_mitk_gui_qt_simulationSimulationTemplates \section org_mitk_gui_qt_simulationOverview Overview The Simulation View allows you to run SOFA simulations. Its layout is very similar to the runSofa application and if you used SOFA before, you should immediately feel comfortable. Currently you can animate your simulation, step through with arbitrary time steps, and reset your simulation scene. Rendering options, e.g. rendering force fields or visual mappings, are represented by properties of a simulation data node. SOFA plugins can be loaded via the simulation preferences. You can easily take snapshots of your simulation scene, which are represented by 3D surfaces, or record your simulation scene as 3D+t surface. An advantage over runSofa is the ability to load multiple simulation scenes in parallel. There is also support for a kind of predefined editing of simulation scene files by writing simulation scene templates, which can be adjusted in the Properties View. \section org_mitk_gui_qt_simulationUsage Usage Simulation scenes are easily recognizable in the Data Manager by their SOFA icon. \image html DataManager_SimulationNodes.png "Data Manager with three simulation scenes." Selection of simulation scenes in the Data Manager does not affect the active simulation, which must be explicitly selected in the Simulation View. However, rendering properties, which can be seen in the Properties View, refer to the selected simulation scene in the Data Manager. \image html Properties_Simulation.png "Properties of a simulation scene." The appearance of the Simulation View changes slightly depending on which operations are currently possible or make sense, i.e. when no simulation scene was loaded, most of the Simulation View controls are disabled. \image html SimulationView_Inactive_Active.png "The Simulation View enables its controls depending on which of them can be executed at the moment." You can take snapshots of the active simulation scene by pressing the Take Snapshot button. You can toggle the Record button at any time to record all following simulation steps until you toggle it again. The number of recorded steps is shown during recording. \image html SimulationView_Recording.png "Recording simulation steps." Snapshots and records are appended to the corresponding simulation scene in the Data Manager. Snapshots are represented by 3D surfaces and recordings with more than a single frame as 3D+t surfaces. You can step through a 3D+t surface by using the Time slider of the Image Navigator View. \image html DataManager_Snapshots_Record.png "The caduceus simulation scene has two snapshots and a record." \section org_mitk_gui_qt_simulationAdvancedUsage Advanced Usage \subsection org_mitk_gui_qt_simulationSOFAPlugins SOFA Plugins SOFA plugins are supported by MITK and can be loaded during runtime. For more information, see \ref SimulationManualSOFAPluginBuildInstructions. \subsection org_mitk_gui_qt_simulationSimulationTemplates Simulation Templates -Simulation templates are extended SOFA scene files with the file extension .scn.template. +Simulation templates are extended SOFA scene files and have the file extension .scn.template. They contain special strings which are parsed and displayed as properties in the Properties View. -You can create a true simulation scene from a simulation template via its context menu in the Data Manager. +You can create a simulation scene from a simulation template via its context menu in the Data Manager. Behind the scenes all templates of a simulation template file are replaced by concrete values. This simulation scene is saved in the same directory as the simulation template and automatically loaded. \image html DataManager_CreateSimulation.png "A simulation can be created from a simulation template via its context menu in the Data Manager."
\image html Properties_SimulationTemplate.png "A simulation template typically has several properties which can be adjusted before creating a simulation." The syntax for templates is as follows: \code {id='Collision.Alarm Distance' type='int' default='5'} \endcode id must be unique and is parsed as property name, i.e. use periods to arrange properties in tree order. type is optional and its default is string. Other valid types are int and float. -default is also optional and specify the default value of the property. +default is also optional and specifies the default value of the property. Since IDs must be unique but some values are supposed to appear in multiple places in a simulation template, you can specify references to templates as follows: \code {'Collision.Alarm Distance'} \endcode +\warning +Braces are only allowed to open or close templates and references. +References must begin with {' and end with '}'. +You must escape single quotation marks C-style, e.g. {id='Flinders\'s Cat' default='Trim'}. + A good practice is to list all templates within XML comments at the beginning of the simulation template file and to just reference them as needed. \code \endcode */