From d65fb811ffb3bdab3180301f3e2d44c5dd94d76a Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Wed, 16 Jul 2025 22:06:13 -0300 Subject: [PATCH 1/7] ENH: Allow markups as linear extrusion input profiles --- .../vtkSlicerDynamicModelerExtrudeTool.cxx | 200 +++++++++++++++--- .../vtkSlicerDynamicModelerExtrudeTool.h | 6 +- 2 files changed, 173 insertions(+), 33 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx index 36c432b..3237ad1 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx @@ -24,8 +24,11 @@ // MRML includes #include +#include #include #include +#include +#include #include #include @@ -60,12 +63,21 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() vtkNew inputModelEvents; inputModelEvents->InsertNextTuple1(vtkCommand::ModifiedEvent); inputModelEvents->InsertNextTuple1(vtkMRMLModelNode::MeshModifiedEvent); + inputModelEvents->InsertNextTuple1(vtkMRMLMarkupsNode::PointModifiedEvent); + inputModelEvents->InsertNextTuple1(vtkMRMLMarkupsNode::PointPositionDefinedEvent); + inputModelEvents->InsertNextTuple1(vtkMRMLMarkupsNode::PointPositionUndefinedEvent); inputModelEvents->InsertNextTuple1(vtkMRMLTransformableNode::TransformModifiedEvent); vtkNew inputModelClassNames; inputModelClassNames->InsertNextValue("vtkMRMLModelNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsPlaneNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsAngleNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsFiducialNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsLineNode"); NodeInfo inputModel( - "Model", - "Model to be extruded.", + "Model or Curve", + "Profile to be extruded.", inputModelClassNames, EXTRUDE_INPUT_MODEL_REFERENCE_ROLE, true, @@ -77,6 +89,8 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() vtkNew inputMarkupEvents; inputMarkupEvents->InsertNextTuple1(vtkCommand::ModifiedEvent); inputMarkupEvents->InsertNextTuple1(vtkMRMLMarkupsNode::PointModifiedEvent); + inputMarkupEvents->InsertNextTuple1(vtkMRMLMarkupsNode::PointPositionDefinedEvent); + inputMarkupEvents->InsertNextTuple1(vtkMRMLMarkupsNode::PointPositionUndefinedEvent); inputMarkupEvents->InsertNextTuple1(vtkMRMLTransformableNode::TransformModifiedEvent); vtkNew inputMarkupClassNames; inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsFiducialNode"); @@ -127,9 +141,9 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() PARAMETER_DOUBLE, 0.0); this->InputParameterInfo.push_back(parameterScale); - this->InputModelToWorldTransformFilter = vtkSmartPointer::New(); - this->InputModelNodeToWorldTransform = vtkSmartPointer::New(); - this->InputModelToWorldTransformFilter->SetTransform(this->InputModelNodeToWorldTransform); + this->InputProfileToWorldTransformFilter = vtkSmartPointer::New(); + this->InputProfileNodeToWorldTransform = vtkSmartPointer::New(); + this->InputProfileToWorldTransformFilter->SetTransform(this->InputProfileNodeToWorldTransform); // This is used when the input polydata does not have normals this->NormalsFilter = vtkSmartPointer::New(); @@ -139,7 +153,7 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() this->AssignAttributeFilter = vtkSmartPointer::New(); this->ExtrudeFilter = vtkSmartPointer::New(); - this->ExtrudeFilter->SetInputConnection(this->InputModelToWorldTransformFilter->GetOutputPort()); + this->ExtrudeFilter->SetInputConnection(this->InputProfileToWorldTransformFilter->GetOutputPort()); this->TriangleFilter = vtkSmartPointer::New(); this->TriangleFilter->SetInputConnection(this->ExtrudeFilter->GetOutputPort()); @@ -160,6 +174,34 @@ const char* vtkSlicerDynamicModelerExtrudeTool::GetName() return "Extrude"; } +//---------------------------------------------------------------------------- +/* +void GeneratePolyDataFromMarkups(vtkMRMLMarkupsNode* markupsNode, vtkPolyData* outputPolyData) +{ + if (!markupsNode || !outputPolyData) + { + vtkErrorMacro("GeneratePolyDataFromMarkups failed: invalid input"); + return; + } + + // Clear output polydata + outputPolyData->Initialize(); + + // Save control points to a vtkPoints object + vtkPoints* points = vtkPoints::New(); + vtkIdType numberOfControlPoints = markupsNode->GetNumberOfControlPoints(); + points->SetNumberOfPoints(numberOfControlPoints); + for (vtkIdType i = 0; i < numberOfControlPoints; ++i) + { + double pos[3]; + markupsNode->GetControlPointPosition(i, pos); + points->SetPoint(i, pos); + } + outputPolyData->SetPoints(points); + + +}*/ + //---------------------------------------------------------------------------- bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* surfaceEditorNode) { @@ -176,28 +218,125 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* return true; } - vtkMRMLModelNode* inputModelNode = vtkMRMLModelNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); - if (!inputModelNode) + vtkMRMLModelNode* inputProfileModelNode = vtkMRMLModelNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + vtkMRMLMarkupsNode* inputProfileMarkupsNode = vtkMRMLMarkupsNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + bool profileIsModel = (inputProfileModelNode) && (!inputProfileMarkupsNode); + bool profileIsMarkup = (!inputProfileModelNode) && (inputProfileMarkupsNode); + if ((!profileIsModel) && (!profileIsMarkup)) { - vtkErrorMacro("Invalid input model node!"); + vtkErrorMacro("Invalid input node!"); return false; } - if (!inputModelNode->GetMesh() || inputModelNode->GetMesh()->GetNumberOfPoints() == 0) - { - vtkNew outputPolyData; - outputModelNode->SetAndObservePolyData(outputPolyData); - return true; - } - - if (inputModelNode->GetParentTransformNode()) + if (profileIsModel) { - inputModelNode->GetParentTransformNode()->GetTransformToWorld(this->InputModelNodeToWorldTransform); + if (!inputProfileModelNode->GetMesh() || inputProfileModelNode->GetMesh()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + if (inputProfileModelNode->GetParentTransformNode()) + { + inputProfileModelNode->GetParentTransformNode()->GetTransformToWorld(this->InputProfileNodeToWorldTransform); + } + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileModelNode->GetMeshConnection()); + } } - else + else // profileIsMarkup { - this->InputModelNodeToWorldTransform->Identity(); + vtkMRMLMarkupsFiducialNode* inputProfileMarkupsPointsNode = vtkMRMLMarkupsFiducialNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + vtkMRMLMarkupsLineNode* inputProfileMarkupsLineNode = vtkMRMLMarkupsLineNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + vtkMRMLMarkupsAngleNode* inputProfileMarkupsAngleNode = vtkMRMLMarkupsAngleNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + vtkMRMLMarkupsPlaneNode* inputProfileMarkupsPlaneNode = vtkMRMLMarkupsPlaneNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + vtkMRMLMarkupsCurveNode* inputProfileMarkupsCurveNode = vtkMRMLMarkupsCurveNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + vtkMRMLMarkupsClosedCurveNode* inputProfileMarkupsClosedCurveNode = vtkMRMLMarkupsClosedCurveNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MODEL_REFERENCE_ROLE)); + this->InputProfileNodeToWorldTransform->Identity(); // this way the transformFilter is a pass-through + if (inputProfileMarkupsPointsNode) + { + if (!inputProfileMarkupsPointsNode->GetCurveWorld() || inputProfileMarkupsPointsNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsPointsNode->GetCurveWorldConnection()); + } + } + if (inputProfileMarkupsLineNode) + { + if (!inputProfileMarkupsLineNode->GetCurveWorld() || inputProfileMarkupsLineNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsLineNode->GetCurveWorldConnection()); + } + } + else if (inputProfileMarkupsAngleNode) + { + if (!inputProfileMarkupsAngleNode->GetCurveWorld() || inputProfileMarkupsAngleNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsAngleNode->GetCurveWorldConnection()); + } + } + if (inputProfileMarkupsPlaneNode) + { + if (!inputProfileMarkupsPlaneNode->GetCurveWorld() || inputProfileMarkupsPlaneNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + // This may need to be improved + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsPlaneNode->GetCurveWorldConnection()); + } + } + else if (inputProfileMarkupsCurveNode) + { + if (!inputProfileMarkupsCurveNode->GetCurveWorld() || inputProfileMarkupsCurveNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsCurveNode->GetCurveWorldConnection()); + } + } + else if (inputProfileMarkupsClosedCurveNode) + { + this->InputProfileNodeToWorldTransform->Identity(); // this way the transformFilter is a pass-through + if (!inputProfileMarkupsClosedCurveNode->GetCurveWorld() || inputProfileMarkupsClosedCurveNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsClosedCurveNode->GetCurveWorldConnection()); + } + } } + + if (outputModelNode && outputModelNode->GetParentTransformNode()) { outputModelNode->GetParentTransformNode()->GetTransformFromWorld(this->OutputWorldToModelTransform); @@ -207,16 +346,15 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* this->OutputWorldToModelTransform->Identity(); } - this->InputModelToWorldTransformFilter->SetInputConnection(inputModelNode->GetMeshConnection()); - this->InputModelToWorldTransformFilter->Update(); + this->InputProfileToWorldTransformFilter->Update(); // with filter input below we'll never create normals unless they don't exist - this->ExtrudeFilter->SetInputConnection(this->InputModelToWorldTransformFilter->GetOutputPort()); + this->ExtrudeFilter->SetInputConnection(this->InputProfileToWorldTransformFilter->GetOutputPort()); vtkDataArray* normalsArray = nullptr; - if (this->InputModelToWorldTransformFilter->GetOutput() - && this->InputModelToWorldTransformFilter->GetOutput()->GetPointData()) + if (this->InputProfileToWorldTransformFilter->GetOutput() + && this->InputProfileToWorldTransformFilter->GetOutput()->GetPointData()) { - normalsArray = this->InputModelToWorldTransformFilter->GetOutput()->GetPointData()->GetNormals(); + normalsArray = this->InputProfileToWorldTransformFilter->GetOutput()->GetPointData()->GetNormals(); } vtkMRMLMarkupsNode* markupsNode = vtkMRMLMarkupsNode::SafeDownCast(surfaceEditorNode->GetNodeReference(EXTRUDE_INPUT_MARKUPS_REFERENCE_ROLE)); @@ -232,12 +370,12 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* if (normalsArray) { // Normals is already available - this->ExtrudeFilter->SetInputConnection(this->InputModelToWorldTransformFilter->GetOutputPort()); + this->ExtrudeFilter->SetInputConnection(this->InputProfileToWorldTransformFilter->GetOutputPort()); } else { // Create the normals by inserting normals filter before extrusion filter - this->NormalsFilter->SetInputConnection(this->InputModelToWorldTransformFilter->GetOutputPort()); + this->NormalsFilter->SetInputConnection(this->InputProfileToWorldTransformFilter->GetOutputPort()); this->ExtrudeFilter->SetInputConnection(this->NormalsFilter->GetOutputPort()); } this->ExtrudeFilter->SetExtrusionTypeToNormalExtrusion(); @@ -276,7 +414,7 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* // Extrude towards a point // No need for normals, so we can always connect to the input model - this->ExtrudeFilter->SetInputConnection(this->InputModelToWorldTransformFilter->GetOutputPort()); + this->ExtrudeFilter->SetInputConnection(this->InputProfileToWorldTransformFilter->GetOutputPort()); if (extrusionLength == 0) { @@ -291,8 +429,8 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* { // Absolute length is specified, this is not supported directly by the extrusion filter - this->InputModelToWorldTransformFilter->Update(); - vtkPolyData* inputPolyData = this->InputModelToWorldTransformFilter->GetOutput(); + this->InputProfileToWorldTransformFilter->Update(); + vtkPolyData* inputPolyData = this->InputProfileToWorldTransformFilter->GetOutput(); // overwrite normals array with directions of (center-pointAt) array vtkNew extrusionVectorArray; diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h index 5cfd3c9..df1870e 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h @@ -72,8 +72,8 @@ class VTK_SLICER_DYNAMICMODELER_MODULE_LOGIC_EXPORT vtkSlicerDynamicModelerExtru void operator=(const vtkSlicerDynamicModelerExtrudeTool&); protected: - vtkSmartPointer InputModelToWorldTransformFilter; - vtkSmartPointer InputModelNodeToWorldTransform; + vtkSmartPointer InputProfileToWorldTransformFilter; + vtkSmartPointer InputProfileNodeToWorldTransform; vtkSmartPointer ExtrudeFilter; vtkSmartPointer TriangleFilter; @@ -83,6 +83,8 @@ class VTK_SLICER_DYNAMICMODELER_MODULE_LOGIC_EXPORT vtkSlicerDynamicModelerExtru vtkSmartPointer OutputModelToWorldTransformFilter; vtkSmartPointer OutputWorldToModelTransform; + //void GeneratePolyDataFromMarkups(vtkMRMLMarkupsNode* markupsNode, vtkPolyData* outputPolyData); + private: vtkSlicerDynamicModelerExtrudeTool(const vtkSlicerDynamicModelerExtrudeTool&) = delete; }; From 947f1daf61cd77c70b86525c0c15336384928e40 Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Sat, 19 Jul 2025 18:50:48 -0300 Subject: [PATCH 2/7] ENH: Add support for curve markups in extrusion tool and update documentation --- .../vtkSlicerDynamicModelerExtrudeTool.cxx | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx index 3237ad1..e30702d 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx @@ -45,6 +45,7 @@ #include #include #include +#include //---------------------------------------------------------------------------- vtkToolNewMacro(vtkSlicerDynamicModelerExtrudeTool); @@ -96,12 +97,16 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsFiducialNode"); inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsLineNode"); inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsPlaneNode"); + inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsAngleNode"); + inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); + inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); NodeInfo inputMarkups( "Markups", "Markups to specify extrusion vector.\n" - "- Plane: extrusion vector is the plane normal.\n" + "- Plane or Angle: extrusion vector is the plane normal.\n" "- Line: extrusion vector is from the first to the second point of the line.\n" "- Point list: extrusion vector is from each model point to the first point of the markup.\n" + "- Curve or Closed Curve: extrusion vector is best-fitting plane normal.\n" "- No markup is selected: extrusion vector is the input model's surface normal.", inputMarkupClassNames, EXTRUDE_INPUT_MARKUPS_REFERENCE_ROLE, @@ -174,34 +179,6 @@ const char* vtkSlicerDynamicModelerExtrudeTool::GetName() return "Extrude"; } -//---------------------------------------------------------------------------- -/* -void GeneratePolyDataFromMarkups(vtkMRMLMarkupsNode* markupsNode, vtkPolyData* outputPolyData) -{ - if (!markupsNode || !outputPolyData) - { - vtkErrorMacro("GeneratePolyDataFromMarkups failed: invalid input"); - return; - } - - // Clear output polydata - outputPolyData->Initialize(); - - // Save control points to a vtkPoints object - vtkPoints* points = vtkPoints::New(); - vtkIdType numberOfControlPoints = markupsNode->GetNumberOfControlPoints(); - points->SetNumberOfPoints(numberOfControlPoints); - for (vtkIdType i = 0; i < numberOfControlPoints; ++i) - { - double pos[3]; - markupsNode->GetControlPointPosition(i, pos); - points->SetPoint(i, pos); - } - outputPolyData->SetPoints(points); - - -}*/ - //---------------------------------------------------------------------------- bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* surfaceEditorNode) { @@ -388,9 +365,31 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* vtkMRMLMarkupsFiducialNode* markupsFiducialNode = vtkMRMLMarkupsFiducialNode::SafeDownCast(markupsNode); vtkMRMLMarkupsLineNode* markupsLineNode = vtkMRMLMarkupsLineNode::SafeDownCast(markupsNode); vtkMRMLMarkupsPlaneNode* markupsPlaneNode = vtkMRMLMarkupsPlaneNode::SafeDownCast(markupsNode); + vtkMRMLMarkupsAngleNode* markupsAngleNode = vtkMRMLMarkupsAngleNode::SafeDownCast(markupsNode); + vtkMRMLMarkupsCurveNode* markupsCurveNode = vtkMRMLMarkupsCurveNode::SafeDownCast(markupsNode); + vtkMRMLMarkupsClosedCurveNode* markupsClosedCurveNode = vtkMRMLMarkupsClosedCurveNode::SafeDownCast(markupsNode); + bool markupsToUseBestFittingPlane = (markupsAngleNode || markupsCurveNode || markupsClosedCurveNode); int numberOfControlPoints = markupsNode->GetNumberOfControlPoints(); - if (markupsPlaneNode) + if (markupsToUseBestFittingPlane && (numberOfControlPoints >= 3)) + { + const double magnitude = 1.0; // normal vector magnitude is always 1.0 + this->ExtrudeFilter->SetScaleFactor(magnitude * extrusionScale + extrusionLength); + // get control points in world coordinates + vtkNew controlPointsWorld; + for (int i = 0; i < numberOfControlPoints; ++i) + { + double controlPointWorld[3] = { 0, 0, 0 }; + markupsNode->GetNthControlPointPositionWorld(i, controlPointWorld); + controlPointsWorld->InsertNextPoint(controlPointWorld); + } + double bestFitOriginWorld[3] = { 0, 0, 0 }; + double bestFitNormalWorld[3] = { 0, 0, 0 }; + vtkPlane::ComputeBestFittingPlane(controlPointsWorld, bestFitOriginWorld, bestFitNormalWorld); + this->ExtrudeFilter->SetVector(bestFitNormalWorld); + this->ExtrudeFilter->SetExtrusionTypeToVectorExtrusion(); + } + else if (markupsPlaneNode) { // Plane normal is used for extrusion markupsPlaneNode->GetRequiredNumberOfControlPoints(); From f6ca0a09079e6b498db8d4d68a54de16d095ed9c Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Sat, 19 Jul 2025 19:29:34 -0300 Subject: [PATCH 3/7] ENH: Update ExtrudeTool to support markups planes as input profiles --- .../vtkSlicerDynamicModelerExtrudeTool.cxx | 19 +++++++++++++++---- .../vtkSlicerDynamicModelerExtrudeTool.h | 2 ++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx index e30702d..0394a5d 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx @@ -46,6 +46,7 @@ #include #include #include +#include //---------------------------------------------------------------------------- vtkToolNewMacro(vtkSlicerDynamicModelerExtrudeTool); @@ -77,7 +78,7 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() inputModelClassNames->InsertNextValue("vtkMRMLMarkupsFiducialNode"); inputModelClassNames->InsertNextValue("vtkMRMLMarkupsLineNode"); NodeInfo inputModel( - "Model or Curve", + "Model or Markup", "Profile to be extruded.", inputModelClassNames, EXTRUDE_INPUT_MODEL_REFERENCE_ROLE, @@ -150,6 +151,9 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() this->InputProfileNodeToWorldTransform = vtkSmartPointer::New(); this->InputProfileToWorldTransformFilter->SetTransform(this->InputProfileNodeToWorldTransform); + // Auxiliar plane source is used to create a plane for the input profile when it is a markups plane + this->AuxiliarPlaneSource = vtkSmartPointer::New(); + // This is used when the input polydata does not have normals this->NormalsFilter = vtkSmartPointer::New(); this->NormalsFilter->AutoOrientNormalsOn(); @@ -272,7 +276,7 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* } if (inputProfileMarkupsPlaneNode) { - if (!inputProfileMarkupsPlaneNode->GetCurveWorld() || inputProfileMarkupsPlaneNode->GetCurveWorld()->GetNumberOfPoints() == 0) + if (!inputProfileMarkupsPlaneNode->GetIsPlaneValid()) { vtkNew outputPolyData; outputModelNode->SetAndObservePolyData(outputPolyData); @@ -280,8 +284,15 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* } else { - // This may need to be improved - this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsPlaneNode->GetCurveWorldConnection()); + // Update the plane based on the corner points + vtkNew planeCornerPoints_World; + inputProfileMarkupsPlaneNode->GetPlaneCornerPointsWorld(planeCornerPoints_World); + + // Update the plane fill + this->AuxiliarPlaneSource->SetOrigin(planeCornerPoints_World->GetPoint(0)); + this->AuxiliarPlaneSource->SetPoint1(planeCornerPoints_World->GetPoint(1)); + this->AuxiliarPlaneSource->SetPoint2(planeCornerPoints_World->GetPoint(3)); + this->InputProfileToWorldTransformFilter->SetInputConnection(this->AuxiliarPlaneSource->GetOutputPort()); } } else if (inputProfileMarkupsCurveNode) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h index df1870e..a860a75 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.h @@ -47,6 +47,7 @@ class vtkTriangleFilter; #include #include #include +#include #include "vtkSlicerDynamicModelerTool.h" @@ -75,6 +76,7 @@ class VTK_SLICER_DYNAMICMODELER_MODULE_LOGIC_EXPORT vtkSlicerDynamicModelerExtru vtkSmartPointer InputProfileToWorldTransformFilter; vtkSmartPointer InputProfileNodeToWorldTransform; + vtkSmartPointer AuxiliarPlaneSource; // used to create a plane for the input profile when it is a markups plane vtkSmartPointer ExtrudeFilter; vtkSmartPointer TriangleFilter; vtkSmartPointer NormalsFilter; From a4fa6ab586f24dc64138013423f1f577d96c1466 Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Sat, 19 Jul 2025 20:12:50 -0300 Subject: [PATCH 4/7] ENH: Enhance RevolveTool to support fiducials, line, angle and plane markups as profiles --- .../vtkSlicerDynamicModelerRevolveTool.cxx | 90 +++++++++++++++++-- .../vtkSlicerDynamicModelerRevolveTool.h | 4 + 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx index b437c53..25348aa 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx @@ -44,6 +44,8 @@ #include #include #include +#include +#include //---------------------------------------------------------------------------- vtkToolNewMacro(vtkSlicerDynamicModelerRevolveTool); @@ -70,6 +72,10 @@ vtkSlicerDynamicModelerRevolveTool::vtkSlicerDynamicModelerRevolveTool() inputModelEvents->InsertNextTuple1(vtkMRMLTransformableNode::TransformModifiedEvent); vtkNew inputModelClassNames; inputModelClassNames->InsertNextValue("vtkMRMLModelNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsFiducialNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsLineNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsPlaneNode"); + inputModelClassNames->InsertNextValue("vtkMRMLMarkupsAngleNode"); inputModelClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); inputModelClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); NodeInfo inputProfile( @@ -92,9 +98,11 @@ vtkSlicerDynamicModelerRevolveTool::vtkSlicerDynamicModelerRevolveTool() inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsLineNode"); inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsPlaneNode"); inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsAngleNode"); + //inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); + //inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); NodeInfo inputMarkups( "Markups", - "Markups to specify spatial revolution axis. Normal for plane and angle, superior axis for a point. The direction of rotation is determined from the direction of the rotation axis by the right hand rule.", + "Markups to specify spatial revolution axis. Normal for plane and angle, superior axis for a point. Best fitting plane normal for curve and closed curve. The direction of rotation is determined from the direction of the rotation axis by the right hand rule.", inputMarkupClassNames, REVOLVE_INPUT_MARKUPS_REFERENCE_ROLE, /*required*/ true, @@ -183,6 +191,8 @@ vtkSlicerDynamicModelerRevolveTool::vtkSlicerDynamicModelerRevolveTool() this->InputParameterInfo.push_back(parameterDeltaRadius); + // Auxiliar plane source is used to create a plane for the input profile when it is a markups plane + this->AuxiliarPlaneSource = vtkSmartPointer::New(); this->InputProfileToWorldTransformFilter = vtkSmartPointer::New(); this->InputProfileNodeToWorldTransform = vtkSmartPointer::New(); @@ -248,18 +258,20 @@ bool vtkSlicerDynamicModelerRevolveTool::RunInternal(vtkMRMLDynamicModelerNode* } vtkMRMLModelNode* inputProfileModelNode = vtkMRMLModelNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); + vtkMRMLMarkupsNode* inputProfileMarkupsNode = vtkMRMLMarkupsNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); vtkMRMLMarkupsCurveNode* inputProfileMarkupsCurveNode = vtkMRMLMarkupsCurveNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); vtkMRMLMarkupsClosedCurveNode* inputProfileMarkupsClosedCurveNode = vtkMRMLMarkupsClosedCurveNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); - bool profileIsModel = (inputProfileModelNode) && (!inputProfileMarkupsCurveNode) && (!inputProfileMarkupsClosedCurveNode); - bool profileIsCurve = (!inputProfileModelNode) && (inputProfileMarkupsCurveNode) && (!inputProfileMarkupsClosedCurveNode); - bool profileIsClosedCurve = (!inputProfileModelNode) && (inputProfileMarkupsCurveNode) && (inputProfileMarkupsClosedCurveNode); - if ((!profileIsModel) && (!profileIsCurve) && (!profileIsClosedCurve)) + vtkMRMLMarkupsFiducialNode* inputProfileMarkupsPointsNode = vtkMRMLMarkupsFiducialNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); + vtkMRMLMarkupsLineNode* inputProfileMarkupsLineNode = vtkMRMLMarkupsLineNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); + vtkMRMLMarkupsPlaneNode* inputProfileMarkupsPlaneNode = vtkMRMLMarkupsPlaneNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); + vtkMRMLMarkupsAngleNode* inputProfileMarkupsAngleNode = vtkMRMLMarkupsAngleNode::SafeDownCast(surfaceEditorNode->GetNodeReference(REVOLVE_INPUT_PROFILE_REFERENCE_ROLE)); + if ((!inputProfileModelNode) && (!inputProfileMarkupsNode)) { vtkErrorMacro("Invalid input node!"); return false; } - if (profileIsModel) + if (inputProfileModelNode) { if (!inputProfileModelNode->GetMesh() || inputProfileModelNode->GetMesh()->GetNumberOfPoints() == 0) { @@ -276,10 +288,70 @@ bool vtkSlicerDynamicModelerRevolveTool::RunInternal(vtkMRMLDynamicModelerNode* this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileModelNode->GetMeshConnection()); } } - else + else // inputProfileMarkupsNode { this->InputProfileNodeToWorldTransform->Identity(); // this way the transformFilter is a pass-through - if (profileIsCurve) + if (inputProfileMarkupsPointsNode) + { + if (!inputProfileMarkupsPointsNode->GetCurveWorld() || inputProfileMarkupsPointsNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsPointsNode->GetCurveWorldConnection()); + } + } + else if (inputProfileMarkupsLineNode) + { + if (!inputProfileMarkupsLineNode->GetCurveWorld() || inputProfileMarkupsLineNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsLineNode->GetCurveWorldConnection()); + } + } + else if (inputProfileMarkupsAngleNode) + { + if (!inputProfileMarkupsAngleNode->GetCurveWorld() || inputProfileMarkupsAngleNode->GetCurveWorld()->GetNumberOfPoints() == 0) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsAngleNode->GetCurveWorldConnection()); + } + } + else if (inputProfileMarkupsPlaneNode) + { + if (!inputProfileMarkupsPlaneNode->GetIsPlaneValid()) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } + else + { + // Update the plane based on the corner points + vtkNew planeCornerPoints_World; + inputProfileMarkupsPlaneNode->GetPlaneCornerPointsWorld(planeCornerPoints_World); + + // Update the plane fill + this->AuxiliarPlaneSource->SetOrigin(planeCornerPoints_World->GetPoint(0)); + this->AuxiliarPlaneSource->SetPoint1(planeCornerPoints_World->GetPoint(1)); + this->AuxiliarPlaneSource->SetPoint2(planeCornerPoints_World->GetPoint(3)); + this->InputProfileToWorldTransformFilter->SetInputConnection(this->AuxiliarPlaneSource->GetOutputPort()); + } + } + else if (inputProfileMarkupsCurveNode) { if (!inputProfileMarkupsCurveNode->GetCurveWorld() || inputProfileMarkupsCurveNode->GetCurveWorld()->GetNumberOfPoints() == 0) { @@ -292,7 +364,7 @@ bool vtkSlicerDynamicModelerRevolveTool::RunInternal(vtkMRMLDynamicModelerNode* this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsCurveNode-> GetCurveWorldConnection()); } } - else if (profileIsClosedCurve) + else if (inputProfileMarkupsClosedCurveNode) { if (!inputProfileMarkupsClosedCurveNode->GetCurveWorld() || inputProfileMarkupsClosedCurveNode->GetCurveWorld()->GetNumberOfPoints() == 0) { diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.h b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.h index eaf3ccf..f792b29 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.h +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.h @@ -37,6 +37,7 @@ class vtkMRMLDynamicModelerNode; #include #include #include +#include #include "vtkSlicerDynamicModelerTool.h" @@ -73,6 +74,9 @@ class VTK_SLICER_DYNAMICMODELER_MODULE_LOGIC_EXPORT vtkSlicerDynamicModelerRevol vtkSmartPointer CapTransformFilter; vtkSmartPointer CapTransform; + // Auxiliar plane source is used to create a plane for the input profile when it is a markups plane + vtkSmartPointer AuxiliarPlaneSource; + vtkSmartPointer RevolveFilter; vtkSmartPointer AppendFilter; From 77efd2528a14c9a25dccdcba023b7009aca20bd5 Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Sat, 19 Jul 2025 20:13:33 -0300 Subject: [PATCH 5/7] ENH: Refactor conditional checks in ExtrudeTool to use 'else if' for clarity --- DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx index 0394a5d..d13a149 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx @@ -248,7 +248,7 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsPointsNode->GetCurveWorldConnection()); } } - if (inputProfileMarkupsLineNode) + else if (inputProfileMarkupsLineNode) { if (!inputProfileMarkupsLineNode->GetCurveWorld() || inputProfileMarkupsLineNode->GetCurveWorld()->GetNumberOfPoints() == 0) { @@ -274,7 +274,7 @@ bool vtkSlicerDynamicModelerExtrudeTool::RunInternal(vtkMRMLDynamicModelerNode* this->InputProfileToWorldTransformFilter->SetInputConnection(inputProfileMarkupsAngleNode->GetCurveWorldConnection()); } } - if (inputProfileMarkupsPlaneNode) + else if (inputProfileMarkupsPlaneNode) { if (!inputProfileMarkupsPlaneNode->GetIsPlaneValid()) { From 067e8a3d619fb183036bf544246a88a91fc9e7af Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Sat, 19 Jul 2025 20:29:16 -0300 Subject: [PATCH 6/7] ENH: Add support for curve and closed curve markups in RevolveTool --- .../vtkSlicerDynamicModelerRevolveTool.cxx | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx index 25348aa..66fe690 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx @@ -79,7 +79,7 @@ vtkSlicerDynamicModelerRevolveTool::vtkSlicerDynamicModelerRevolveTool() inputModelClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); inputModelClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); NodeInfo inputProfile( - "Model or Curve", + "Model or Markup", "Profile to be revolved.", inputModelClassNames, REVOLVE_INPUT_PROFILE_REFERENCE_ROLE, @@ -98,11 +98,11 @@ vtkSlicerDynamicModelerRevolveTool::vtkSlicerDynamicModelerRevolveTool() inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsLineNode"); inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsPlaneNode"); inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsAngleNode"); - //inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); - //inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); + inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsCurveNode"); + inputMarkupClassNames->InsertNextValue("vtkMRMLMarkupsClosedCurveNode"); NodeInfo inputMarkups( - "Markups", - "Markups to specify spatial revolution axis. Normal for plane and angle, superior axis for a point. Best fitting plane normal for curve and closed curve. The direction of rotation is determined from the direction of the rotation axis by the right hand rule.", + "Revolution axis", + "Markups to specify spatial revolution axis. Normal for plane and angle. Superior axis for a point. Best fitting plane normal for curve and closed curve. The direction of rotation is determined from the direction of the rotation axis by the right hand rule.", inputMarkupClassNames, REVOLVE_INPUT_MARKUPS_REFERENCE_ROLE, /*required*/ true, @@ -412,7 +412,8 @@ bool vtkSlicerDynamicModelerRevolveTool::RunInternal(vtkMRMLDynamicModelerNode* vtkMRMLMarkupsLineNode* markupsLineNode = vtkMRMLMarkupsLineNode::SafeDownCast(markupsNode); vtkMRMLMarkupsPlaneNode* markupsPlaneNode = vtkMRMLMarkupsPlaneNode::SafeDownCast(markupsNode); vtkMRMLMarkupsAngleNode* markupsAngleNode = vtkMRMLMarkupsAngleNode::SafeDownCast(markupsNode); - + vtkMRMLMarkupsCurveNode* markupsCurveNode = vtkMRMLMarkupsCurveNode::SafeDownCast(markupsNode); + vtkMRMLMarkupsClosedCurveNode* markupsClosedCurveNode = vtkMRMLMarkupsClosedCurveNode::SafeDownCast(markupsNode); if ((markupsLineNode) && (numberOfControlPoints != 2)) { vtkNew outputPolyData; @@ -425,6 +426,13 @@ bool vtkSlicerDynamicModelerRevolveTool::RunInternal(vtkMRMLDynamicModelerNode* outputModelNode->SetAndObservePolyData(outputPolyData); return true; } + if (((markupsCurveNode) && (numberOfControlPoints < 3)) || + ((markupsClosedCurveNode) && (numberOfControlPoints < 3))) + { + vtkNew outputPolyData; + outputModelNode->SetAndObservePolyData(outputPolyData); + return true; + } // get parameters double rotationAngleDegrees = this->GetNthInputParameterValue(0, surfaceEditorNode).ToDouble(); @@ -459,6 +467,18 @@ bool vtkSlicerDynamicModelerRevolveTool::RunInternal(vtkMRMLDynamicModelerNode* markupsPlaneNode->GetNthControlPointPositionWorld(0, origin); markupsPlaneNode->GetNormalWorld(axis); } + if (markupsCurveNode || markupsClosedCurveNode) + { + // get control points in world coordinates + vtkNew controlPointsWorld; + for (int i = 0; i < numberOfControlPoints; ++i) + { + double controlPointWorld[3] = { 0, 0, 0 }; + markupsNode->GetNthControlPointPositionWorld(i, controlPointWorld); + controlPointsWorld->InsertNextPoint(controlPointWorld); + } + vtkPlane::ComputeBestFittingPlane(controlPointsWorld, origin, axis); + } if (markupsAngleNode) { double firstPoint[3] = {0.,0.,0.}; From 8ec5f2727226177120c0cc73cb583cd44ce8185c Mon Sep 17 00:00:00 2001 From: "Mauro I. Dominguez" Date: Wed, 23 Jul 2025 15:31:31 -0300 Subject: [PATCH 7/7] BUG: correct outputClass for Dynamic Modeler's extrude and revolve tools --- DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx | 4 +++- DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx index d13a149..bd65288 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerExtrudeTool.cxx @@ -119,10 +119,12 @@ vtkSlicerDynamicModelerExtrudeTool::vtkSlicerDynamicModelerExtrudeTool() ///////// // Outputs + vtkNew outputModelClassNames; + outputModelClassNames->InsertNextValue("vtkMRMLModelNode"); NodeInfo outputModel( "Extruded model", "Result of the extrusion operation.", - inputModelClassNames, + outputModelClassNames, EXTRUDE_OUTPUT_MODEL_REFERENCE_ROLE, false, false diff --git a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx index 66fe690..664ee5d 100644 --- a/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx +++ b/DynamicModeler/Logic/vtkSlicerDynamicModelerRevolveTool.cxx @@ -113,10 +113,12 @@ vtkSlicerDynamicModelerRevolveTool::vtkSlicerDynamicModelerRevolveTool() ///////// // Outputs + vtkNew outputModelClassNames; + outputModelClassNames->InsertNextValue("vtkMRMLModelNode"); NodeInfo outputModel( "Revolved model", "Result of the revolving operation.", - inputModelClassNames, + outputModelClassNames, REVOLVE_OUTPUT_MODEL_REFERENCE_ROLE, false, false