diff --git a/CMakeExternals/PatchACVD.cmake b/CMakeExternals/PatchACVD.cmake index 06239e30de..aaa47c6a8e 100644 --- a/CMakeExternals/PatchACVD.cmake +++ b/CMakeExternals/PatchACVD.cmake @@ -1,37 +1,47 @@ +# Create Config.cmake file to make ACVD findable through config mode of find_package() + file(WRITE "ACVDConfig.cmake.in" "set(ACVD_INCLUDE_DIRS \"@VTKSURFACE_INCLUDE_DIR@;@VTKDISCRETEREMESHING_INCLUDE_DIR@;@VTKVOLUMEPROCESSING_INCLUDE_DIR@\") set(ACVD_LIBRARY_DIRS \"@VTKSURFACE_BINARY_DIR@/bin\") set(ACVD_LIBRARIES vtkSurface vtkDiscreteRemeshing vtkVolumeProcessing) add_definitions(-DDOmultithread)") file(APPEND "CMakeLists.txt" "CONFIGURE_FILE(ACVDConfig.cmake.in ACVDConfig.cmake @ONLY)") +# Replace VTK_COMMON_EXPORT by VTK_EXPORT in class declarations + set(path "Common/vtkMyMinimalStandardRandomSequence.h") file(STRINGS ${path} contents NEWLINE_CONSUME) string(REPLACE "COMMON_" "" contents ${contents}) set(CONTENTS ${contents}) configure_file(${TEMPLATE_FILE} ${path} @ONLY) set(path "DiscreteRemeshing/vtkThreadedClustering.h") file(STRINGS ${path} contents NEWLINE_CONSUME) string(REPLACE "COMMON_" "" contents ${contents}) set(CONTENTS ${contents}) configure_file(${TEMPLATE_FILE} ${path} @ONLY) -set(path "DiscreteRemeshing/vtkVerticesProcessing.h") +# Add missing VTK_EXPORT to class declaration + +set(path "VolumeProcessing/vtkImageDataCleanLabels.h") file(STRINGS ${path} contents NEWLINE_CONSUME) -string(REPLACE "int N" "vtkIdType N" contents ${contents}) +string(REPLACE "ss" "ss VTK_EXPORT" contents ${contents}) set(CONTENTS ${contents}) configure_file(${TEMPLATE_FILE} ${path} @ONLY) -set(path "VolumeProcessing/vtkImageDataCleanLabels.h") +# Replace int by vtkIdType, which are types of different size (x64) + +set(path "DiscreteRemeshing/vtkVerticesProcessing.h") file(STRINGS ${path} contents NEWLINE_CONSUME) -string(REPLACE "ss" "ss VTK_EXPORT" contents ${contents}) +string(REPLACE "int N" "vtkIdType N" contents ${contents}) set(CONTENTS ${contents}) configure_file(${TEMPLATE_FILE} ${path} @ONLY) +# Link to POSIX thread library + set(path "DiscreteRemeshing/CMakeLists.txt") file(STRINGS ${path} contents NEWLINE_CONSUME) string(REPLACE "TARGET_LINK_LIBRARIES(v" "IF(UNIX AND NOT APPLE)\n LIST(APPEND LIB_ADDED pthread)\nENDIF(UNIX AND NOT APPLE)\n\nTARGET_LINK_LIBRARIES(v" contents ${contents}) set(CONTENTS ${contents}) configure_file(${TEMPLATE_FILE} ${path} @ONLY) diff --git a/Documentation/Doxygen/UserManual/MITKPluginManualsList.dox b/Documentation/Doxygen/UserManual/MITKPluginManualsList.dox index 89b0531026..d715d1a7ca 100644 --- a/Documentation/Doxygen/UserManual/MITKPluginManualsList.dox +++ b/Documentation/Doxygen/UserManual/MITKPluginManualsList.dox @@ -1,43 +1,44 @@ /** \page PluginListPage MITK Plugin Manuals \section PluginListPageOverview Overview The plugins and bundles provide much of the extended functionality of MITK. Each encapsulates a solution to a problem and associated features. This way one can easily assemble the necessary capabilites for a workflow without adding a lot of bloat, by combining plugins as needed. The distinction between developer and end user use is for convenience only and mainly distinguishes which group a plugin is primarily aimed at. \section PluginListPageEndUserPluginList List of Plugins for End User Use \li \subpage org_blueberry_ui_qt_log \li \subpage org_mitk_views_basicimageprocessing \li \subpage org_mitk_views_cmdlinemodules \li \subpage org_mitk_views_datamanager \li \subpage org_mitk_gui_qt_dicom \li \subpage org_mitk_gui_qt_diffusionimaging \li \subpage org_mitk_views_imagecropper \li \subpage org_mitk_views_imagenavigator \li \subpage org_mitk_gui_qt_measurementtoolbox \li \subpage org_mitk_views_meshdecimation \li \subpage org_mitk_views_moviemaker \li \subpage org_mitk_views_pointsetinteraction \li \subpage org_mitk_gui_qt_python \li \subpage org_mitk_gui_qt_registration + \li \subpage org_mitk_gui_qt_remeshing \li \subpage org_mitk_views_segmentation \li \subpage org_mitk_gui_qt_simulation \li \subpage org_mitk_gui_qt_ultrasound \li \subpage org_mitk_views_volumevisualization \section PluginListPageDevPluginList List of Plugins for Developer Use and Examples \li \subpage org_surfacematerialeditor \li \subpage org_toftutorial \li \subpage org_mitk_gui_qt_examples \li \subpage org_mitkexamplesopencv \li \ref org_mitk_gui_qt_igtexample \li \ref org_mitk_gui_qt_igttracking \li \subpage org_blueberry_ui_qt_objectinspector \li \subpage org_mitk_gui_InteractionTests */ diff --git a/Modules/Remeshing/Documentation/doxygen/Manual.dox b/Modules/Remeshing/Documentation/doxygen/Manual.dox deleted file mode 100644 index 2bbd1afd15..0000000000 --- a/Modules/Remeshing/Documentation/doxygen/Manual.dox +++ /dev/null @@ -1,3 +0,0 @@ -/** -\page RemeshingManualPage The MITK Remeshing Module -*/ diff --git a/Modules/Remeshing/mitkACVD.cpp b/Modules/Remeshing/mitkACVD.cpp index e43a044d4b..3509695d18 100644 --- a/Modules/Remeshing/mitkACVD.cpp +++ b/Modules/Remeshing/mitkACVD.cpp @@ -1,161 +1,196 @@ /*=================================================================== 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 "mitkACVD.h" #include #include #include #include #include #include #include #include struct ClustersQuadrics { explicit ClustersQuadrics(int size) : Elements(new double*[size]), Size(size) { for (int i = 0; i < size; ++i) { Elements[i] = new double[9]; for (int j = 0; j < 9; ++j) Elements[i][j] = 0.0; } } ~ClustersQuadrics() { for (int i = 0; i < Size; ++i) delete[] Elements[i]; delete Elements; } double** Elements; int Size; private: ClustersQuadrics(const ClustersQuadrics&); ClustersQuadrics& operator=(const ClustersQuadrics&); }; -mitk::Surface::Pointer mitk::ACVD::Remesh(mitk::Surface::Pointer surface, int numVertices, double gradation, int subsampling, double edgeSplitting, int optimizationLevel, bool forceManifold, bool boundaryFixing) +static bool ValidateSurface(mitk::Surface::Pointer surface, unsigned int t) { + if (surface.IsNull()) + { + MITK_ERROR << "Input surface is NULL!"; + return false; + } + + if (t >= surface->GetSizeOfPolyDataSeries()) + { + MITK_ERROR << "Input surface doesn't have data at time step " << t << "!"; + return false; + } + + vtkPolyData* polyData = surface->GetVtkPolyData(t); + + if (polyData == NULL) + { + MITK_ERROR << "PolyData of input surface at time step " << t << " is NULL!"; + return false; + } + + if (polyData->GetNumberOfPolys() == 0) + { + MITK_ERROR << "Input surface has no polygons at time step " << t << "!"; + return false; + } + + return true; +} + +mitk::Surface::Pointer mitk::ACVD::Remesh(mitk::Surface::Pointer surface, unsigned int t, int numVertices, double gradation, int subsampling, double edgeSplitting, int optimizationLevel, bool forceManifold, bool boundaryFixing) +{ + if (!ValidateSurface(surface, t)) + return NULL; + MITK_INFO << "Start remeshing..."; vtkSmartPointer surfacePolyData = vtkSmartPointer::New(); - surfacePolyData->DeepCopy(surface->GetVtkPolyData()); + surfacePolyData->DeepCopy(surface->GetVtkPolyData(t)); vtkSmartPointer mesh = vtkSmartPointer::New(); mesh->CreateFromPolyData(surfacePolyData); mesh->GetCellData()->Initialize(); mesh->GetPointData()->Initialize(); mesh->DisplayMeshProperties(); if (edgeSplitting != 0.0) mesh->SplitLongEdges(edgeSplitting); vtkSmartPointer remesher = vtkSmartPointer::New(); remesher->GetMetric()->SetGradation(gradation); remesher->SetBoundaryFixing(boundaryFixing); remesher->SetConsoleOutput(1); remesher->SetForceManifold(forceManifold); remesher->SetInput(mesh); remesher->SetNumberOfClusters(numVertices); remesher->SetNumberOfThreads(vtkMultiThreader::GetGlobalDefaultNumberOfThreads()); remesher->SetSubsamplingThreshold(subsampling); remesher->Remesh(); + // Optimization: Minimize distance between input surface and remeshed surface if (optimizationLevel != 0) { ClustersQuadrics clustersQuadrics(numVertices); vtkSmartPointer faceList = vtkSmartPointer::New(); vtkSmartPointer clustering = remesher->GetClustering(); vtkSmartPointer remesherInput = remesher->GetInput(); int clusteringType = remesher->GetClusteringType(); int numItems = remesher->GetNumberOfItems(); int numMisclassifiedItems = 0; for (int i = 0; i < numItems; ++i) { int cluster = clustering->GetValue(i); if (cluster >= 0 && cluster < numVertices) { if (clusteringType != 0) { remesherInput->GetVertexNeighbourFaces(i, faceList); int numIds = static_cast(faceList->GetNumberOfIds()); for (int j = 0; j < numIds; ++j) vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster], remesherInput, faceList->GetId(j), false); } else { vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster], remesherInput, i, false); } } else { ++numMisclassifiedItems; } } if (numMisclassifiedItems != 0) std::cout << numMisclassifiedItems << " items with wrong cluster association" << std::endl; vtkSmartPointer remesherOutput = remesher->GetOutput(); double point[3]; for (int i = 0; i < numVertices; ++i) { remesherOutput->GetPoint(i, point); vtkQuadricTools::ComputeRepresentativePoint(clustersQuadrics.Elements[i], point, optimizationLevel); remesherOutput->SetPointCoordinates(i, point); } std::cout << "After quadrics post-processing:" << std::endl; remesherOutput->DisplayMeshProperties(); } vtkSmartPointer normals = vtkSmartPointer::New(); normals->SetInput(remesher->GetOutput()); normals->AutoOrientNormalsOn(); normals->ComputeCellNormalsOff(); normals->ComputePointNormalsOn(); normals->ConsistencyOff(); normals->FlipNormalsOff(); normals->NonManifoldTraversalOff(); normals->SplittingOff(); normals->Update(); Surface::Pointer remeshedSurface = Surface::New(); remeshedSurface->SetVtkPolyData(normals->GetOutput()); MITK_INFO << "Finished remeshing"; return remeshedSurface; } diff --git a/Modules/Remeshing/mitkACVD.h b/Modules/Remeshing/mitkACVD.h index cdfc441678..99b8cbc2c3 100644 --- a/Modules/Remeshing/mitkACVD.h +++ b/Modules/Remeshing/mitkACVD.h @@ -1,31 +1,55 @@ /*=================================================================== 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. ===================================================================*/ #ifndef mitkACVD_h #define mitkACVD_h #include #include namespace mitk { namespace ACVD { - Remeshing_EXPORT Surface::Pointer Remesh(Surface::Pointer surface, int numVertices, double gradation, int subsampling = 10, double edgeSplitting = 0.0, int optimizationLevel = 1, bool forceManifold = false, bool boundaryFixing = false); + /** \brief Remesh a surface and store the result in a new surface. + * + * The %ACVD library is used for remeshing which is based on the paper "Approximated Centroidal Voronoi Diagrams for Uniform Polygonal Mesh Coarsening" by S. Valette, and J. M. Chassery. + * There are a few rules of thumbs regarding the ranges of parameters to gain high quality remeshed surfaces: + * + *
    + *
  • numVertices is exact, however, if boundaryFixing is enabled, additional vertices are generated at boundaries + *
  • %Set gradation to zero in case you want polygons of roughly the same size all over the remeshed surface; start with 1 otherwise + *
  • subsampling has direct influence on the quality of the remeshed surface (higher values take more time) + *
  • edgeSplitting is useful for surfaces that contain long and thin triangles but takes a long time + *
  • Leave optimizationLevel set to 1 as greater values result in degenerated polygons + *
  • Irregular shrinking of boundaries during remeshing can be avoided by boundaryFixing, however this results in additional, lower quality polygons at boundaries + *
+ * + * \param[in] surface Input surface. + * \param[in] t Time step of a four-dimensional input surface, zero otherwise. + * \param[in] numVertices Desired number of vertices in the remeshed surface. + * \param[in] gradation Influence of surface curvature on polygon size. + * \param[in] subsampling Subsample input surface until number of vertices exceeds initial count times this parameter. + * \param[in] edgeSplitting Recursively split edges that are longer than the average edge length times this parameter. + * \param[in] optimizationLevel Minimize distance between input surface and remeshed surface. + * \param[in] boundaryFixing Keep original surface boundaries by adding additional polygons. + * \return Returns the remeshed surface or NULL if input surface is invalid. + */ + Remeshing_EXPORT Surface::Pointer Remesh(Surface::Pointer surface, unsigned int t, int numVertices, double gradation, int subsampling = 10, double edgeSplitting = 0.0, int optimizationLevel = 1, bool forceManifold = false, bool boundaryFixing = false); } } #endif diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder.png new file mode 100644 index 0000000000..db87d5e438 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder_bad.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder_bad.png new file mode 100644 index 0000000000..b598444ee3 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder_bad.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder_good.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder_good.png new file mode 100644 index 0000000000..cb04f3cae9 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/cylinder_good.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/gradation_10percent.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/gradation_10percent.png new file mode 100644 index 0000000000..f20115d7fc Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/gradation_10percent.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/noBoundaryFixing_10percent.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/noBoundaryFixing_10percent.png new file mode 100644 index 0000000000..f6a07ae909 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/noBoundaryFixing_10percent.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/org_mitk_gui_qt_remeshing.dox b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/org_mitk_gui_qt_remeshing.dox index bdee5cb3e7..d3c4348a4c 100644 --- a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/org_mitk_gui_qt_remeshing.dox +++ b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/org_mitk_gui_qt_remeshing.dox @@ -1,5 +1,71 @@ /** \page org_mitk_gui_qt_remeshing The Remeshing Plugin \image html org_mitk_gui_qt_remeshing.png "Icon of the Remeshing Plugin." + +\tableofcontents + +\section org_mitk_gui_qt_remeshingOverview Overview + +The Remeshing View allows you to remesh surfaces. +If done right, remeshing can dramatically increase the quality of your surface mesh. +However, you might lose precision if you reduce your surface mesh too strong. +Even when you keep the detail of your mesh there might be a tiny distance between your original surface and the remeshed surface. +Hence, be careful when using remeshed surfaces for evaluation purposes and always keep the original versions. + +\section org_mitk_gui_qt_remeshingUsage Usage + +\image html remeshingView.png "Basic and advanced mode of the Remeshing View." + +There are two basic and about a handful of advanced settings that influence remeshing. +Most of the time you should be able to gain satisfying results by adjusting only the two basic settings or even without changing any of the default parameters. +In the following the effects of all settings are described in more detail. Image examples are based on the following surface: + +\image html originalMesh.png "The surface from which all examples below originate from." + +\subsection org_mitk_gui_qt_remeshingBasicSettings Basic Settings + +The Vertices setting is the number of vertices the remeshed surface will consist of. +This is exact as long as Boundary fixing is turned off (default). +The maximum number of vertices is limited to the number of vertices of the input surface, however, you can increase this limit by adjusting the Max. # of vertices setting. + +The Gradation setting controls the distribution of vertices in the remeshed surface. +If set to zero the vertices are distributed equally all over the remeshed surface. +You can push more vertices towards surface regions with high curvature, i.e., more detailed regions, by increasing this setting. + +\image html gradation_10percent.png "Vertex count reduced to 10 percent, gradation 0 vs. 1." + +\subsection org_mitk_gui_qt_remeshingAdvancedSettings Advanced Settings + +You can arbirarily increase the maximum adjustable number of vertices by changing the Max. # of vertices setting. + +Edge splitting is disabled by default and might take a long time during remeshing when enabled. +This setting represents a number by which the average edge length of the input surface is multiplied to serve as a threshold which regulates edge splitting. +Long edges are split recursively until all edges satisfy the threshold. +Edge splitting is useful for surfaces that contain thin and long polygons. + +\image html cylinder.png "A surface that contains extremely long polygons." + +
+ +\image html cylinder_bad.png "A remeshing attempt without edge splitting." + +
+ +\image html cylinder_good.png "Increased max. # of vertices, enabled edge splitting, followed by a second remeshing run without edge splitting." + +The Subsampling setting has direct impact on the quality of the remeshed surface. +The input surface is recursively subdivided until the total number of vertices exceeds its initial vertex count times this setting. + +\image html subsampling_20percent.png "Vertex count reduced to 20 percent, subsampling 10 vs. 500." + +You usually leave the Optimization level set to its default value 1. +When disabled, the remeshed surface has usually a slightly smaller volume than the original surface. +The optimization process minimizes the distance between the two surfaces but values higher than 1 introduce degenerated triangles to the remeshed surface. + +If your surface is open, i.e., it has holes in it, boundaries tend to shrink irregularly during remeshing. +If the position and smoothness of your surface boundaries are important, you should activate the Boundary fixing setting. +This results in additional vertices that make up extra polygons at the remeshed boundaries to keep the original boundaries. + +\image html noBoundaryFixing_10percent.png "Vertex count reduced to 10 percent, no boundary fixing." */ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/originalMesh.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/originalMesh.png new file mode 100644 index 0000000000..375cc307bd Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/originalMesh.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/remeshingView.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/remeshingView.png new file mode 100644 index 0000000000..e202cf6a96 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/remeshingView.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/subsampling_20percent.png b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/subsampling_20percent.png new file mode 100644 index 0000000000..2b7d96dd37 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/subsampling_20percent.png differ diff --git a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp index d94ebf404b..ac5b17ecfa 100644 --- a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp +++ b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp @@ -1,230 +1,232 @@ /*=================================================================== 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 "QmitkRemeshingView.h" // MITK #include #include #include #include #include #include #include // Qt #include // VTK #include #include // C++ Standard Library #include #include QmitkRemeshingView::QmitkRemeshingView() { } QmitkRemeshingView::~QmitkRemeshingView() { } void QmitkRemeshingView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.surfaceComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.surfaceComboBox->SetPredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); QIntValidator* posIntValidator = new QIntValidator(0, std::numeric_limits::max(), parent); m_Controls.maxNumVerticesLineEdit->setValidator(posIntValidator); this->EnableWidgets(m_Controls.surfaceComboBox->GetSelectedNode().IsNotNull()); this->OnAdvancedSettingsButtonToggled(false); connect(m_Controls.surfaceComboBox, SIGNAL(OnSelectionChanged(const mitk::DataNode *)), this, SLOT(OnSelectedSurfaceChanged(const mitk::DataNode *))); connect(m_Controls.numVerticesSlider, SIGNAL(valueChanged(int)), this, SLOT(OnNumberOfVerticesChanged(int))); connect(m_Controls.numVerticesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnNumberOfVerticesChanged(int))); connect(m_Controls.gradationSlider, SIGNAL(valueChanged(double)), this, SLOT(OnGradationChanged(double))); connect(m_Controls.gradationSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnGradationChanged(double))); connect(m_Controls.advancedSettingsButton, SIGNAL(toggled(bool)), this, SLOT(OnAdvancedSettingsButtonToggled(bool))); connect(m_Controls.maxNumVerticesLineEdit, SIGNAL(editingFinished()), this, SLOT(OnMaxNumVerticesLineEditEditingFinished())); connect(m_Controls.edgeSplittingSlider, SIGNAL(valueChanged(double)), this, SLOT(OnEdgeSplittingChanged(double))); connect(m_Controls.edgeSplittingSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnEdgeSplittingChanged(double))); connect(m_Controls.subsamplingSlider, SIGNAL(valueChanged(int)), this, SLOT(OnSubsamplingChanged(int))); connect(m_Controls.subsamplingSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSubsamplingChanged(int))); connect(m_Controls.optimizationLevelSlider, SIGNAL(valueChanged(int)), this, SLOT(OnOptimizationLevelChanged(int))); connect(m_Controls.optimizationLevelSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnOptimizationLevelChanged(int))); connect(m_Controls.remeshPushButton, SIGNAL(clicked()), this, SLOT(OnRemeshButtonClicked())); + + this->OnSelectedSurfaceChanged(m_Controls.surfaceComboBox->GetSelectedNode()); } void QmitkRemeshingView::EnableWidgets(bool enable) { m_Controls.surfaceComboBox->setEnabled(enable); m_Controls.numVerticesSlider->setEnabled(enable); m_Controls.numVerticesSpinBox->setEnabled(enable); m_Controls.gradationSlider->setEnabled(enable); m_Controls.gradationSpinBox->setEnabled(enable); m_Controls.maxNumVerticesLineEdit->setEnabled(enable); m_Controls.edgeSplittingSlider->setEnabled(enable); m_Controls.edgeSplittingSpinBox->setEnabled(enable); m_Controls.subsamplingSlider->setEnabled(enable); m_Controls.subsamplingSpinBox->setEnabled(enable); m_Controls.optimizationLevelSlider->setEnabled(enable); m_Controls.optimizationLevelSpinBox->setEnabled(enable); m_Controls.forceManifoldCheckBox->setEnabled(enable); m_Controls.boundaryFixingCheckBox->setEnabled(enable); m_Controls.remeshPushButton->setEnabled(enable); } void QmitkRemeshingView::OnAdvancedSettingsButtonToggled(bool toggled) { m_Controls.maxNumVerticesLabel->setVisible(toggled); m_Controls.maxNumVerticesLineEdit->setVisible(toggled); m_Controls.edgeSplittingLabel->setVisible(toggled); m_Controls.edgeSplittingSlider->setVisible(toggled); m_Controls.edgeSplittingSpinBox->setVisible(toggled); m_Controls.subsamplingLabel->setVisible(toggled); m_Controls.subsamplingSlider->setVisible(toggled); m_Controls.subsamplingSpinBox->setVisible(toggled); m_Controls.optimizationLevelLabel->setVisible(toggled); m_Controls.optimizationLevelSlider->setVisible(toggled); m_Controls.optimizationLevelSpinBox->setVisible(toggled); m_Controls.forceManifoldCheckBox->setVisible(toggled); m_Controls.boundaryFixingCheckBox->setVisible(toggled); } void QmitkRemeshingView::OnEdgeSplittingChanged(double edgeSplitting) { if (edgeSplitting != m_Controls.edgeSplittingSlider->value()) m_Controls.edgeSplittingSlider->setValue(edgeSplitting); if (edgeSplitting != m_Controls.edgeSplittingSpinBox->value()) m_Controls.edgeSplittingSpinBox->setValue(edgeSplitting); } void QmitkRemeshingView::OnGradationChanged(double gradation) { if (gradation != m_Controls.gradationSlider->value()) m_Controls.gradationSlider->setValue(gradation); if (gradation != m_Controls.gradationSpinBox->value()) m_Controls.gradationSpinBox->setValue(gradation); } void QmitkRemeshingView::OnMaxNumVerticesLineEditEditingFinished() { int maximum = m_Controls.maxNumVerticesLineEdit->text().toInt(); if (m_Controls.numVerticesSpinBox->maximum() != maximum) { m_Controls.numVerticesSlider->setMaximum(maximum); m_Controls.numVerticesSpinBox->setMaximum(maximum); } } void QmitkRemeshingView::OnNumberOfVerticesChanged(int numVertices) { if (numVertices != m_Controls.numVerticesSlider->value()) m_Controls.numVerticesSlider->setValue(numVertices); if (numVertices != m_Controls.numVerticesSpinBox->value()) m_Controls.numVerticesSpinBox->setValue(numVertices); } void QmitkRemeshingView::OnOptimizationLevelChanged(int optimizationLevel) { if (optimizationLevel != m_Controls.optimizationLevelSlider->value()) m_Controls.optimizationLevelSlider->setValue(optimizationLevel); if (optimizationLevel != m_Controls.optimizationLevelSpinBox->value()) m_Controls.optimizationLevelSpinBox->setValue(optimizationLevel); } void QmitkRemeshingView::OnRemeshButtonClicked() { mitk::DataNode::Pointer selectedNode = m_Controls.surfaceComboBox->GetSelectedNode(); mitk::Surface::Pointer surface = static_cast(selectedNode->GetData()); int numVertices = m_Controls.numVerticesSpinBox->value(); double gradation = m_Controls.gradationSpinBox->value(); int subsampling = m_Controls.subsamplingSpinBox->value(); double edgeSplitting = m_Controls.edgeSplittingSpinBox->value(); int optimizationLevel = m_Controls.optimizationLevelSpinBox->value(); bool forceManifold = m_Controls.forceManifoldCheckBox->isChecked(); bool boundaryFixing = m_Controls.boundaryFixingCheckBox->isChecked(); - mitk::Surface::Pointer remeshedSurface = mitk::ACVD::Remesh(surface, numVertices, gradation, subsampling, edgeSplitting, optimizationLevel, forceManifold, boundaryFixing); + mitk::Surface::Pointer remeshedSurface = mitk::ACVD::Remesh(surface, 0, numVertices, gradation, subsampling, edgeSplitting, optimizationLevel, forceManifold, boundaryFixing); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(QString("%1 (%2, %3)").arg(selectedNode->GetName().c_str()).arg(remeshedSurface->GetVtkPolyData()->GetNumberOfPoints()).arg(gradation).toStdString()); newNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); newNode->SetProperty("material.specularCoefficient", mitk::FloatProperty::New(0.0f)); newNode->SetData(remeshedSurface); this->GetDataStorage()->Add(newNode, selectedNode); } void QmitkRemeshingView::OnSelectedSurfaceChanged(const mitk::DataNode *node) { if (node != NULL) { int numVertices = static_cast(static_cast(node->GetData())->GetVtkPolyData()->GetNumberOfPoints()); int minimum = numVertices < 100 ? numVertices : 100; int maximum = numVertices == minimum ? numVertices * 10 : numVertices; int step = std::max(1, maximum / 10); this->SetNumberOfVertices(minimum, maximum, step, numVertices); this->EnableWidgets(true); } else { this->EnableWidgets(false); this->SetNumberOfVertices(0, 0, 0, 0); } } void QmitkRemeshingView::OnSubsamplingChanged(int subsampling) { if (subsampling != m_Controls.subsamplingSlider->value()) m_Controls.subsamplingSlider->setValue(subsampling); if (subsampling != m_Controls.subsamplingSpinBox->value()) m_Controls.subsamplingSpinBox->setValue(subsampling); } void QmitkRemeshingView::SetFocus() { m_Controls.surfaceComboBox->setFocus(); } void QmitkRemeshingView::SetNumberOfVertices(int minimum, int maximum, int step, int value) { m_Controls.numVerticesSlider->setMinimum(minimum); m_Controls.numVerticesSlider->setMaximum(maximum); m_Controls.numVerticesSlider->setSingleStep(1); m_Controls.numVerticesSlider->setPageStep(step); m_Controls.numVerticesSpinBox->setMinimum(minimum); m_Controls.numVerticesSpinBox->setMaximum(maximum); m_Controls.numVerticesSpinBox->setSingleStep(step); m_Controls.numVerticesSpinBox->setValue(value); m_Controls.maxNumVerticesLineEdit->setText(QString("%1").arg(maximum)); } 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 d2a5a4846f..2b4640f14f 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,108 +1,103 @@ /** \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 +\tableofcontents \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 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 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 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 */