diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp index dca9656896..989d3d2d40 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp @@ -1,276 +1,276 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSimpleExampleView.h" #include #include #include #include #include #include "QmitkRenderWindow.h" #include "QmitkStepperAdapter.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include #include #include const std::string QmitkSimpleExampleView::VIEW_ID = "org.mitk.views.simpleexample"; QmitkSimpleExampleView::QmitkSimpleExampleView() : m_Controls(nullptr), m_NavigatorsInitialized(false), m_Parent(nullptr) { } QmitkSimpleExampleView::~QmitkSimpleExampleView() { } void QmitkSimpleExampleView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { m_Parent = parent; // create GUI widgets m_Controls = new Ui::QmitkSimpleExampleViewControls; m_Controls->setupUi(parent); this->CreateConnections(); this->RenderWindowPartActivated(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)); } } void QmitkSimpleExampleView::SetFocus() { m_Controls->renderWindowComboBox->setFocus(); } void QmitkSimpleExampleView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { if (renderWindowPart == nullptr) { m_Parent->setEnabled(false); return; } QHashIterator renderIter(renderWindowPart->GetQmitkRenderWindows()); while (renderIter.hasNext()) { renderIter.next(); m_Controls->renderWindowComboBox->addItem(renderIter.key()); } RenderWindowSelected(m_Controls->renderWindowComboBox->currentText()); m_TimeStepper.reset(new QmitkStepperAdapter(m_Controls->timeSliceNavigationWidget, - renderWindowPart->GetTimeNavigationController()->GetTime())); + renderWindowPart->GetTimeNavigationController()->GetStepper())); m_MovieStepper.reset(new QmitkStepperAdapter(m_Controls->movieNavigatorTime, - renderWindowPart->GetTimeNavigationController()->GetTime())); + renderWindowPart->GetTimeNavigationController()->GetStepper())); m_Parent->setEnabled(true); } void QmitkSimpleExampleView::RenderWindowPartDeactivated(mitk::IRenderWindowPart * /*renderWindowPart*/) { m_Parent->setEnabled(false); m_SliceStepper.reset(); m_TimeStepper.reset(); m_MovieStepper.reset(); m_Controls->renderWindowComboBox->clear(); } void QmitkSimpleExampleView::CreateConnections() { if (m_Controls) { connect(m_Controls->renderWindowComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(RenderWindowSelected(QString))); connect(m_Controls->stereoSelect, SIGNAL(activated(int)), this, SLOT(StereoSelectionChanged(int))); connect(m_Controls->reInitializeNavigatorsButton, SIGNAL(clicked()), this, SLOT(InitNavigators())); connect(m_Controls->m_TakeScreenshotBtn, SIGNAL(clicked()), this, SLOT(OnTakeScreenshot())); connect(m_Controls->m_TakeHighResScreenShotBtn, SIGNAL(clicked()), this, SLOT(OnTakeHighResolutionScreenshot())); } } void QmitkSimpleExampleView::InitNavigators() { /* get all nodes that have not set "includeInBoundingBox" to false */ mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDataStorage()->GetSubset(pred); /* calculate bounding geometry of these nodes */ auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(rs); /* initialize the views to the bounding geometry */ m_NavigatorsInitialized = mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } void QmitkSimpleExampleView::StereoSelectionChanged(int id) { /* From vtkRenderWindow.h tells us about stereo rendering: Set/Get what type of stereo rendering to use. CrystalEyes mode uses frame-sequential capabilities available in OpenGL to drive LCD shutter glasses and stereo projectors. RedBlue mode is a simple type of stereo for use with red-blue glasses. Anaglyph mode is a superset of RedBlue mode, but the color output channels can be configured using the AnaglyphColorMask and the color of the original image can be (somewhat maintained using AnaglyphColorSaturation; the default colors for Anaglyph mode is red-cyan. Interlaced stereo mode produces a composite image where horizontal lines alternate between left and right views. StereoLeft and StereoRight modes choose one or the other stereo view. Dresden mode is yet another stereoscopic interleaving. */ auto *renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); vtkRenderWindow *vtkrenderwindow = renderWindowPart->GetQmitkRenderWindow("3d")->GetVtkRenderWindow(); // note: foreground vtkRenderers (at least the department logo renderer) produce errors in stereoscopic visualization. // Therefore, we disable the logo visualization during stereo rendering. switch (id) { case 0: vtkrenderwindow->StereoRenderOff(); break; case 1: vtkrenderwindow->SetStereoTypeToRedBlue(); vtkrenderwindow->StereoRenderOn(); renderWindowPart->EnableDecorations(false, QStringList(mitk::IRenderWindowPart::DECORATION_LOGO)); break; case 2: vtkrenderwindow->SetStereoTypeToDresden(); vtkrenderwindow->StereoRenderOn(); renderWindowPart->EnableDecorations(false, QStringList(mitk::IRenderWindowPart::DECORATION_LOGO)); break; } mitk::BaseRenderer::GetInstance(vtkrenderwindow)->SetMapperID(mitk::BaseRenderer::Standard3D); renderWindowPart->RequestUpdate(); } QmitkRenderWindow *QmitkSimpleExampleView::GetSelectedRenderWindow() const { QString id = m_Controls->renderWindowComboBox->currentText(); if (id.isEmpty()) { return nullptr; } else { return this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id); } } void QmitkSimpleExampleView::OnTakeHighResolutionScreenshot() { QString filter; QString fileName = QFileDialog::getSaveFileName( nullptr, "Save screenshot to...", QDir::currentPath(), m_PNGExtension + ";;" + m_JPGExtension, &filter); vtkRenderer *renderer = this->GetSelectedRenderWindow()->GetRenderer()->GetVtkRenderer(); if (renderer == nullptr) return; this->TakeScreenshot(renderer, 4, fileName, filter); } void QmitkSimpleExampleView::OnTakeScreenshot() { QString filter; QString fileName = QFileDialog::getSaveFileName( nullptr, "Save screenshot to...", QDir::currentPath(), m_PNGExtension + ";;" + m_JPGExtension, &filter); QmitkRenderWindow *renWin = this->GetSelectedRenderWindow(); if (renWin == nullptr) return; vtkRenderer *renderer = renWin->GetRenderer()->GetVtkRenderer(); if (renderer == nullptr) return; this->TakeScreenshot(renderer, 1, fileName, filter); } void QmitkSimpleExampleView::TakeScreenshot(vtkRenderer *renderer, unsigned int magnificationFactor, QString fileName, QString filter) { if ((renderer == nullptr) || (magnificationFactor < 1) || fileName.isEmpty()) return; bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer()); renderer->GetRenderWindow()->DoubleBufferOff(); vtkImageWriter *fileWriter = nullptr; QFileInfo fi(fileName); QString suffix = fi.suffix().toLower(); if (suffix.isEmpty() || (suffix != "png" && suffix != "jpg" && suffix != "jpeg")) { if (filter == m_PNGExtension) { suffix = "png"; } else if (filter == m_JPGExtension) { suffix = "jpg"; } fileName += "." + suffix; } if (suffix.compare("jpg", Qt::CaseInsensitive) == 0 || suffix.compare("jpeg", Qt::CaseInsensitive) == 0) { vtkJPEGWriter *w = vtkJPEGWriter::New(); w->SetQuality(100); w->ProgressiveOff(); fileWriter = w; } else // default is png { fileWriter = vtkPNGWriter::New(); } vtkRenderLargeImage *magnifier = vtkRenderLargeImage::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(magnificationFactor); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.toLatin1()); // vtkRenderLargeImage has problems with different layers, therefore we have to // temporarily deactivate all other layers. // we set the background to white, because it is nicer than black... double oldBackground[3]; renderer->GetBackground(oldBackground); double white[] = {1.0, 1.0, 1.0}; renderer->SetBackground(white); mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); renderWindowPart->EnableDecorations(false); fileWriter->Write(); fileWriter->Delete(); renderWindowPart->EnableDecorations(true); renderer->SetBackground(oldBackground); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void QmitkSimpleExampleView::RenderWindowSelected(const QString &id) { if (!id.isEmpty()) { m_SliceStepper.reset(new QmitkStepperAdapter(m_Controls->sliceNavigationWidget, - this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id)->GetSliceNavigationController()->GetSlice())); + this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id)->GetSliceNavigationController()->GetStepper())); } } diff --git a/Examples/Tutorial/Step1/Step1.cpp b/Examples/Tutorial/Step1/Step1.cpp index c203119b7f..3ac735e7a8 100644 --- a/Examples/Tutorial/Step1/Step1.cpp +++ b/Examples/Tutorial/Step1/Step1.cpp @@ -1,85 +1,85 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include #include #include #include //##Documentation //## @brief Load image (nrrd format) and display it in a 2D view int main(int argc, char *argv[]) { QApplication qtapplication(argc, argv); if (argc < 2) { fprintf(stderr, "Usage: %s [filename] \n\n", itksys::SystemTools::GetFilenameName(argv[0]).c_str()); return 1; } // Register Qmitk-dependent global instances QmitkRegisterClasses(); //************************************************************************* // Part I: Basic initialization //************************************************************************* // Create a DataStorage // The DataStorage manages all data objects. It is used by the // rendering mechanism to render all data objects // We use the standard implementation mitk::StandaloneDataStorage. mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading a file //************************************************************************* // Load datanode (eg. many image formats, surface formats, etc.) mitk::IOUtil::Load(argv[1], *ds); //************************************************************************* // Part IV: Create window and pass the datastorage to it //************************************************************************* // Create a RenderWindow QmitkRenderWindow renderWindow; // Tell the RenderWindow which (part of) the datastorage to render renderWindow.GetRenderer()->SetDataStorage(ds); // Initialize the RenderWindow auto geo = ds->ComputeBoundingGeometry3D(ds->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); // mitk::RenderingManager::GetInstance()->InitializeViews(); // Select a slice mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); if (sliceNaviController) - sliceNaviController->GetSlice()->SetPos(0); + sliceNaviController->GetStepper()->SetPos(0); //************************************************************************* // Part V: Qt-specific initialization //************************************************************************* renderWindow.show(); renderWindow.resize(256, 256); return qtapplication.exec(); } /** \example Step1.cpp */ diff --git a/Examples/Tutorial/Step2/Step2.cpp b/Examples/Tutorial/Step2/Step2.cpp index 690cecd5b4..b836cf9447 100644 --- a/Examples/Tutorial/Step2/Step2.cpp +++ b/Examples/Tutorial/Step2/Step2.cpp @@ -1,93 +1,93 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include #include #include #include //##Documentation //## @brief Load one or more data sets (many image, surface //## and other formats) and display it in a 2D view int main(int argc, char *argv[]) { QApplication qtapplication(argc, argv); if (argc < 2) { fprintf( stderr, "Usage: %s [filename1] [filename2] ...\n\n", itksys::SystemTools::GetFilenameName(argv[0]).c_str()); return 1; } // Register Qmitk-dependent global instances QmitkRegisterClasses(); //************************************************************************* // Part I: Basic initialization //************************************************************************* // Create a data storage object. We will use it as a singleton mitk::StandaloneDataStorage::Pointer storage = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for (i = 1; i < argc; ++i) { // For testing if (strcmp(argv[i], "-testing") == 0) continue; //********************************************************************* // Part III: Put the data into the datastorage //********************************************************************* // Add the node to the DataStorage mitk::IOUtil::Load(argv[i], *storage); } //************************************************************************* // Part IV: Create window and pass the datastorage to it //************************************************************************* // Create a RenderWindow QmitkRenderWindow renderWindow; // Tell the RenderWindow which (part of) the datastorage to render renderWindow.GetRenderer()->SetDataStorage(storage); // Initialize the RenderWindow auto geo = storage->ComputeBoundingGeometry3D(storage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); // Select a slice mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); if (sliceNaviController) - sliceNaviController->GetSlice()->SetPos(2); + sliceNaviController->GetStepper()->SetPos(2); //************************************************************************* // Part V: Qt-specific initialization //************************************************************************* renderWindow.show(); renderWindow.resize(256, 256); return qtapplication.exec(); } /** \example Step2.cpp */ diff --git a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp index 80ba5e8016..4ff557a00d 100644 --- a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp +++ b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp @@ -1,786 +1,786 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkCLPolyToNrrd_cpp #define mitkCLPolyToNrrd_cpp #include "time.h" #include #include #include #include "mitkCommandLineParser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkResampleImageFilter.h" #include #include #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include "vtkRenderLargeImage.h" #include "vtkPNGWriter.h" typedef itk::Image< double, 3 > FloatImageType; typedef itk::Image< unsigned short, 3 > MaskImageType; template class punct_facet : public std::numpunct { public: punct_facet(charT sep) : m_Sep(sep) { } protected: charT do_decimal_point() const override { return m_Sep; } private: charT m_Sep; }; template void ResampleImage(itk::Image* itkImage, float resolution, mitk::Image::Pointer& newImage) { typedef itk::Image ImageType; typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); auto spacing = itkImage->GetSpacing(); auto size = itkImage->GetLargestPossibleRegion().GetSize(); for (unsigned int i = 0; i < VImageDimension; ++i) { size[i] = size[i] / (1.0*resolution)*(1.0*spacing[i])+1.0; } spacing.Fill(resolution); resampler->SetInput(itkImage); resampler->SetSize(size); resampler->SetOutputSpacing(spacing); resampler->SetOutputOrigin(itkImage->GetOrigin()); resampler->SetOutputDirection(itkImage->GetDirection()); resampler->Update(); newImage->InitializeByItk(resampler->GetOutput()); mitk::GrabItkImageMemory(resampler->GetOutput(), newImage); } template static void CreateNoNaNMask(itk::Image* itkValue, mitk::Image::Pointer mask, mitk::Image::Pointer& newMask) { typedef itk::Image< TPixel, VImageDimension> LFloatImageType; typedef itk::Image< unsigned short, VImageDimension> LMaskImageType; typename LMaskImageType::Pointer itkMask = LMaskImageType::New(); mitk::CastToItkImage(mask, itkMask); typedef itk::ImageDuplicator< LMaskImageType > DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(itkMask); duplicator->Update(); auto tmpMask = duplicator->GetOutput(); itk::ImageRegionIterator mask1Iter(itkMask, itkMask->GetLargestPossibleRegion()); itk::ImageRegionIterator mask2Iter(tmpMask, tmpMask->GetLargestPossibleRegion()); itk::ImageRegionIterator imageIter(itkValue, itkValue->GetLargestPossibleRegion()); while (!mask1Iter.IsAtEnd()) { mask2Iter.Set(0); if (mask1Iter.Value() > 0) { // Is not NaN if (imageIter.Value() == imageIter.Value()) { mask2Iter.Set(1); } } ++mask1Iter; ++mask2Iter; ++imageIter; } newMask->InitializeByItk(tmpMask); mitk::GrabItkImageMemory(tmpMask, newMask); } template static void ResampleMask(itk::Image* itkMoving, mitk::Image::Pointer ref, mitk::Image::Pointer& newMask) { typedef itk::Image< TPixel, VImageDimension> LMaskImageType; typedef itk::NearestNeighborInterpolateImageFunction< LMaskImageType> NearestNeighborInterpolateImageFunctionType; typedef itk::ResampleImageFilter ResampleFilterType; typename NearestNeighborInterpolateImageFunctionType::Pointer nn_interpolator = NearestNeighborInterpolateImageFunctionType::New(); typename LMaskImageType::Pointer itkRef = LMaskImageType::New(); mitk::CastToItkImage(ref, itkRef); typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetInput(itkMoving); resampler->SetReferenceImage(itkRef); resampler->UseReferenceImageOn(); resampler->SetInterpolator(nn_interpolator); resampler->Update(); newMask->InitializeByItk(resampler->GetOutput()); mitk::GrabItkImageMemory(resampler->GetOutput(), newMask); } static void ExtractSlicesFromImages(mitk::Image::Pointer image, mitk::Image::Pointer mask, mitk::Image::Pointer maskNoNaN, mitk::Image::Pointer morphMask, int direction, std::vector &imageVector, std::vector &maskVector, std::vector &maskNoNaNVector, std::vector &morphMaskVector) { typedef itk::Image< double, 2 > FloatImage2DType; typedef itk::Image< unsigned short, 2 > MaskImage2DType; FloatImageType::Pointer itkFloat = FloatImageType::New(); MaskImageType::Pointer itkMask = MaskImageType::New(); MaskImageType::Pointer itkMaskNoNaN = MaskImageType::New(); MaskImageType::Pointer itkMorphMask = MaskImageType::New(); mitk::CastToItkImage(mask, itkMask); mitk::CastToItkImage(maskNoNaN, itkMaskNoNaN); mitk::CastToItkImage(image, itkFloat); mitk::CastToItkImage(morphMask, itkMorphMask); int idxA, idxB, idxC; switch (direction) { case 0: idxA = 1; idxB = 2; idxC = 0; break; case 1: idxA = 0; idxB = 2; idxC = 1; break; case 2: idxA = 0; idxB = 1; idxC = 2; break; default: idxA = 1; idxB = 2; idxC = 0; break; } auto imageSize = image->GetLargestPossibleRegion().GetSize(); FloatImageType::IndexType index3D; FloatImage2DType::IndexType index2D; FloatImage2DType::SpacingType spacing2D; spacing2D[0] = itkFloat->GetSpacing()[idxA]; spacing2D[1] = itkFloat->GetSpacing()[idxB]; for (unsigned int i = 0; i < imageSize[idxC]; ++i) { FloatImage2DType::RegionType region; FloatImage2DType::IndexType start; FloatImage2DType::SizeType size; start[0] = 0; start[1] = 0; size[0] = imageSize[idxA]; size[1] = imageSize[idxB]; region.SetIndex(start); region.SetSize(size); FloatImage2DType::Pointer image2D = FloatImage2DType::New(); image2D->SetRegions(region); image2D->Allocate(); MaskImage2DType::Pointer mask2D = MaskImage2DType::New(); mask2D->SetRegions(region); mask2D->Allocate(); MaskImage2DType::Pointer masnNoNaN2D = MaskImage2DType::New(); masnNoNaN2D->SetRegions(region); masnNoNaN2D->Allocate(); MaskImage2DType::Pointer morph2D = MaskImage2DType::New(); morph2D->SetRegions(region); morph2D->Allocate(); unsigned long voxelsInMask = 0; for (unsigned int a = 0; a < imageSize[idxA]; ++a) { for (unsigned int b = 0; b < imageSize[idxB]; ++b) { index3D[idxA] = a; index3D[idxB] = b; index3D[idxC] = i; index2D[0] = a; index2D[1] = b; image2D->SetPixel(index2D, itkFloat->GetPixel(index3D)); mask2D->SetPixel(index2D, itkMask->GetPixel(index3D)); masnNoNaN2D->SetPixel(index2D, itkMaskNoNaN->GetPixel(index3D)); morph2D->SetPixel(index2D, itkMorphMask->GetPixel(index3D)); voxelsInMask += (itkMask->GetPixel(index3D) > 0) ? 1 : 0; } } image2D->SetSpacing(spacing2D); mask2D->SetSpacing(spacing2D); masnNoNaN2D->SetSpacing(spacing2D); morph2D->SetSpacing(spacing2D); mitk::Image::Pointer tmpFloatImage = mitk::Image::New(); tmpFloatImage->InitializeByItk(image2D.GetPointer()); mitk::GrabItkImageMemory(image2D, tmpFloatImage); mitk::Image::Pointer tmpMaskImage = mitk::Image::New(); tmpMaskImage->InitializeByItk(mask2D.GetPointer()); mitk::GrabItkImageMemory(mask2D, tmpMaskImage); mitk::Image::Pointer tmpMaskNoNaNImage = mitk::Image::New(); tmpMaskNoNaNImage->InitializeByItk(masnNoNaN2D.GetPointer()); mitk::GrabItkImageMemory(masnNoNaN2D, tmpMaskNoNaNImage); mitk::Image::Pointer tmpMorphMaskImage = mitk::Image::New(); tmpMorphMaskImage->InitializeByItk(morph2D.GetPointer()); mitk::GrabItkImageMemory(morph2D, tmpMorphMaskImage); if (voxelsInMask > 0) { imageVector.push_back(tmpFloatImage); maskVector.push_back(tmpMaskImage); maskNoNaNVector.push_back(tmpMaskNoNaNImage); morphMaskVector.push_back(tmpMorphMaskImage); } } } static void SaveSliceOrImageAsPNG(mitk::Image::Pointer image, mitk::Image::Pointer mask, std::string path, int index) { // Create a Standalone Datastorage for the single purpose of saving screenshots.. mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); QmitkRenderWindow renderWindow; renderWindow.GetRenderer()->SetDataStorage(ds); auto nodeI = mitk::DataNode::New(); nodeI->SetData(image); auto nodeM = mitk::DataNode::New(); nodeM->SetData(mask); ds->Add(nodeI); ds->Add(nodeM); auto geo = ds->ComputeBoundingGeometry3D(ds->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); unsigned int numberOfSteps = 1; if (sliceNaviController) { - numberOfSteps = sliceNaviController->GetSlice()->GetSteps(); - sliceNaviController->GetSlice()->SetPos(0); + numberOfSteps = sliceNaviController->GetStepper()->GetSteps(); + sliceNaviController->GetStepper()->SetPos(0); } renderWindow.show(); renderWindow.resize(256, 256); for (unsigned int currentStep = 0; currentStep < numberOfSteps; ++currentStep) { if (sliceNaviController) { - sliceNaviController->GetSlice()->SetPos(currentStep); + sliceNaviController->GetStepper()->SetPos(currentStep); } renderWindow.GetRenderer()->PrepareRender(); vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow(); mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2); auto vtkRender = baserenderer->GetVtkRenderer(); vtkRender->GetRenderWindow()->WaitForCompletion(); vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New(); magnifier->SetInput(vtkRender); magnifier->SetMagnification(3.0); std::stringstream ss; ss << path << "_Idx-" << index << "_Step-"<> tmpImageName; auto fileWriter = vtkPNGWriter::New(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(tmpImageName.c_str()); fileWriter->Write(); fileWriter->Delete(); } } int main(int argc, char* argv[]) { // Commented : Updated to a common interface, include, if possible, mask is type unsigned short, uses Quantification, Comments // Name follows standard scheme with Class Name::Feature Name // Commented 2: Updated to use automatic inclusion of list of parameters if required. mitk::GIFImageDescriptionFeatures::Pointer ipCalculator = mitk::GIFImageDescriptionFeatures::New(); // Commented 2, Tested mitk::GIFFirstOrderStatistics::Pointer firstOrderCalculator = mitk::GIFFirstOrderStatistics::New(); //Commented 2 mitk::GIFFirstOrderHistogramStatistics::Pointer firstOrderHistoCalculator = mitk::GIFFirstOrderHistogramStatistics::New(); // Commented 2, Tested mitk::GIFFirstOrderNumericStatistics::Pointer firstOrderNumericCalculator = mitk::GIFFirstOrderNumericStatistics::New(); // Commented 2, Tested mitk::GIFVolumetricStatistics::Pointer volCalculator = mitk::GIFVolumetricStatistics::New(); // Commented 2, Tested mitk::GIFVolumetricDensityStatistics::Pointer voldenCalculator = mitk::GIFVolumetricDensityStatistics::New(); // Commented 2, Tested mitk::GIFCooccurenceMatrix::Pointer coocCalculator = mitk::GIFCooccurenceMatrix::New(); // Commented 2, Will not be tested mitk::GIFCooccurenceMatrix2::Pointer cooc2Calculator = mitk::GIFCooccurenceMatrix2::New(); //Commented 2 mitk::GIFNeighbouringGreyLevelDependenceFeature::Pointer ngldCalculator = mitk::GIFNeighbouringGreyLevelDependenceFeature::New(); //Commented 2, Tested mitk::GIFGreyLevelRunLength::Pointer rlCalculator = mitk::GIFGreyLevelRunLength::New(); // Commented 2 mitk::GIFGreyLevelSizeZone::Pointer glszCalculator = mitk::GIFGreyLevelSizeZone::New(); // Commented 2, Tested mitk::GIFGreyLevelDistanceZone::Pointer gldzCalculator = mitk::GIFGreyLevelDistanceZone::New(); //Commented 2, Tested mitk::GIFLocalIntensity::Pointer lociCalculator = mitk::GIFLocalIntensity::New(); //Commented 2, Tested mitk::GIFIntensityVolumeHistogramFeatures::Pointer ivohCalculator = mitk::GIFIntensityVolumeHistogramFeatures::New(); // Commented 2 mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::Pointer ngtdCalculator = mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::New(); //Commented 2, Tested mitk::GIFCurvatureStatistic::Pointer curvCalculator = mitk::GIFCurvatureStatistic::New(); //Commented 2, Tested std::vector features; features.push_back(volCalculator.GetPointer()); features.push_back(voldenCalculator.GetPointer()); features.push_back(curvCalculator.GetPointer()); features.push_back(firstOrderCalculator.GetPointer()); features.push_back(firstOrderNumericCalculator.GetPointer()); features.push_back(firstOrderHistoCalculator.GetPointer()); features.push_back(ivohCalculator.GetPointer()); features.push_back(lociCalculator.GetPointer()); features.push_back(coocCalculator.GetPointer()); features.push_back(cooc2Calculator.GetPointer()); features.push_back(ngldCalculator.GetPointer()); features.push_back(rlCalculator.GetPointer()); features.push_back(glszCalculator.GetPointer()); features.push_back(gldzCalculator.GetPointer()); features.push_back(ipCalculator.GetPointer()); features.push_back(ngtdCalculator.GetPointer()); mitkCommandLineParser parser; parser.setArgumentPrefix("--", "-"); mitk::cl::GlobalImageFeaturesParameter param; param.AddParameter(parser); parser.addArgument("--","-", mitkCommandLineParser::String, "---", "---", us::Any(),true); for (auto cFeature : features) { cFeature->AddArguments(parser); } parser.addArgument("--", "-", mitkCommandLineParser::String, "---", "---", us::Any(), true); parser.addArgument("description","d",mitkCommandLineParser::String,"Text","Description that is added to the output",us::Any()); parser.addArgument("direction", "dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... Without dimension 0,1,2... ", us::Any()); parser.addArgument("slice-wise", "slice", mitkCommandLineParser::String, "Int", "Allows to specify if the image is processed slice-wise (number giving direction) ", us::Any()); parser.addArgument("output-mode", "omode", mitkCommandLineParser::Int, "Int", "Defines the format of the output. 0: (Default) results of an image / slice are written in a single row;" " 1: results of an image / slice are written in a single column; 2: store the result of on image as structured radiomocs report (XML)."); // Miniapp Infos parser.setCategory("Classification Tools"); parser.setTitle("Global Image Feature calculator"); parser.setDescription("Calculates different global statistics for a given segmentation / image combination"); parser.setContributor("German Cancer Research Center (DKFZ)"); std::map parsedArgs = parser.parseArguments(argc, argv); param.ParseParameter(parsedArgs); if (parsedArgs.size()==0) { return EXIT_FAILURE; } if ( parsedArgs.count("help") || parsedArgs.count("h")) { return EXIT_SUCCESS; } std::string version = "Version: 1.23"; MITK_INFO << version; std::ofstream log; if (param.useLogfile) { log.open(param.logfilePath, std::ios::app); log << std::endl; log << version; log << "Image: " << param.imagePath; log << "Mask: " << param.maskPath; } if (param.useDecimalPoint) { std::cout.imbue(std::locale(std::cout.getloc(), new punct_facet(param.decimalPoint))); } //representing the original loaded image data without any prepropcessing that might come. mitk::Image::Pointer loadedImage = mitk::IOUtil::Load(param.imagePath); //representing the original loaded mask data without any prepropcessing that might come. mitk::Image::Pointer loadedMask = mitk::IOUtil::Load(param.maskPath); mitk::Image::Pointer image = loadedImage; mitk::Image::Pointer mask = loadedMask; mitk::Image::Pointer tmpImage = loadedImage; mitk::Image::Pointer tmpMask = loadedMask; mitk::Image::Pointer morphMask = mask; if (param.useMorphMask) { morphMask = mitk::IOUtil::Load(param.morphPath); } log << " Check for Dimensions -"; if ((image->GetDimension() != mask->GetDimension())) { MITK_INFO << "Dimension of image does not match. "; MITK_INFO << "Correct one image, may affect the result"; if (image->GetDimension() == 2) { mitk::Convert2Dto3DImageFilter::Pointer multiFilter2 = mitk::Convert2Dto3DImageFilter::New(); multiFilter2->SetInput(tmpImage); multiFilter2->Update(); image = multiFilter2->GetOutput(); } if (mask->GetDimension() == 2) { mitk::Convert2Dto3DImageFilter::Pointer multiFilter3 = mitk::Convert2Dto3DImageFilter::New(); multiFilter3->SetInput(tmpMask); multiFilter3->Update(); mask = multiFilter3->GetOutput(); } } int writeDirection = 0; if (parsedArgs.count("output-mode")) { writeDirection = us::any_cast(parsedArgs["output-mode"]); } log << " Check for Resolution -"; if (param.resampleToFixIsotropic) { mitk::Image::Pointer newImage = mitk::Image::New(); AccessByItk_2(image, ResampleImage, param.resampleResolution, newImage); image = newImage; } log << " Resample if required -"; if (param.resampleMask) { mitk::Image::Pointer newMaskImage = mitk::Image::New(); AccessByItk_2(mask, ResampleMask, image, newMaskImage); mask = newMaskImage; } if ( ! mitk::Equal(mask->GetGeometry(0)->GetOrigin(), image->GetGeometry(0)->GetOrigin())) { MITK_INFO << "Not equal Origins"; if (param.ensureSameSpace) { MITK_INFO << "Warning!"; MITK_INFO << "The origin of the input image and the mask do not match. They are"; MITK_INFO << "now corrected. Please check to make sure that the images still match"; image->GetGeometry(0)->SetOrigin(mask->GetGeometry(0)->GetOrigin()); } else { return -1; } } log << " Check for Equality -"; if ( ! mitk::Equal(mask->GetGeometry(0)->GetSpacing(), image->GetGeometry(0)->GetSpacing())) { MITK_INFO << "Not equal Spacing"; if (param.ensureSameSpace) { MITK_INFO << "Warning!"; MITK_INFO << "The spacing of the mask was set to match the spacing of the input image."; MITK_INFO << "This might cause unintended spacing of the mask image"; image->GetGeometry(0)->SetSpacing(mask->GetGeometry(0)->GetSpacing()); } else { MITK_INFO << "The spacing of the mask and the input images is not equal."; MITK_INFO << "Terminating the programm. You may use the '-fi' option"; return -1; } } int direction = 0; if (parsedArgs.count("direction")) { direction = mitk::cl::splitDouble(parsedArgs["direction"].ToString(), ';')[0]; } MITK_INFO << "Start creating Mask without NaN"; mitk::Image::Pointer maskNoNaN = mitk::Image::New(); AccessByItk_2(image, CreateNoNaNMask, mask, maskNoNaN); //CreateNoNaNMask(mask, image, maskNoNaN); bool sliceWise = false; int sliceDirection = 0; unsigned int currentSlice = 0; bool imageToProcess = true; std::vector floatVector; std::vector maskVector; std::vector maskNoNaNVector; std::vector morphMaskVector; if ((parsedArgs.count("slice-wise")) && image->GetDimension() > 2) { MITK_INFO << "Enabled slice-wise"; sliceWise = true; sliceDirection = mitk::cl::splitDouble(parsedArgs["slice-wise"].ToString(), ';')[0]; MITK_INFO << sliceDirection; ExtractSlicesFromImages(image, mask, maskNoNaN, morphMask, sliceDirection, floatVector, maskVector, maskNoNaNVector, morphMaskVector); MITK_INFO << "Slice"; } log << " Configure features -"; for (auto cFeature : features) { if (param.defineGlobalMinimumIntensity) { cFeature->SetMinimumIntensity(param.globalMinimumIntensity); cFeature->SetUseMinimumIntensity(true); } if (param.defineGlobalMaximumIntensity) { cFeature->SetMaximumIntensity(param.globalMaximumIntensity); cFeature->SetUseMaximumIntensity(true); } if (param.defineGlobalNumberOfBins) { cFeature->SetBins(param.globalNumberOfBins); MITK_INFO << param.globalNumberOfBins; } cFeature->SetParameters(parsedArgs); cFeature->SetDirection(direction); cFeature->SetEncodeParametersInFeaturePrefix(param.encodeParameter); } bool addDescription = parsedArgs.count("description"); mitk::cl::FeatureResultWriter writer(param.outputPath, writeDirection); if (param.useDecimalPoint) { writer.SetDecimalPoint(param.decimalPoint); } std::string description = ""; if (addDescription) { description = parsedArgs["description"].ToString(); } mitk::Image::Pointer cImage = image; mitk::Image::Pointer cMask = mask; mitk::Image::Pointer cMaskNoNaN = maskNoNaN; mitk::Image::Pointer cMorphMask = morphMask; if (param.useHeader) { writer.AddColumn("SoftwareVersion"); writer.AddColumn("Patient"); writer.AddColumn("Image"); writer.AddColumn("Segmentation"); } // Create a QTApplication and a Datastorage // This is necessary in order to save screenshots of // each image / slice. QApplication qtapplication(argc, argv); QmitkRegisterClasses(); std::vector allStats; log << " Begin Processing -"; while (imageToProcess) { if (sliceWise) { cImage = floatVector[currentSlice]; cMask = maskVector[currentSlice]; cMaskNoNaN = maskNoNaNVector[currentSlice]; cMorphMask = morphMaskVector[currentSlice]; imageToProcess = (floatVector.size()-1 > (currentSlice)) ? true : false ; } else { imageToProcess = false; } if (param.writePNGScreenshots) { SaveSliceOrImageAsPNG(cImage, cMask, param.pngScreenshotsPath, currentSlice); } if (param.writeAnalysisImage) { mitk::IOUtil::Save(cImage, param.anaylsisImagePath); } if (param.writeAnalysisMask) { mitk::IOUtil::Save(cMask, param.analysisMaskPath); } mitk::AbstractGlobalImageFeature::FeatureListType stats; for (auto cFeature : features) { log << " Calculating " << cFeature->GetFeatureClassName() << " -"; cFeature->SetMorphMask(cMorphMask); cFeature->CalculateAndAppendFeatures(cImage, cMask, cMaskNoNaN, stats, !param.calculateAllFeatures); } for (std::size_t i = 0; i < stats.size(); ++i) { std::cout << stats[i].first.legacyName << " - " << stats[i].second << std::endl; } writer.AddHeader(description, currentSlice, stats, param.useHeader, addDescription); if (true) { writer.AddSubjectInformation(MITK_REVISION); writer.AddSubjectInformation(param.imageFolder); writer.AddSubjectInformation(param.imageName); writer.AddSubjectInformation(param.maskName); } writer.AddResult(description, currentSlice, stats, param.useHeader, addDescription); allStats.push_back(stats); ++currentSlice; } log << " Process Slicewise -"; if (sliceWise) { mitk::AbstractGlobalImageFeature::FeatureListType statMean, statStd; for (std::size_t i = 0; i < allStats[0].size(); ++i) { auto cElement1 = allStats[0][i]; cElement1.first.legacyName = "SliceWise Mean " + cElement1.first.legacyName; cElement1.second = 0.0; auto cElement2 = allStats[0][i]; cElement2.first.legacyName = "SliceWise Var. " + cElement2.first.legacyName; cElement2.second = 0.0; statMean.push_back(cElement1); statStd.push_back(cElement2); } for (auto cStat : allStats) { for (std::size_t i = 0; i < cStat.size(); ++i) { statMean[i].second += cStat[i].second / (1.0*allStats.size()); } } for (auto cStat : allStats) { for (std::size_t i = 0; i < cStat.size(); ++i) { statStd[i].second += (cStat[i].second - statMean[i].second)*(cStat[i].second - statMean[i].second) / (1.0*allStats.size()); } } for (std::size_t i = 0; i < statMean.size(); ++i) { std::cout << statMean[i].first.legacyName << " - " << statMean[i].second << std::endl; std::cout << statStd[i].first.legacyName << " - " << statStd[i].second << std::endl; } if (true) { writer.AddSubjectInformation(MITK_REVISION); writer.AddSubjectInformation(param.imageFolder); writer.AddSubjectInformation(param.imageName); writer.AddSubjectInformation(param.maskName + " - Mean"); } writer.AddResult(description, currentSlice, statMean, param.useHeader, addDescription); if (true) { writer.AddSubjectInformation(MITK_REVISION); writer.AddSubjectInformation(param.imageFolder); writer.AddSubjectInformation(param.imageName); writer.AddSubjectInformation(param.maskName + " - Var."); } writer.AddResult(description, currentSlice, statStd, param.useHeader, addDescription); } int returnCode = EXIT_SUCCESS; if (!param.outputXMLPath.empty()) { if (sliceWise) { MITK_ERROR << "Xml output is not supported in slicewise mode"; returnCode = EXIT_FAILURE; } else { mitk::cl::CLResultXMLWriter xmlWriter; xmlWriter.SetCLIArgs(parsedArgs); xmlWriter.SetFeatures(allStats.front()); xmlWriter.SetImage(loadedImage); xmlWriter.SetMask(loadedMask); xmlWriter.SetMethodName("CLGlobalImageFeatures"); xmlWriter.SetMethodVersion(version + "(mitk: " MITK_VERSION_STRING+")"); xmlWriter.SetOrganisation("German Cancer Research Center (DKFZ)"); xmlWriter.SetPipelineUID(param.pipelineUID); xmlWriter.write(param.outputXMLPath); } } if (param.useLogfile) { log << "Finished calculation" << std::endl; log.close(); } return returnCode; } #endif diff --git a/Modules/Classification/CLMiniApps/CLScreenshot.cpp b/Modules/Classification/CLMiniApps/CLScreenshot.cpp index 439d62b1c5..ad7532da5b 100644 --- a/Modules/Classification/CLMiniApps/CLScreenshot.cpp +++ b/Modules/Classification/CLMiniApps/CLScreenshot.cpp @@ -1,167 +1,167 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkCLPolyToNrrd_cpp #define mitkCLPolyToNrrd_cpp #include "time.h" #include #include #include #include "mitkCommandLineParser.h" #include #include #include #include #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include "vtkRenderLargeImage.h" #include "vtkPNGWriter.h" static void SaveSliceOrImageAsPNG(std::vector listOfOutputs, std::string path) { std::vector colorList; colorList.push_back(mitk::ColorProperty::New(0.9569, 0.16471, 0.25490)); // Red colorList.push_back(mitk::ColorProperty::New(1, 0.839, 0)); // Yellow colorList.push_back(mitk::ColorProperty::New(0, 0.6, 0.2)); // Green colorList.push_back(mitk::ColorProperty::New(0, 0.2784, 0.7255)); // BLue colorList.push_back(mitk::ColorProperty::New(1,0.3608,0)); // Orange colorList.push_back(mitk::ColorProperty::New(0.839215,0.141176,0.80784)); // Violett colorList.push_back(mitk::ColorProperty::New(0.1372,0.81568,0.7647)); // Turkis colorList.push_back(mitk::ColorProperty::New(0.61176,0.9568,0.16078)); // Bright Green colorList.push_back(mitk::ColorProperty::New(1,0.4274,0.16862)); // Dark Orange colorList.push_back(mitk::ColorProperty::New(0.88633,0.14901,0.64705)); // Pink // Create a Standalone Datastorage for the single purpose of saving screenshots.. mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); QmitkRenderWindow renderWindow; renderWindow.GetRenderer()->SetDataStorage(ds); int numberOfSegmentations = 0; bool isSegmentation = false; for (auto name : listOfOutputs) { mitk::Image::Pointer tmpImage = mitk::IOUtil::Load(name); auto nodeI = mitk::DataNode::New(); nodeI->SetData(tmpImage); nodeI->GetPropertyValue("binary",isSegmentation); if (isSegmentation) { nodeI->SetProperty("color", colorList[numberOfSegmentations % colorList.size()]); nodeI->SetProperty("binaryimage.hoveringannotationcolor", colorList[numberOfSegmentations % colorList.size()]); nodeI->SetProperty("binaryimage.hoveringcolor", colorList[numberOfSegmentations % colorList.size()]); nodeI->SetProperty("binaryimage.selectedannotationcolor", colorList[numberOfSegmentations % colorList.size()]); nodeI->SetProperty("binaryimage.selectedcolor", colorList[numberOfSegmentations % colorList.size()]); numberOfSegmentations++; } ds->Add(nodeI); } auto geo = ds->ComputeBoundingGeometry3D(ds->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController(); unsigned int numberOfSteps = 1; if (sliceNaviController) { - numberOfSteps = sliceNaviController->GetSlice()->GetSteps(); - sliceNaviController->GetSlice()->SetPos(0); + numberOfSteps = sliceNaviController->GetStepper()->GetSteps(); + sliceNaviController->GetStepper()->SetPos(0); } renderWindow.show(); renderWindow.resize(512, 512); for (unsigned int currentStep = 0; currentStep < numberOfSteps; ++currentStep) { if (sliceNaviController) { - sliceNaviController->GetSlice()->SetPos(currentStep); + sliceNaviController->GetStepper()->SetPos(currentStep); } renderWindow.GetRenderer()->PrepareRender(); vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow(); mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2); auto vtkRender = baserenderer->GetVtkRenderer(); vtkRender->GetRenderWindow()->WaitForCompletion(); vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New(); magnifier->SetInput(vtkRender); magnifier->SetMagnification(3.0); std::stringstream ss; ss << path << "screenshot_step-"<> tmpImageName; auto fileWriter = vtkPNGWriter::New(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(tmpImageName.c_str()); fileWriter->Write(); fileWriter->Delete(); } } int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setArgumentPrefix("--", "-"); // Required Parameter parser.addArgument("image", "i", mitkCommandLineParser::Image, "Input Image", "Path to the input image files (Separated with semicolons)", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output text file", "Path to output file. The output statistic is appended to this file.", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.addArgument("direction", "dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... Without dimension 0,1,2... ", us::Any()); // Miniapp Infos parser.setCategory("Classification Tools"); parser.setTitle("Screenshot of a single image"); parser.setDescription(""); parser.setContributor("German Cancer Research Center (DKFZ)"); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) { return EXIT_FAILURE; } if ( parsedArgs.count("help") || parsedArgs.count("h")) { return EXIT_SUCCESS; } std::string version = "Version: 1.0"; MITK_INFO << version; //int direction = 0; if (parsedArgs.count("direction")) { MITK_INFO << "Warning: Option direction currently not supported"; // direction = mitk::cl::splitDouble(parsedArgs["direction"].ToString(), ';')[0]; } auto listOfFiles = mitk::cl::splitString(parsedArgs["image"].ToString(), ';'); // Create a QTApplication and a Datastorage // This is necessary in order to save screenshots of // each image / slice. QApplication qtapplication(argc, argv); QmitkRegisterClasses(); SaveSliceOrImageAsPNG(listOfFiles, parsedArgs["output"].ToString()); return 0; } #endif diff --git a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp index bfb05627d7..32ca0c577e 100644 --- a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp @@ -1,435 +1,435 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include #include #include #include #include // VTK #include #include // us #include #include mitk::InteractionTestHelper::InteractionTestHelper(const std::string &interactionXmlFilePath) : m_InteractionFilePath(interactionXmlFilePath) { this->Initialize(interactionXmlFilePath); } void mitk::InteractionTestHelper::Initialize(const std::string &interactionXmlFilePath) { tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS == document.LoadFile(interactionXmlFilePath.c_str())) { // get RenderingManager instance auto rm = mitk::RenderingManager::GetInstance(); // create data storage m_DataStorage = mitk::StandaloneDataStorage::New(); // for each renderer found create a render window and configure for (auto *element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer().c_str()); element != nullptr; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer().c_str())) { // get name of renderer const char *rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); // get view direction mitk::AnatomicalPlane viewDirection = mitk::AnatomicalPlane::Axial; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection().c_str()) != nullptr) { int viewDirectionNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection().c_str())); viewDirection = static_cast(viewDirectionNum); } // get mapper slot id MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str()) != nullptr) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str())); mapperID = static_cast(mapperIDNum); } // Get Size of Render Windows int size[3]; size[0] = size[1] = size[2] = 0; if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX().c_str()) != nullptr) { size[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX().c_str())); } if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY().c_str()) != nullptr) { size[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY().c_str())); } if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ().c_str()) != nullptr) { size[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ().c_str())); } // create renderWindow, renderer and dispatcher auto rw = RenderWindow::New(nullptr, rendererName); // VtkRenderWindow is created within constructor if nullptr if (size[0] != 0 && size[1] != 0) { rw->SetSize(size[0], size[1]); rw->GetRenderer()->Resize(size[0], size[1]); } // set storage of renderer rw->GetRenderer()->SetDataStorage(m_DataStorage); // set view direction to axial rw->GetSliceNavigationController()->SetDefaultViewDirection(viewDirection); // set renderer to render 2D rw->GetRenderer()->SetMapperID(mapperID); rw->GetRenderer()->PrepareRender(); // Some more magic for the 3D render window case: // Camera view direction, position and focal point if (mapperID == mitk::BaseRenderer::Standard3D) { if (element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str()) != nullptr) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str())); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY().c_str())); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ().c_str())); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if (element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str()) != nullptr) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str())); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY().c_str())); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ().c_str())); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if (element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str()) != nullptr) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str())); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY().c_str())); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ().c_str())); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } } rw->GetVtkRenderWindow()->Render(); rw->GetVtkRenderWindow()->WaitForCompletion(); // connect SliceNavigationControllers to timestep changed event of TimeNavigationController rw->GetSliceNavigationController()->ConnectGeometryTimeEvent(rm->GetTimeNavigationController()); rm->GetTimeNavigationController()->ConnectGeometryTimeEvent(rw->GetSliceNavigationController()); // add to list of known render windows m_RenderWindowList.push_back(rw); } // TODO: check the following lines taken from QmitkStdMultiWidget and adapt them to be executed in our code here. // mitkWidget1->GetSliceNavigationController() // ->ConnectGeometrySendEvent(mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); // register interaction event obserer to handle scroll events InitializeDisplayActionEventHandling(); } else { mitkThrow() << "Can not load interaction xml file <" << m_InteractionFilePath << ">"; } // WARNING assumes a 3D window exists !!!! this->AddDisplayPlaneSubTree(); } void mitk::InteractionTestHelper::InitializeDisplayActionEventHandling() { m_DisplayActionEventBroadcast = mitk::DisplayActionEventBroadcast::New(); m_DisplayActionEventBroadcast->LoadStateMachine("DisplayInteraction.xml"); m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigMITKBase.xml"); m_DisplayActionEventBroadcast->AddEventConfig("DisplayConfigCrosshair.xml"); } mitk::InteractionTestHelper::~InteractionTestHelper() { mitk::RenderingManager *rm = mitk::RenderingManager::GetInstance(); // unregister renderers auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { rm->GetTimeNavigationController()->Disconnect((*it)->GetSliceNavigationController()); (*it)->GetSliceNavigationController()->Disconnect(rm->GetTimeNavigationController()); mitk::BaseRenderer::RemoveInstance((*it)->GetVtkRenderWindow()); } rm->RemoveAllObservers(); } mitk::DataStorage::Pointer mitk::InteractionTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::InteractionTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); this->Set3dCameraSettings(); } void mitk::InteractionTestHelper::PlaybackInteraction() { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); // load events if not loaded yet if (m_Events.empty()) this->LoadInteraction(); auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { (*it)->GetRenderer()->PrepareRender(); (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); it = m_RenderWindowList.begin(); for (; it != end; ++it) { (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } // mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); // playback all events in queue for (unsigned long i = 0; i < m_Events.size(); ++i) { // let dispatcher of sending renderer process the event m_Events.at(i)->GetSender()->GetDispatcher()->ProcessEvent(m_Events.at(i)); } if (false) { it--; (*it)->GetVtkRenderWindow()->GetInteractor()->Start(); } } void mitk::InteractionTestHelper::LoadInteraction() { // load interaction pattern from xml file std::ifstream xmlStream(m_InteractionFilePath.c_str()); mitk::XML2EventParser parser(xmlStream); m_Events = parser.GetInteractions(); xmlStream.close(); // Avoid VTK warning: Trying to delete object with non-zero reference count. parser.SetReferenceCount(0); } void mitk::InteractionTestHelper::SetTimeStep(int newTimeStep) { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); bool timeStepIsvalid = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetCreatedWorldGeometry()->IsValidTimeStep( newTimeStep); if (timeStepIsvalid) { - mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(newTimeStep); + mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetStepper()->SetPos(newTimeStep); } } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindowByName(const std::string &name) { auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { if (name.compare((*it)->GetRenderer()->GetName()) == 0) return (*it).GetPointer(); } return nullptr; } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindowByDefaultViewDirection(AnatomicalPlane viewDirection) { auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { if (viewDirection == (*it)->GetSliceNavigationController()->GetDefaultViewDirection()) return (*it).GetPointer(); } return nullptr; } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindow(unsigned int index) { if (index < m_RenderWindowList.size()) { return m_RenderWindowList.at(index).GetPointer(); } else { return nullptr; } } void mitk::InteractionTestHelper::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetProperty("name", mitk::StringProperty::New("Widgets")); node->SetProperty("helper object", mitk::BoolProperty::New(true)); m_DataStorage->Add(node); for (auto it : m_RenderWindowList) { if (it->GetRenderer()->GetMapperID() == BaseRenderer::Standard3D) continue; // ... of widget 1 mitk::DataNode::Pointer planeNode1 = (mitk::BaseRenderer::GetInstance(it->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); planeNode1->SetProperty("visible", mitk::BoolProperty::New(true)); planeNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); planeNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); planeNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); planeNode1->SetProperty("layer", layer); planeNode1->SetColor(1.0, 0.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); planeNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_DataStorage->Add(planeNode1, node); } } void mitk::InteractionTestHelper::Set3dCameraSettings() { tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS == document.LoadFile(m_InteractionFilePath.c_str())) { // for each renderer found create a render window and configure for (auto *element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer().c_str()); element != nullptr; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer().c_str())) { // get name of renderer const char *rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); // get mapper slot id MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str()) != nullptr) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str())); mapperID = static_cast(mapperIDNum); } if (mapperID == mitk::BaseRenderer::Standard3D) { RenderWindow *namedRenderer = nullptr; for (const auto &it : m_RenderWindowList) { if (strcmp(it->GetRenderer()->GetName(), rendererName) == 0) { namedRenderer = it.GetPointer(); break; } } if (namedRenderer == nullptr) { MITK_ERROR << "No match for render window was found."; return; } namedRenderer->GetRenderer()->PrepareRender(); if (element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str()) != nullptr) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str())); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY().c_str())); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ().c_str())); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if (element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str()) != nullptr) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str())); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY().c_str())); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ().c_str())); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if (element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str()) != nullptr) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str())); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY().c_str())); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ().c_str())); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } namedRenderer->GetVtkRenderWindow()->Render(); } } } } diff --git a/Modules/Core/include/mitkBaseController.h b/Modules/Core/include/mitkBaseController.h index 41d8307e65..f0b33dc004 100644 --- a/Modules/Core/include/mitkBaseController.h +++ b/Modules/Core/include/mitkBaseController.h @@ -1,77 +1,69 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkBaseController_h #define mitkBaseController_h #include "mitkEventStateMachine.h" #include "mitkOperationActor.h" #include "mitkStepper.h" #include #include namespace mitk { class BaseRenderer; - //##Documentation - //## @brief Baseclass for renderer slice-/camera-control - //## - //## Tells the render (subclass of BaseRenderer) which slice (subclass - //## SliceNavigationController) or from which direction (subclass - //## CameraController) it has to render. Contains two Stepper for stepping - //## through the slices or through different camera views (e.g., for the - //## creation of a movie around the data), respectively, and through time, if - //## there is 3D+t data. - //## @note not yet implemented - //## @ingroup NavigationControl + /** + * \brief Baseclass for renderer slice-/camera-/time-control + * + * Tells the renderer (subclass of BaseRenderer) which slice (SliceNavigationController), + * which direction (CameraController) or which time (TimeNavigationController) it has to render. + * Contains a Stepper for stepping through the slices, through different + * camera views (e.g., for the creation of a movie around the data) + * and through time, if there is 3D+t data. + */ class MITKCORE_EXPORT BaseController : public OperationActor, public itk::Object { public: - /** Standard class typedefs. */ + mitkClassMacroItkParent(BaseController, OperationActor); itkFactorylessNewMacro(Self); - //##Documentation - //## @brief Get the Stepper through the slices - Stepper *GetSlice(); - const Stepper* GetSlice() const; - - //##Documentation - //## @brief Get the Stepper through the time - Stepper *GetTime(); - const Stepper* GetTime() const; + /** + * \brief Get the stepper through the corresponding dimension. + */ + Stepper* GetStepper(); + const Stepper* GetStepper() const; protected: /** * @brief Default Constructor **/ BaseController(); /** * @brief Default Destructor **/ ~BaseController() override; void ExecuteOperation(Operation *) override; - //## @brief Stepper through the time - Stepper::Pointer m_Time; - //## @brief Stepper through the slices - Stepper::Pointer m_Slice; + //## @brief Stepper through the different dimensions + Stepper::Pointer m_Stepper; unsigned long m_LastUpdateTime; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h index 3450c698d1..f433b1b5b1 100644 --- a/Modules/Core/include/mitkSliceNavigationController.h +++ b/Modules/Core/include/mitkSliceNavigationController.h @@ -1,392 +1,390 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSliceNavigationController_h #define mitkSliceNavigationController_h #include #include #include #include #include #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include namespace mitk { #define mitkTimeGeometryEventMacro(classname, super) \ class MITKCORE_EXPORT classname : public super \ { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char *GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self &); \ } class PlaneGeometry; class BaseGeometry; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a TimeGeometry as input world time geometry * and generates a different TimeGeometry as output, depending on the current * view direction and the current time step. * The TimeGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of PlaneGeometries. One of these PlaneGeometries is * selected as world geometry for the BaseRenderers associated to 2D views. * - * The SliceNavigationController holds has Steppers (one for the slice, a - * second for the time step), which control the selection of a single - * PlaneGeometry from the TimeGeometry. SliceNavigationController generates - * ITK events to tell observers, like a BaseRenderer, when the selected slice - * or timestep changes. + * The SliceNavigationController holds a Stepper, which controls the + * slice-selection of a single PlaneGeometry from the TimeGeometry. + * SliceNavigationController generates ITK events to tell observers, + * like a BaseRenderer, when the selected slice changes. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigation controller the geometry to be sliced * // (with geometry a BaseGeometry::ConstPointer) * sliceCtrl->SetInputWorldTimeGeometry(geometry.GetPointer()); * * // Tell the navigation controller in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::AnatomicalPlane::Axial); * * // Connect one or more BaseRenderer to this navigation controller, i.e.: * // events sent by the navigation controller when stepping through the slices - * // (e.g. by sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer - * // (in this example only slice-changes, see also ConnectGeometryTimeEvent - * // and ConnectGeometryEvents.) + * // (e.g. by sliceCtrl->GetStepper()->Next()) will be received by the BaseRenderer + * // (in this example only slice-changes, see also ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigation widgets to a SliceNavigationController, e.g., a * QmitkSliceNavigationWidget (for Qt): * * \code * // Create the visible navigation widget (a slider with a spin-box) * QmitkSliceNavigationWidget* navigationWidget = * new QmitkSliceNavigationWidget(parent); * * // Connect the navigation widget to the slice-stepper of the - * // SliceNavigationController. For initialization (position, mininal and + * // SliceNavigationController. For initialization (position, minimal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigation widget is normally not necessary, * // since everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. - * new QmitkStepperAdapter(navigationWidget, sliceCtrl->GetSlice()); + * new QmitkStepperAdapter(navigationWidget, sliceCtrl->GetStepper()); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITKCORE_EXPORT SliceNavigationController : public BaseController { public: mitkClassMacro(SliceNavigationController, BaseController); itkNewMacro(Self); /** * \brief Set the input world time geometry out of which the * geometries for slicing will be created. * * Any previous set input geometry (3D or Time) will * be ignored in the future. */ void SetInputWorldTimeGeometry(const TimeGeometry* geometry); itkGetConstObjectMacro(InputWorldTimeGeometry, TimeGeometry); /** * \brief Access the created geometry */ itkGetConstObjectMacro(CreatedWorldGeometry, TimeGeometry); itkGetObjectMacro(CreatedWorldGeometry, TimeGeometry); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(AnatomicalPlane viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, AnatomicalPlane); itkGetEnumMacro(ViewDirection, AnatomicalPlane); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(AnatomicalPlane viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, AnatomicalPlane); itkGetEnumMacro(DefaultViewDirection, AnatomicalPlane); const char *GetViewDirectionAsString() const; virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(AnatomicalPlane viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); class MITKCORE_EXPORT TimeGeometryEvent : public itk::AnyEvent { public: typedef TimeGeometryEvent Self; typedef itk::AnyEvent Superclass; TimeGeometryEvent(TimeGeometry* aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} ~TimeGeometryEvent() override {} const char* GetEventName() const override { return "TimeGeometryEvent"; } bool CheckEvent(const ::itk::EventObject* e) const override { return dynamic_cast(e); } ::itk::EventObject* MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); } TimeGeometry* GetTimeGeometry() const { return m_TimeGeometry; } unsigned int GetPos() const { return m_Pos; } private: TimeGeometry::Pointer m_TimeGeometry; unsigned int m_Pos; // TimeGeometryEvent(const Self&); void operator=(const Self&); // just hide }; mitkTimeGeometryEventMacro(GeometrySendEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryUpdateEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometrySliceEvent, TimeGeometryEvent); template void ConnectGeometrySendEvent(T* receiver) { auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryUpdateEvent(T* receiver) { auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometrySliceEvent(T* receiver) { auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } // use a templated method to get the right offset when casting to void* template void Disconnect(T* receiver) { auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list& tags = i->second; for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } Message1 SetCrosshairEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ virtual void SetGeometry(const itk::EventObject& geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometrySlice(const itk::EventObject& geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometryTime(const itk::EventObject& geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ void SelectSliceByPoint(const Point3D& point); /** \brief Returns the BaseGeometry of the currently selected time step. */ const BaseGeometry* GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * BaseGeometry (if existent). */ const PlaneGeometry* GetCurrentPlaneGeometry(); /** \brief Sets / gets the BaseRenderer associated with this SNC (if any). * While the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. */ itkSetObjectMacro(Renderer, BaseRenderer); itkGetMacro(Renderer, BaseRenderer*); /** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector. The given point (world coordinates) defines the selected slice. Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use ReorientSlices (const Point3D &point, const Vector3D &axisVec0, const Vector3D &axisVec1). */ void ReorientSlices(const Point3D& point, const Vector3D& normal); /** \brief Re-orients the slice stack so that all planes are oriented according to the * given axis vectors. The given Point eventually defines selected slice. */ void ReorientSlices(const Point3D& point, const Vector3D& axisVec0, const Vector3D& axisVec1); void ExecuteOperation(Operation* operation) override; /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); protected: SliceNavigationController(); ~SliceNavigationController() override; void CreateWorldGeometry(bool top, bool frontside, bool rotated); TimeGeometry::ConstPointer m_InputWorldTimeGeometry; TimeGeometry::Pointer m_CreatedWorldGeometry; AnatomicalPlane m_ViewDirection; AnatomicalPlane m_DefaultViewDirection; RenderingManager::Pointer m_RenderingManager; BaseRenderer* m_Renderer; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; typedef std::map> ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTimeNavigationController.h b/Modules/Core/include/mitkTimeNavigationController.h index 69d81ccd18..16128e2106 100644 --- a/Modules/Core/include/mitkTimeNavigationController.h +++ b/Modules/Core/include/mitkTimeNavigationController.h @@ -1,172 +1,172 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef TimeNavigationController_h #define TimeNavigationController_h #include #include #include #include namespace mitk { /** * \brief Controls the time-related properties of the time stepper, according to the * given input world time geomtry. * * A TimeNavigationController takes a TimeGeometry as input world time geometry * and sets the properties of the associated stepper (BaseController). * The time-related properties are: * - steps: the number of input time steps * - pos: the current time step / position of the stepper * - range: the time bounds for the slider (minimum, maximum) * * The timestep controls the selection of a specific time point from the TimeGeometry. * The TimeNavigationController generates ITK events to tell observers, * like a BaseRenderer, when the selected timestep changes. * * Example: * \code * // Initialize the time navigation controller. * timeCtrl = mitk::TimeNavigationController::New(); * * // Set the required input world time geomtry (a TimeGeometry::ConstPointer). * timeCtrl->SetInputWorldTimeGeometry(geometry.GetPointer()); * * // Set the time-related properties and send the information to the connected renderer(s). * timeCtrl->Update(); * \endcode * * Vvisible navigation widgets can be connected to a TimeNavigationController, e.g., a * QmitkSliceNavigationWidget (for Qt): * * \code * // Create the visible navigation widget (a slider with a spin-box). * QmitkSliceNavigationWidget* navigationWidget = new QmitkSliceNavigationWidget(parent); * * // Connect the navigation widget to the time-stepper of the * // TimeNavigationController. For initialization (timestep, minimal and * // maximal values) the values of the TimeNavigationController are used. * // Thus, accessing methods of a navigation widget is normally not necessary, * // since everything can be set via the (Qt-independent) TimeNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. - * new QmitkStepperAdapter(navigationWidget, timeCtrl->GetSlice()); + * new QmitkStepperAdapter(navigationWidget, timeCtrl->GetStepper()); * \endcode */ class MITKCORE_EXPORT TimeNavigationController : public BaseController { public: mitkClassMacro(TimeNavigationController, BaseController); itkNewMacro(Self); /** * \brief Set the input time geometry out of which the * time-related properties will be generated. * * Any previous set input geometry (3D or Time) will * be ignored in the future. */ void SetInputWorldTimeGeometry(const TimeGeometry* geometry); itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry); /** * \brief Do the actual time-related properties extraction and send the currently selected time step to the connecte observers (renderers). */ virtual void Update(); /** * \brief Send the currently selected time step to the connected * observers (renderers). * * Called by Update(). */ virtual void SendTime(); class MITKCORE_EXPORT TimeEvent : public itk::AnyEvent { public: typedef TimeEvent Self; typedef itk::AnyEvent Superclass; TimeEvent(TimeStepType timeStep) : m_TimeStep(timeStep) {} ~TimeEvent() override {} const char *GetEventName() const override { return "TimeEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_TimeStep); } TimeStepType GetTimeStep() const { return m_TimeStep; } private: TimeStepType m_TimeStep; void operator=(const Self &); }; template void ConnectTimeEvent(T* receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(TimeEvent(0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } // use a templated method to get the right offset when casting to void* template void Disconnect(T* receiver) { auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list& tags = i->second; for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } /** * \brief Convenience method that returns the time step currently selected by the controller. */ TimeStepType GetSelectedTimeStep() const; /** * \brief Convenience method that returns the time point that corresponds to the selected * time step. The conversion is done using the time geometry of the controller. * If the time geometry is not yet set, this function will always return 0.0. */ TimePointType GetSelectedTimePoint() const; protected: TimeNavigationController(); ~TimeNavigationController() override; TimeGeometry::ConstPointer m_InputWorldTimeGeometry; bool m_BlockUpdate; typedef std::map> ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif diff --git a/Modules/Core/src/Controllers/mitkBaseController.cpp b/Modules/Core/src/Controllers/mitkBaseController.cpp index 135ac0763f..89342a64a9 100644 --- a/Modules/Core/src/Controllers/mitkBaseController.cpp +++ b/Modules/Core/src/Controllers/mitkBaseController.cpp @@ -1,48 +1,39 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBaseController.h" #include "mitkBaseRenderer.h" -mitk::BaseController::BaseController() : m_LastUpdateTime(0) +mitk::BaseController::BaseController() + : m_LastUpdateTime(0) { - m_Slice = Stepper::New(); - m_Time = Stepper::New(); + m_Stepper = Stepper::New(); } mitk::BaseController::~BaseController() { } void mitk::BaseController::ExecuteOperation(mitk::Operation * /* *operation */) { } -mitk::Stepper *mitk::BaseController::GetSlice() +mitk::Stepper *mitk::BaseController::GetStepper() { - return m_Slice.GetPointer(); + return m_Stepper.GetPointer(); } -const mitk::Stepper* mitk::BaseController::GetSlice() const +const mitk::Stepper* mitk::BaseController::GetStepper() const { - return m_Slice.GetPointer(); + return m_Stepper.GetPointer(); } -mitk::Stepper *mitk::BaseController::GetTime() -{ - return m_Time.GetPointer(); -} - -const mitk::Stepper* mitk::BaseController::GetTime() const -{ - return m_Time.GetPointer(); -} diff --git a/Modules/Core/src/Controllers/mitkCameraRotationController.cpp b/Modules/Core/src/Controllers/mitkCameraRotationController.cpp index f6c94adbc5..a542ae61ec 100644 --- a/Modules/Core/src/Controllers/mitkCameraRotationController.cpp +++ b/Modules/Core/src/Controllers/mitkCameraRotationController.cpp @@ -1,89 +1,89 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCameraRotationController.h" #include #include #include #include #include "mitkRenderingManager.h" #include "mitkVtkPropRenderer.h" mitk::CameraRotationController::CameraRotationController() : BaseController(), m_LastStepperValue(180), m_Camera(nullptr), m_RenderWindow(nullptr) { - m_Slice->SetAutoRepeat(true); - m_Slice->SetSteps(360); - m_Slice->SetPos(180); + m_Stepper->SetAutoRepeat(true); + m_Stepper->SetSteps(360); + m_Stepper->SetPos(180); itk::SimpleMemberCommand::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = itk::SimpleMemberCommand::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &CameraRotationController::RotateCamera); - m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); + m_Stepper->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); } mitk::CameraRotationController::~CameraRotationController() { } void mitk::CameraRotationController::RotateCamera() { if (!m_Camera) { this->AcquireCamera(); } if (m_Camera) { - int newStepperValue = m_Slice->GetPos(); + int newStepperValue = m_Stepper->GetPos(); m_Camera->Azimuth(m_LastStepperValue - newStepperValue); m_LastStepperValue = newStepperValue; // const_cast< RenderWindow* >(m_RenderWindow)->RequestUpdate(); // TODO does not work with movie generator! mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow); // m_MultiWidget->RequestUpdate(); } } void mitk::CameraRotationController::AcquireCamera() { BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(m_RenderWindow); const auto *propRenderer = dynamic_cast(renderer); if (propRenderer) { // get vtk renderer vtkRenderer *vtkrenderer = propRenderer->GetVtkRenderer(); if (vtkrenderer) { // get vtk camera vtkCamera *vtkcam = vtkrenderer->GetActiveCamera(); if (vtkcam) { // vtk smart pointer handling if (!m_Camera) { m_Camera = vtkcam; m_Camera->Register(nullptr); } else { m_Camera->UnRegister(nullptr); m_Camera = vtkcam; m_Camera->Register(nullptr); } } } } } diff --git a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp index 2a5d497f4c..527d8dbaf9 100644 --- a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp +++ b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp @@ -1,167 +1,168 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCrosshairManager.h" #include #include #include mitk::CrosshairManager::CrosshairManager(BaseRenderer* baseRenderer) { m_CrosshairDataNode = mitk::DataNode::New(); std::string rendererName = std::string(baseRenderer->GetName()); m_CrosshairDataNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CrosshairDataNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(5)); m_CrosshairDataNode->SetProperty("Crosshair.Gap Size", mitk::IntProperty::New(32)); // set the crosshair only visible for this specific renderer m_CrosshairDataNode->SetVisibility(false); m_CrosshairDataNode->SetVisibility(true, baseRenderer); m_CrosshairDataNode->SetProperty("name", mitk::StringProperty::New(std::string(rendererName + "crosshairData"))); m_CrosshairDataNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_CrosshairData = CrosshairData::New(); m_CrosshairDataNode->SetData(m_CrosshairData); m_CrosshairDataNode->SetMapper(mitk::BaseRenderer::Standard2D, mitk::CrosshairVtkMapper2D::New()); } mitk::CrosshairManager::~CrosshairManager() { } void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint) { if (m_CrosshairData.IsNull()) { mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } m_CrosshairData->SetPosition(selectedPoint); } void mitk::CrosshairManager::UpdateCrosshairPosition(const SliceNavigationController* sliceNavigationController) { if (m_CrosshairData.IsNull()) { mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } Point3D crosshairPosition = m_CrosshairData->GetPosition(); auto viewDirection = sliceNavigationController->GetViewDirection(); - unsigned int slicePosition = sliceNavigationController->GetSlice()->GetPos(); + unsigned int slicePosition = sliceNavigationController->GetStepper()->GetPos(); + switch (viewDirection) { case AnatomicalPlane::Original: return; case AnatomicalPlane::Axial: { crosshairPosition[2] = slicePosition; break; } case AnatomicalPlane::Coronal: { crosshairPosition[1] = slicePosition; break; } case AnatomicalPlane::Sagittal: { crosshairPosition[0] = slicePosition; break; } } m_CrosshairData->SetPosition(crosshairPosition); } mitk::Point3D mitk::CrosshairManager::GetCrosshairPosition() const { if (m_CrosshairData.IsNull()) { mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } return m_CrosshairData->GetPosition(); } void mitk::CrosshairManager::SetCrosshairVisibility(bool visible, const BaseRenderer* baseRenderer) { if (m_CrosshairDataNode.IsNull()) { mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } m_CrosshairDataNode->SetVisibility(visible, baseRenderer); } bool mitk::CrosshairManager::GetCrosshairVisibility(const BaseRenderer* baseRenderer) const { if (m_CrosshairDataNode.IsNull()) { mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } bool visibility = false; if (m_CrosshairDataNode->GetVisibility(visibility, baseRenderer)) { return false; } return visibility; } void mitk::CrosshairManager::SetCrosshairGap(unsigned int gapSize) { if (m_CrosshairDataNode.IsNull()) { mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } m_CrosshairDataNode->SetIntProperty("Crosshair.Gap Size", gapSize); } void mitk::CrosshairManager::AddCrosshairNodeToDataStorage(DataStorage* dataStorage) { if (m_CrosshairDataNode.IsNull()) { mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } if (nullptr == dataStorage) { mitkThrow() << "Datastorage is invalid. Cannot add crosshair node."; } if (!dataStorage->Exists(m_CrosshairDataNode)) { dataStorage->Add(m_CrosshairDataNode); } } void mitk::CrosshairManager::RemoveCrosshairNodeFromDataStorage(DataStorage* dataStorage) { if (m_CrosshairDataNode.IsNull()) { mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } if (nullptr == dataStorage) { mitkThrow() << "Datastorage is invalid. Cannot remove crosshair node."; } dataStorage->Remove(m_CrosshairDataNode); } diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index 5df02a1a3e..6d5fb036c0 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,791 +1,791 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { itkEventMacroDefinition(RenderingManagerEvent, itk::AnyEvent); itkEventMacroDefinition(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent); RenderingManager::Pointer RenderingManager::s_Instance = nullptr; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr; RenderingManager::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(nullptr), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr), m_AntiAliasing(AntiAliasing::FastApproximate) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(nullptr); auto callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager::New() { const RenderingManagerFactory *factory = GetFactory(); if (factory == nullptr) return nullptr; return factory->CreateRenderingManager(); } RenderingManager *RenderingManager::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(nullptr); // Add callbacks for rendering abort mechanism // BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; // Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { auto callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } auto rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(nullptr); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertently create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertently create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { // prepare the camera etc. before rendering // Note: this is a very important step which should be called before the VTK render! // If you modify the camera anywhere else or after the render call, the scene cannot be seen. auto *vPR = dynamic_cast(BaseRenderer::GetInstance(renderWindow)); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage* dataStorage) { if (nullptr == dataStorage) { return; } // get all nodes that have not set "includeInBoundingBox" to false auto pred = NodePredicateNot::New(NodePredicateProperty::New("includeInBoundingBox", BoolProperty::New(false))); DataStorage::SetOfObjects::ConstPointer filteredNodes = dataStorage->GetSubset(pred); TimeGeometry::ConstPointer boundingGeometry; if (!filteredNodes->empty()) { // calculate bounding geometry of these nodes boundingGeometry = dataStorage->ComputeBoundingGeometry3D(filteredNodes, "visible"); } // initialize the views to the bounding geometry this->InitializeViews(boundingGeometry); } void RenderingManager::InitializeViewByBoundingObjects(vtkRenderWindow* renderWindow, const DataStorage* dataStorage, bool resetCamera) { if (nullptr == dataStorage) { return; } // get all nodes that have not set "includeInBoundingBox" to false auto pred = NodePredicateNot::New(NodePredicateProperty::New("includeInBoundingBox", BoolProperty::New(false))); DataStorage::SetOfObjects::ConstPointer filteredNodes = dataStorage->GetSubset(pred); BaseRenderer* baseRenderer = BaseRenderer::GetInstance(renderWindow); TimeGeometry::ConstPointer boundingGeometry; if (!filteredNodes->empty()) { // calculate bounding geometry of these nodes boundingGeometry = dataStorage->ComputeBoundingGeometry3D(filteredNodes, "visible", baseRenderer); } // initialize the views to the bounding geometry this->InitializeView(renderWindow, boundingGeometry, resetCamera); } bool RenderingManager::InitializeViews(const BaseGeometry* geometry, RequestType type, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return this->InitializeViews(propTimeGeometry, type, resetCamera); } bool RenderingManager::InitializeViews(const TimeGeometry* geometry, RequestType type, bool resetCamera) { bool boundingBoxInitialized = false; TimeGeometry::Pointer modifiedGeometry = nullptr; try { boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry); } catch (Exception& exception) { mitkReThrow(exception); } RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows(); RenderWindowVector::const_iterator it; for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it); baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming()); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera); } } if (boundingBoxInitialized) { this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry); } this->GetTimeNavigationController()->Update(); this->RequestUpdateAll(type); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeViews(RequestType type) { const RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows(); RenderWindowVector::const_iterator it; for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->InternalViewInitialization(baseRenderer, nullptr, false, id, false); } } this->RequestUpdateAll(type); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return true; } bool RenderingManager::InitializeView(vtkRenderWindow* renderWindow, const BaseGeometry* geometry, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return this->InitializeView(renderWindow, propTimeGeometry, resetCamera); } bool RenderingManager::InitializeView(vtkRenderWindow* renderWindow, const TimeGeometry* geometry, bool resetCamera) { bool boundingBoxInitialized = false; TimeGeometry::Pointer modifiedGeometry = nullptr; try { boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry); } catch (Exception &exception) { mitkReThrow(exception); } BaseRenderer* baseRenderer = BaseRenderer::GetInstance(renderWindow); baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming()); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera); if (boundingBoxInitialized) { this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry); } this->GetTimeNavigationController()->Update(); this->RequestUpdate(renderWindow); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, nullptr, false, id, false); this->RequestUpdate(renderWindow); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return true; } void RenderingManager::InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera) { SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (resetCamera) { if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered in the image - nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); + nc->GetStepper()->SetPos(nc->GetStepper()->GetSteps() / 2); baseRenderer->GetCameraController()->Fit(); } else if (mapperID == BaseRenderer::Standard3D) { baseRenderer->GetCameraController()->SetViewToAnterior(); } } } else { nc->Update(); } } bool RenderingManager::ExtendGeometryForBoundingBox(const TimeGeometry *geometry, TimeGeometry::Pointer& modifiedGeometry) { bool boundingBoxInitialized = false; if (nullptr == geometry) { return boundingBoxInitialized; } modifiedGeometry = geometry->Clone(); if (modifiedGeometry.IsNull()) { return boundingBoxInitialized; } if (modifiedGeometry->GetBoundingBoxInWorld()->GetDiagonalLength2() > eps) { boundingBoxInitialized = true; } // make sure bounding box has an extent bigger than zero in any direction for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { // check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if (Equal( newBounds[2 * dimension], newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision { mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers " "beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } return boundingBoxInitialized; } const SliceNavigationController *RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController *RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) renderingManager->m_RenderWindowList[renderWindow] = RENDERING_INPROGRESS; renderingManager->m_UpdatePending = false; } void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); if (renderingManager->m_LODAbortMechanismEnabled) { auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) { auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr != renderer && 0 < renderer->GetNumberOfVisibleLODEnabledMappers()) renderingManager->DoMonitorRendering(); } } } void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderWindow = dynamic_cast(caller); if (nullptr == renderWindow) { return; } auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr == renderer) { return; } auto renderingManager = RenderingManager::GetInstance(); renderingManager->m_RenderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; if (0 < renderer->GetNumberOfVisibleLODEnabledMappers()) { if (0 == renderingManager->m_NextLODMap[renderer]) { renderingManager->StartOrResetTimer(); } else { renderingManager->m_NextLODMap[renderer] = 0; } } } bool RenderingManager::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager::GetNextLOD(BaseRenderer *renderer) { if (renderer != nullptr) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } // enable/disable shading void RenderingManager::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } // enable/disable the clipping plane void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage *storage) { if (storage != nullptr) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (focusWindow != m_FocusedRenderWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } } void RenderingManager::SetAntiAliasing(AntiAliasing antiAliasing) { if (m_AntiAliasing != antiAliasing) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindows = renderingManager->GetAllRegisteredRenderWindows(); for (auto renderWindow : renderWindows) { auto renderers = renderWindow->GetRenderers(); if (nullptr != renderers) { renderers->InitTraversal(); auto renderer = renderers->GetNextItem(); while (nullptr != renderer) { renderer->SetUseFXAA(AntiAliasing::FastApproximate == antiAliasing); renderer = renderers->GetNextItem(); } renderingManager->RequestUpdate(renderWindow); } } m_AntiAliasing = antiAliasing; } } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index 465c4e81dc..e7255908f5 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,486 +1,484 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { SliceNavigationController::SliceNavigationController() : BaseController() , m_InputWorldTimeGeometry(TimeGeometry::ConstPointer()) , m_CreatedWorldGeometry(TimeGeometry::Pointer()) , m_ViewDirection(AnatomicalPlane::Axial) , m_DefaultViewDirection(AnatomicalPlane::Axial) , m_RenderingManager(RenderingManager::Pointer()) , m_Renderer(nullptr) , m_BlockUpdate(false) , m_SliceLocked(false) , m_SliceRotationLocked(false) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); - sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); - m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); - - m_Slice->SetUnitName("mm"); + m_Stepper->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); + m_Stepper->SetUnitName("mm"); } SliceNavigationController::~SliceNavigationController() { // nothing here } void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry* geometry) { if (nullptr != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; this->Modified(); } } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char* SliceNavigationController::GetViewDirectionAsString() const { const char* viewDirectionString; switch (m_ViewDirection) { case AnatomicalPlane::Axial: viewDirectionString = "Axial"; break; case AnatomicalPlane::Sagittal: viewDirectionString = "Sagittal"; break; case AnatomicalPlane::Coronal: viewDirectionString = "Coronal"; break; case AnatomicalPlane::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No view direction available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == AnatomicalPlane::Sagittal) { this->Update(AnatomicalPlane::Sagittal, true, true, false); } else if (m_ViewDirection == AnatomicalPlane::Coronal) { this->Update(AnatomicalPlane::Coronal, false, true, false); } else if (m_ViewDirection == AnatomicalPlane::Axial) { this->Update(AnatomicalPlane::Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(AnatomicalPlane viewDirection, bool top, bool frontside, bool rotated) { if (m_BlockUpdate) { return; } if (m_InputWorldTimeGeometry.IsNull()) { return; } if (0 == m_InputWorldTimeGeometry->CountTimeSteps()) { return; } m_BlockUpdate = true; if (m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } this->SetViewDirection(viewDirection); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); this->CreateWorldGeometry(top, frontside, rotated); } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe - // Update() was only called to re-send the old geometry and time/slice data. + // Update() was only called to re-send the old geometry and slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { - this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); + this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Stepper->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { - this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); + this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Stepper->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry(const itk::EventObject&) { // not implemented } void SliceNavigationController::SetGeometrySlice(const itk::EventObject& geometrySliceEvent) { const auto* sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent != nullptr); - this->GetSlice()->SetPos(sliceEvent->GetPos()); + m_Stepper->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint(const Point3D& point) { if (m_CreatedWorldGeometry.IsNull()) { return; } int selectedSlice = -1; try { selectedSlice = SliceNavigationHelper::SelectSliceByPoint(m_CreatedWorldGeometry, point); } catch (const mitk::Exception& e) { MITK_ERROR << "Unable to select a slice by the given point " << point << "\n" << "Reason: " << e.GetDescription(); } if (-1 == selectedSlice) { return; } - this->GetSlice()->SetPos(selectedSlice); + m_Stepper->SetPos(selectedSlice); this->SendCreatedWorldGeometryUpdate(); // send crosshair event SetCrosshairEvent.Send(point); } void SliceNavigationController::ReorientSlices(const Point3D& point, const Vector3D& normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const Point3D& point, const Vector3D& axisVec0, const Vector3D& axisVec1) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } const BaseGeometry* SliceNavigationController::GetCurrentGeometry3D() { if (m_CreatedWorldGeometry.IsNull()) { return nullptr; } auto timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); if (nullptr == timeNavigationController) { return nullptr; } TimeStepType selectedTimestep = timeNavigationController->GetSelectedTimeStep(); return m_CreatedWorldGeometry->GetGeometryForTimeStep(selectedTimestep); } const PlaneGeometry* SliceNavigationController::GetCurrentPlaneGeometry() { const auto* slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); if (nullptr == slicedGeometry) { return nullptr; } - return slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos()); + return slicedGeometry->GetPlaneGeometry(m_Stepper->GetPos()); } void SliceNavigationController::AdjustSliceStepperRange() { const auto* slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); const Vector3D& direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); - m_Slice->SetRange(min, max); + m_Stepper->SetRange(min, max); } else { - m_Slice->InvalidateRange(); + m_Stepper->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation(Operation* operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice auto* po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { - this->GetSlice()->SetPos(po->GetIndex()); + m_Stepper->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } void SliceNavigationController::CreateWorldGeometry(bool top, bool frontside, bool rotated) { auto timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); if (nullptr == timeNavigationController) { return; } // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry; BaseGeometry::ConstPointer currentGeometry; // get the BaseGeometry (ArbitraryTimeGeometry or ProportionalTimeGeometry) of the current time step TimeStepType selectedTimestep = timeNavigationController->GetSelectedTimeStep(); if (m_InputWorldTimeGeometry->IsValidTimeStep(selectedTimestep)) { currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(selectedTimestep); } else { currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); } if (AnatomicalPlane::Original == m_ViewDirection) { slicedWorldGeometry = dynamic_cast( m_InputWorldTimeGeometry->GetGeometryForTimeStep(selectedTimestep).GetPointer()); if (slicedWorldGeometry.IsNull()) { slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, AnatomicalPlane::Original, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); } } else { slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, m_ViewDirection, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); } // reset the stepper - m_Slice->SetSteps(slicedWorldGeometry->GetSlices()); - m_Slice->SetPos(0); + m_Stepper->SetSteps(slicedWorldGeometry->GetSlices()); + m_Stepper->SetPos(0); TimeStepType inputTimeSteps = m_InputWorldTimeGeometry->CountTimeSteps(); // create new time geometry and initialize it according to the type of the 'm_InputWorldTimeGeometry' // the created world geometry will either have equidistant timesteps (ProportionalTimeGeometry) // or individual time bounds for each timestep (ArbitraryTimeGeometry) m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); if (nullptr != dynamic_cast(m_InputWorldTimeGeometry.GetPointer())) { const TimePointType minimumTimePoint = m_InputWorldTimeGeometry->TimeStepToTimePoint(0); const TimePointType stepDuration = m_InputWorldTimeGeometry->TimeStepToTimePoint(1) - minimumTimePoint; auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize(slicedWorldGeometry, inputTimeSteps); createdTimeGeometry->SetFirstTimePoint(minimumTimePoint); createdTimeGeometry->SetStepDuration(stepDuration); m_CreatedWorldGeometry = createdTimeGeometry; } else { auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); createdTimeGeometry->ReserveSpaceForGeometries(inputTimeSteps); const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone(); for (TimeStepType i = 0; i < inputTimeSteps; ++i) { const auto bounds = m_InputWorldTimeGeometry->GetTimeBounds(i); createdTimeGeometry->AppendNewTimeStep(clonedGeometry, bounds[0], bounds[1]); } createdTimeGeometry->Update(); m_CreatedWorldGeometry = createdTimeGeometry; } } } // namespace mitk diff --git a/Modules/Core/src/Controllers/mitkTimeNavigationController.cpp b/Modules/Core/src/Controllers/mitkTimeNavigationController.cpp index 30189059e3..4766eb9732 100644 --- a/Modules/Core/src/Controllers/mitkTimeNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkTimeNavigationController.cpp @@ -1,129 +1,129 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include mitk::TimeNavigationController::TimeNavigationController() : BaseController() , m_InputWorldTimeGeometry(mitk::TimeGeometry::ConstPointer()) , m_BlockUpdate(false) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer timeStepperChangedCommand; timeStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand->SetCallbackFunction(this, &TimeNavigationController::SendTime); - m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); - m_Time->SetUnitName("ms"); + m_Stepper->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); + m_Stepper->SetUnitName("ms"); } mitk::TimeNavigationController::~TimeNavigationController() {} void mitk::TimeNavigationController::SetInputWorldTimeGeometry(const TimeGeometry* geometry) { if (geometry != nullptr) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; this->Modified(); } } void mitk::TimeNavigationController::Update() { if (m_BlockUpdate) { return; } if (m_InputWorldTimeGeometry.IsNull()) { return; } if (0 == m_InputWorldTimeGeometry->CountTimeSteps()) { return; } m_BlockUpdate = true; if (m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); TimeStepType inputTimeSteps = m_InputWorldTimeGeometry->CountTimeSteps(); const TimeBounds &timeBounds = m_InputWorldTimeGeometry->GetTimeBounds(); - m_Time->SetSteps(inputTimeSteps); - m_Time->SetPos(0); - m_Time->SetRange(timeBounds[0], timeBounds[1]); + m_Stepper->SetSteps(inputTimeSteps); + m_Stepper->SetPos(0); + m_Stepper->SetRange(timeBounds[0], timeBounds[1]); } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the time. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old time data. this->SendTime(); } void mitk::TimeNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_InputWorldTimeGeometry.IsNotNull()) { - this->InvokeEvent(TimeEvent(m_Time->GetPos())); + this->InvokeEvent(TimeEvent(m_Stepper->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } mitk::TimeStepType mitk::TimeNavigationController::GetSelectedTimeStep() const { - return this->GetTime()->GetPos(); + return m_Stepper->GetPos(); } mitk::TimePointType mitk::TimeNavigationController::GetSelectedTimePoint() const { const auto timeStep = this->GetSelectedTimeStep(); if (m_InputWorldTimeGeometry.IsNull()) { return 0.0; } if (!m_InputWorldTimeGeometry->IsValidTimeStep(timeStep)) { mitkThrow() << "SliceNavigationController is in an invalid state. It has a time step" << "selected that is not covered by its time geometry. Selected time step: " << timeStep << "; TimeGeometry steps count: " << m_InputWorldTimeGeometry->CountTimeSteps(); } return m_InputWorldTimeGeometry->TimeStepToTimePoint(timeStep); } diff --git a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp index be03c44fd6..6e23ef32d9 100644 --- a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,962 +1,962 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkSlicedGeometry3D.h" #include "mitkAbstractTransformGeometry.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkInteractionConst.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkSliceNavigationController.h" const mitk::ScalarType PI = 3.14159265359; mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced(true), m_Slices(0), m_ReferenceGeometry(nullptr), m_SliceNavigationController(nullptr) { m_DirectionVector.Fill(0); this->InitializeSlicedGeometry(m_Slices); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D &other) : Superclass(other), m_EvenlySpaced(other.m_EvenlySpaced), m_Slices(other.m_Slices), m_ReferenceGeometry(other.m_ReferenceGeometry), m_SliceNavigationController(other.m_SliceNavigationController) { m_DirectionVector.Fill(0); SetSpacing(other.GetSpacing()); SetDirectionVector(other.GetDirectionVector()); if (m_EvenlySpaced) { assert(!other.m_PlaneGeometries.empty() && "This may happen when you use one of the old Initialize methods, which had a bool parameter that is implicitly casted to the number of slices now."); PlaneGeometry::Pointer geometry = other.m_PlaneGeometries[0]->Clone(); assert(geometry.IsNotNull()); SetPlaneGeometry(geometry, 0); } else { unsigned int s; for (s = 0; s < other.m_Slices; ++s) { if (other.m_PlaneGeometries[s].IsNull()) { assert(other.m_EvenlySpaced); m_PlaneGeometries[s] = nullptr; } else { PlaneGeometry *geometry2D = other.m_PlaneGeometries[s]->Clone(); assert(geometry2D != nullptr); SetPlaneGeometry(geometry2D, s); } } } } mitk::SlicedGeometry3D::~SlicedGeometry3D() { } mitk::PlaneGeometry *mitk::SlicedGeometry3D::GetPlaneGeometry(int s) const { mitk::PlaneGeometry::Pointer geometry2D = nullptr; if (this->IsValidSlice(s)) { geometry2D = m_PlaneGeometries[s]; // If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored // for the requested slice, and (c) the first slice (s=0) // is a PlaneGeometry instance, then we calculate the geometry of the // requested as the plane of the first slice shifted by m_Spacing[2]*s // in the direction of m_DirectionVector. if ((m_EvenlySpaced) && (geometry2D.IsNull())) { PlaneGeometry *firstSlice = m_PlaneGeometries[0]; if (firstSlice != nullptr && dynamic_cast(m_PlaneGeometries[0].GetPointer()) == nullptr) { if ((m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0)) { m_DirectionVector = firstSlice->GetNormal(); m_DirectionVector.Normalize(); } Vector3D direction; direction = m_DirectionVector * this->GetSpacing()[2]; mitk::PlaneGeometry::Pointer requestedslice; requestedslice = static_cast(firstSlice->Clone().GetPointer()); requestedslice->SetOrigin(requestedslice->GetOrigin() + direction * s); geometry2D = requestedslice; m_PlaneGeometries[s] = geometry2D; } } return geometry2D; } else { return nullptr; } } const mitk::BoundingBox *mitk::SlicedGeometry3D::GetBoundingBox() const { assert(this->IsBoundingBoxNull() == false); return Superclass::GetBoundingBox(); } bool mitk::SlicedGeometry3D::SetPlaneGeometry(mitk::PlaneGeometry *geometry2D, int s) { if (this->IsValidSlice(s)) { m_PlaneGeometries[s] = geometry2D; m_PlaneGeometries[s]->SetReferenceGeometry(m_ReferenceGeometry); return true; } return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry(unsigned int slices) { Superclass::Initialize(); m_Slices = slices; PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); Vector3D spacing; spacing.Fill(1.0); this->SetSpacing(spacing); m_DirectionVector.Fill(0); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices) { assert(geometry2D != nullptr); this->InitializeEvenlySpaced(geometry2D, geometry2D->GetExtentInMM(2) / geometry2D->GetExtent(2), slices); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, mitk::ScalarType zSpacing, unsigned int slices) { assert(geometry2D != nullptr); assert(geometry2D->GetExtent(0) > 0); assert(geometry2D->GetExtent(1) > 0); geometry2D->Register(); Superclass::Initialize(); m_Slices = slices; BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); bounds[4] = 0; bounds[5] = slices; // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); Vector3D directionVector = geometry2D->GetAxisVector(2); directionVector.Normalize(); directionVector *= zSpacing; // Normally we should use the following four lines to create a copy of // the transform contained in geometry2D, because it may not be changed // by us. But we know that SetSpacing creates a new transform without // changing the old (coming from geometry2D), so we can use the fifth // line instead. We check this at (**). // // AffineTransform3D::Pointer transform = AffineTransform3D::New(); // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); // SetIndexToWorldTransform(transform); this->SetIndexToWorldTransform(geometry2D->GetIndexToWorldTransform()); mitk::Vector3D spacing; FillVector3D(spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing); this->SetDirectionVector(directionVector); this->SetBounds(bounds); this->SetPlaneGeometry(geometry2D, 0); this->SetSpacing(spacing, true); this->SetEvenlySpaced(); // this->SetTimeBounds( geometry2D->GetTimeBounds() ); assert(this->GetIndexToWorldTransform() != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID(geometry2D->GetFrameOfReferenceID()); this->SetImageGeometry(geometry2D->GetImageGeometry()); geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes(const mitk::BaseGeometry *geometry3D, mitk::AnatomicalPlane orientation, bool top, bool frontside, bool rotated) { m_ReferenceGeometry = geometry3D; PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(geometry3D, top, orientation, frontside, rotated); int worldAxis = orientation == AnatomicalPlane::Sagittal ? 0 : orientation == AnatomicalPlane::Coronal ? 1 : 2; // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetTranspose(); int dominantAxis = planeGeometry->CalculateDominantAxes(inverseMatrix).at(worldAxis); ScalarType viewSpacing = geometry3D->GetSpacing()[dominantAxis]; /// Although the double value returned by GetExtent() holds a round number, /// you need to add 0.5 to safely convert it to unsigned it. I have seen a /// case when the result was less by one without this. auto slices = static_cast(geometry3D->GetExtent(dominantAxis) + 0.5); if ( slices == 0 && geometry3D->GetExtent(dominantAxis) > 0) { // require at least one slice if there is _some_ extent slices = 1; } #ifndef NDEBUG int upDirection = itk::Function::Sign(inverseMatrix[dominantAxis][worldAxis]); /// The normal vector of an imaginary plane that points from the world origin (bottom left back /// corner or the world, with the lowest physical coordinates) towards the inside of the volume, /// along the renderer axis. Length is the slice thickness. Vector3D worldPlaneNormal = inverseMatrix.get_row(dominantAxis) * (upDirection * viewSpacing); /// The normal of the standard plane geometry just created. Vector3D standardPlaneNormal = planeGeometry->GetNormal(); /// The standard plane must be parallel to the 'world plane'. The normal of the standard plane /// must point against the world plane if and only if 'top' is 'false'. The length of the /// standard plane normal must be equal to the slice thickness. assert((standardPlaneNormal - (top ? 1.0 : -1.0) * worldPlaneNormal).GetSquaredNorm() < 0.000001); #endif this->InitializeEvenlySpaced(planeGeometry, viewSpacing, slices); #ifndef NDEBUG /// The standard plane normal and the z axis vector of the sliced geometry must point in /// the same direction. Vector3D zAxisVector = this->GetAxisVector(2); Vector3D upscaledStandardPlaneNormal = standardPlaneNormal; upscaledStandardPlaneNormal *= slices; assert((zAxisVector - upscaledStandardPlaneNormal).GetSquaredNorm() < 0.000001); /// You can use this test is to check the handedness of the coordinate system of the current /// geometry. In principle, you can use either left- or right-handed coordinate systems, but /// you normally want it to be consistent, that is the handedness should be the same across /// the renderers of the same viewer. // ScalarType det = vnl_det(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix()); // MITK_DEBUG << "world axis: " << worldAxis << (det > 0 ? " ; right-handed" : " ; left-handed"); #endif } void mitk::SlicedGeometry3D::ReinitializePlanes(const Point3D ¢er, const Point3D &referencePoint) { // Need a reference frame to align the rotated planes if (!m_ReferenceGeometry) { return; } // Get first plane of plane stack PlaneGeometry *firstPlane = m_PlaneGeometries[0]; // If plane stack is empty, exit if (!firstPlane || dynamic_cast(firstPlane)) { return; } // Calculate the "directed" spacing when taking the plane (defined by its axes // vectors and normal) as the reference coordinate frame. // // This is done by calculating the radius of the ellipsoid defined by the // original volume spacing axes, in the direction of the respective axis of the // reference frame. mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); mitk::Vector3D normal = firstPlane->GetNormal(); normal.Normalize(); Vector3D spacing; spacing[0] = this->CalculateSpacing(axis0); spacing[1] = this->CalculateSpacing(axis1); spacing[2] = this->CalculateSpacing(normal); Superclass::SetSpacing(spacing); // Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above. ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * normal[0]) + std::abs(m_ReferenceGeometry->GetExtentInMM(1) * normal[1]) + std::abs(m_ReferenceGeometry->GetExtentInMM(2) * normal[2]); if (directedExtent >= spacing[2]) { m_Slices = static_cast(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } // The origin of our "first plane" needs to be adapted to this new extent. // To achieve this, we first calculate the current distance to the volume's // center, and then shift the origin in the direction of the normal by the // difference between this distance and half of the new extent. double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane(center); if (centerOfRotationDistance > 0) { firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0)); m_DirectionVector = normal; } else { firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance)); m_DirectionVector = -normal; } // Now we adjust this distance according with respect to the given reference // point: we need to make sure that the point is touched by one slice of the // new slice stack. double referencePointDistance = firstPlane->SignedDistanceFromPlane(referencePoint); auto referencePointSlice = static_cast(referencePointDistance / spacing[2]); double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice; firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * alignmentValue * spacing[2]); // Finally, we can clear the previous geometry stack and initialize it with // our re-initialized "first plane". m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr)); if (m_Slices > 0) { m_PlaneGeometries[0] = firstPlane; } // Reinitialize SNC with new number of slices - m_SliceNavigationController->GetSlice()->SetSteps(m_Slices); + m_SliceNavigationController->GetStepper()->SetSteps(m_Slices); this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing(const mitk::Vector3D &d) const { // Need the spacing of the underlying dataset / geometry if (!m_ReferenceGeometry) { return 1.0; } const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); return SlicedGeometry3D::CalculateSpacing(spacing, d); } double mitk::SlicedGeometry3D::CalculateSpacing(const mitk::Vector3D &spacing, const mitk::Vector3D &d) { // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // double scaling = d[0] * d[0] / (spacing[0] * spacing[0]) + d[1] * d[1] / (spacing[1] * spacing[1]) + d[2] * d[2] / (spacing[2] * spacing[2]); scaling = sqrt(scaling); return (sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) / scaling); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal(const mitk::Vector3D &normal) const { TransformType::Pointer inverse = TransformType::New(); m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse(inverse); Vector3D transformedNormal = inverse->TransformVector(normal); transformedNormal.Normalize(); return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry(const bool isAnImageGeometry) { Superclass::SetImageGeometry(isAnImageGeometry); unsigned int s; for (s = 0; s < m_Slices; ++s) { mitk::BaseGeometry *geometry = m_PlaneGeometries[s]; if (geometry != nullptr) { geometry->SetImageGeometry(isAnImageGeometry); } } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { unsigned int s; for (s = 0; s < m_Slices; ++s) { mitk::BaseGeometry *geometry = m_PlaneGeometries[s]; if (geometry != nullptr) { geometry->ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } } Superclass::ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } bool mitk::SlicedGeometry3D::IsValidSlice(int s) const { return ((s >= 0) && (s < (int)m_Slices)); } const mitk::BaseGeometry *mitk::SlicedGeometry3D::GetReferenceGeometry() const { return m_ReferenceGeometry; } void mitk::SlicedGeometry3D::SetReferenceGeometry(const BaseGeometry *referenceGeometry) { m_ReferenceGeometry = referenceGeometry; std::vector::iterator it; for (it = m_PlaneGeometries.begin(); it != m_PlaneGeometries.end(); ++it) { (*it)->SetReferenceGeometry(referenceGeometry); } } bool mitk::SlicedGeometry3D::HasReferenceGeometry() const { return ( m_ReferenceGeometry != nullptr ); } void mitk::SlicedGeometry3D::PreSetSpacing(const mitk::Vector3D &aSpacing) { bool hasEvenlySpacedPlaneGeometry = false; mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; BoundingBox::BoundsArrayType bounds; // Check for valid spacing if (!(aSpacing[0] > 0 && aSpacing[1] > 0 && aSpacing[2] > 0)) { mitkThrow() << "You try to set a spacing with at least one element equal or " "smaller to \"0\". This might lead to a crash during rendering. Please double" " check your data!"; } // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { const PlaneGeometry *planeGeometry = m_PlaneGeometries[0]; if (planeGeometry && !dynamic_cast(planeGeometry)) { this->WorldToIndex(planeGeometry->GetOrigin(), origin); this->WorldToIndex(planeGeometry->GetAxisVector(0), rightDV); this->WorldToIndex(planeGeometry->GetAxisVector(1), bottomDV); bounds = planeGeometry->GetBounds(); hasEvenlySpacedPlaneGeometry = true; } } BaseGeometry::_SetSpacing(aSpacing); mitk::PlaneGeometry::Pointer firstGeometry; // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if (hasEvenlySpacedPlaneGeometry) { // create planeGeometry according to new spacing this->IndexToWorld(origin, origin); this->IndexToWorld(rightDV, rightDV); this->IndexToWorld(bottomDV, bottomDV); mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->SetImageGeometry(this->GetImageGeometry()); planeGeometry->SetReferenceGeometry(m_ReferenceGeometry); // Store spacing, as Initialize... needs a pointer mitk::Vector3D lokalSpacing = this->GetSpacing(); planeGeometry->InitializeStandardPlane(rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &lokalSpacing); planeGeometry->SetOrigin(origin); planeGeometry->SetBounds(bounds); firstGeometry = planeGeometry; } else if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { firstGeometry = m_PlaneGeometries[0].GetPointer(); } // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); if (m_Slices > 0) { m_PlaneGeometries[0] = firstGeometry; } this->Modified(); } void mitk::SlicedGeometry3D::SetSliceNavigationController(SliceNavigationController *snc) { m_SliceNavigationController = snc; } mitk::SliceNavigationController *mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if (m_EvenlySpaced != on) { m_EvenlySpaced = on; this->Modified(); } } void mitk::SlicedGeometry3D::SetDirectionVector(const mitk::Vector3D &directionVector) { Vector3D newDir = directionVector; newDir.Normalize(); if (newDir != m_DirectionVector) { m_DirectionVector = newDir; this->Modified(); } } // void // mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) //{ // Superclass::SetTimeBounds( timebounds ); // // unsigned int s; // for ( s = 0; s < m_Slices; ++s ) // { // if(m_Geometry2Ds[s].IsNotNull()) // { // m_Geometry2Ds[s]->SetTimeBounds( timebounds ); // } // } // m_TimeBounds = timebounds; //} itk::LightObject::Pointer mitk::SlicedGeometry3D::InternalClone() const { Self::Pointer newGeometry = new SlicedGeometry3D(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::SlicedGeometry3D::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; if (m_EvenlySpaced) { os << indent << " DirectionVector: " << m_DirectionVector << std::endl; } os << indent << " Slices: " << m_Slices << std::endl; os << std::endl; os << indent << " GetPlaneGeometry(0): "; if (this->GetPlaneGeometry(0) == nullptr) { os << "nullptr" << std::endl; } else { this->GetPlaneGeometry(0)->Print(os, indent); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation *operation) { PlaneGeometry::Pointer geometry2D; ApplyTransformMatrixOperation *applyMatrixOp; Point3D center; switch (operation->GetOperationType()) { case OpNOTHING: break; case OpROTATE: if (m_EvenlySpaced) { // Need a reference frame to align the rotation if (m_ReferenceGeometry) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Save first slice PlaneGeometry::Pointer geometry2D = m_PlaneGeometries[0]; auto *rotOp = dynamic_cast(operation); // Generate a RotationOperation using the dataset center instead of // the supplied rotation center. This is necessary so that the rotated // zero-plane does not shift away. The supplied center is instead used // to adjust the slice stack afterwards. Point3D center = m_ReferenceGeometry->GetCenter(); RotationOperation centeredRotation( rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation()); // Rotate first slice geometry2D->ExecuteOperation(¢eredRotation); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes(center, rotOp->GetCenterOfRotation()); geometry2D->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(rotOp->GetCenterOfRotation()); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(¢eredRotation); } else { // we also have to consider the case, that there is no reference geometry available. if (m_PlaneGeometries.size() > 0) { // Reach through to all slices in my container for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { // Test for empty slices, which can happen if evenly spaced geometry if ((*iter).IsNotNull()) { (*iter)->ExecuteOperation(operation); } } // rotate overall geometry auto *rotOp = dynamic_cast(operation); BaseGeometry::ExecuteOperation(rotOp); } } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if (m_EvenlySpaced) { // get operation data auto *planeOp = dynamic_cast(operation); // Get first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation. If not all available, stop here if (!m_ReferenceGeometry || (!planeGeometry || dynamic_cast(planeGeometry.GetPointer())) || !planeOp) { break; } // General Behavior: // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // // 1st Step: Reorient Normal Vector of first plane // Point3D center = planeOp->GetPoint(); // m_ReferenceGeometry->GetCenter(); mitk::Vector3D currentNormal = planeGeometry->GetNormal(); mitk::Vector3D newNormal; if (planeOp->AreAxisDefined()) { // If planeOp was defined by one centerpoint and two axis vectors newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1()); } else { // If planeOp was defined by one centerpoint and one normal vector newNormal = planeOp->GetNormal(); } // Get Rotation axis und angle currentNormal.Normalize(); newNormal.Normalize(); ScalarType rotationAngle = angle(currentNormal.GetVnlVector(), newNormal.GetVnlVector()); rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg Vector3D rotationAxis = itk::CrossProduct(currentNormal, newNormal); if (std::abs(rotationAngle - 180) < mitk::eps) { // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis should be ANY vector that is 90� to current Normal mitk::Vector3D helpNormal; helpNormal = currentNormal; helpNormal[0] += 1; helpNormal[1] -= 1; helpNormal[2] += 1; helpNormal.Normalize(); rotationAxis = itk::CrossProduct(helpNormal, currentNormal); } RotationOperation centeredRotation(mitk::OpROTATE, center, rotationAxis, rotationAngle); // Rotate first slice planeGeometry->ExecuteOperation(¢eredRotation); // Reinitialize planes and select slice, if my rotations are all done. if (!planeOp->AreAxisDefined()) { // Clear the slice stack and adjust it according to the center of // rotation and plane position (see documentation of ReinitializePlanes) this->ReinitializePlanes(center, planeOp->GetPoint()); planeGeometry->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint()); m_SliceNavigationController->AdjustSliceStepperRange(); } } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation(¢eredRotation); // // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these // if (planeOp->AreAxisDefined()) { mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); vecAxixNew.Normalize(); mitk::Vector3D VecAxisCurr = planeGeometry->GetAxisVector(0); VecAxisCurr.Normalize(); ScalarType rotationAngle = angle(VecAxisCurr.GetVnlVector(), vecAxixNew.GetVnlVector()); rotationAngle = rotationAngle * 180 / PI; // Rad to Deg // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector. // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes // normal rotationAxis = itk::CrossProduct(VecAxisCurr, vecAxixNew); if (std::abs(rotationAngle - 180) < mitk::eps) { // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis can be just plane Normal. (have to rotate by 180�) rotationAxis = newNormal; } // Perform Rotation mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); planeGeometry->ExecuteOperation(&op); // Apply changes on first slice to whole slice stack this->ReinitializePlanes(center, planeOp->GetPoint()); planeGeometry->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint()); m_SliceNavigationController->AdjustSliceStepperRange(); } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation(&op); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpRESTOREPLANEPOSITION: if (m_EvenlySpaced) { // Save first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; auto *restorePlaneOp = dynamic_cast(operation); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation if (m_ReferenceGeometry && (planeGeometry && dynamic_cast(planeGeometry.GetPointer()) == nullptr) && restorePlaneOp) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Rotate first slice planeGeometry->ExecuteOperation(restorePlaneOp); m_DirectionVector = restorePlaneOp->GetDirectionVector(); double centerOfRotationDistance = planeGeometry->SignedDistanceFromPlane(m_ReferenceGeometry->GetCenter()); if (centerOfRotationDistance <= 0) { m_DirectionVector = -m_DirectionVector; } Vector3D spacing = restorePlaneOp->GetSpacing(); Superclass::SetSpacing(spacing); // /*Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above.*/ ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * m_DirectionVector[0]) + std::abs(m_ReferenceGeometry->GetExtentInMM(1) * m_DirectionVector[1]) + std::abs(m_ReferenceGeometry->GetExtentInMM(2) * m_DirectionVector[2]); if (directedExtent >= spacing[2]) { m_Slices = static_cast(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr)); if (m_Slices > 0) { m_PlaneGeometries[0] = planeGeometry; } - m_SliceNavigationController->GetSlice()->SetSteps(m_Slices); + m_SliceNavigationController->GetStepper()->SetSteps(m_Slices); this->Modified(); // End Reinitialization if (m_SliceNavigationController) { - m_SliceNavigationController->GetSlice()->SetPos(restorePlaneOp->GetPos()); + m_SliceNavigationController->GetStepper()->SetPos(restorePlaneOp->GetPos()); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(restorePlaneOp); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpAPPLYTRANSFORMMATRIX: // Clear all generated geometries and then transform only the first slice. // The other slices will be re-generated on demand // Save first slice geometry2D = m_PlaneGeometries[0]; applyMatrixOp = dynamic_cast(operation); // Apply transformation to first plane geometry2D->ExecuteOperation(applyMatrixOp); // Generate a ApplyTransformMatrixOperation using the dataset center instead of // the supplied rotation center. The supplied center is instead used to adjust the // slice stack afterwards (see OpROTATE). center = m_ReferenceGeometry->GetCenter(); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes(center, applyMatrixOp->GetReferencePoint()); BaseGeometry::ExecuteOperation(applyMatrixOp); break; default: // let handle by base class if we don't do anything BaseGeometry::ExecuteOperation(operation); } this->Modified(); } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp index fd3cf979ef..be87835ed5 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -1,807 +1,807 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDisplayActionEventBroadcast.h" // us #include "usGetModuleContext.h" #include "usModuleContext.h" // mitk core module #include #include #include #include #include #include #include mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast() : m_AlwaysReact(false) , m_AutoRepeat(false) , m_IndexToSliceModifier(4) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_ZoomFactor(2) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_CurrentDisplayCoordinate.Fill(0); // register the broadcast class (itself) as an interaction event observer via micro services us::ServiceProperties props; props["name"] = std::string("DisplayActionEventBroadcast"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(this, props); } mitk::DisplayActionEventBroadcast::~DisplayActionEventBroadcast() { m_ServiceRegistration.Unregister(); } void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled) { // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } void mitk::DisplayActionEventBroadcast::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } void mitk::DisplayActionEventBroadcast::ConfigurationChanged() { PropertyList::Pointer properties = GetAttributes(); // alwaysReact std::string strAlwaysReact = ""; m_AlwaysReact = false; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } } // auto repeat std::string strAutoRepeat = ""; m_AutoRepeat = false; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; m_IndexToSliceModifier = 4; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; m_LinkPlanes = false; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") { m_LinkPlanes = true; } } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } } bool mitk::DisplayActionEventBroadcast::FilterEvents(InteractionEvent* interactionEvent, DataNode * /*dataNode*/) { BaseRenderer* sendingRenderer = interactionEvent->GetSender(); if (nullptr == sendingRenderer) { return false; } if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID()) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckRotationPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO intersection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const PlaneGeometry* rendererWorldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == rendererWorldPlaneGeometry) { return false; } Point3D position = positionEvent->GetPositionInWorld(); const auto spacing = rendererWorldPlaneGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const ScalarType threshholdDistancePixels = 12.0; auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } // check if there is an intersection between rendered / clicked geometry and the one being analyzed Line3D intersectionLine; if (!rendererWorldPlaneGeometry->IntersectionLine(rendererPlaneGeometry, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(position) / spacing[static_cast(snc->GetDefaultViewDirection())]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { // we just take the last one, so overwrite each iteration (we just need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used anyOtherGeometry = rendererPlaneGeometry; if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (nullptr == geometryToBeRotated) // first one close to the cursor { geometryToBeRotated = rendererPlaneGeometry; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && rendererWorldPlaneGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we have enough information for rotation // remember where the last cursor position ON THE LINE has been observed m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(position); // find center of rotation by intersection with any of the OTHER lines if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) { return true; } else { return false; } } return false; } bool mitk::DisplayActionEventBroadcast::CheckSwivelPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const Point3D& position = positionEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry* clickedGeometry(nullptr); const PlaneGeometry* otherGeometry1(nullptr); const PlaneGeometry* otherGeometry2(nullptr); const ScalarType threshholdDistancePixels = 6.0; auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = rendererPlaneGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (nullptr == otherGeometry1) { otherGeometry1 = rendererPlaneGeometry; } else { otherGeometry2 = rendererPlaneGeometry; } if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } Line3D line; Point3D point; if ((nullptr != clickedGeometry) && (nullptr != otherGeometry1) && (nullptr != otherGeometry2) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(position) < threshholdDistancePixels) { return false; } else { m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = { 1.0, 0.0, 0.0 }; ScalarType yVector[] = { 0.0, 1.0, 0.0 }; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); } void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer* sender = interactionEvent->GetSender(); Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen(); if (m_InvertMoveDirection) { moveVector *= -1.0; } moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here? // store new display coordinate m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate move event with computed geometry values InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector)); } void mitk::DisplayActionEventBroadcast::SetCrosshair(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); // propagate set crosshair event with computed geometry values InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position)); } void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate zoom event with computed geometry values InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); } void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } int sliceDelta = 0; // scroll direction if (m_ScrollDirection == "updown") { sliceDelta = static_cast(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]); } else { sliceDelta = static_cast(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]); } if (m_InvertScrollDirection) { sliceDelta *= -1; } // set how many pixels the mouse has to be moved to scroll one slice // if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier) { sliceDelta = m_IndexToSliceModifier; } else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier) { sliceDelta = -m_IndexToSliceModifier; } sliceDelta /= m_IndexToSliceModifier; // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate scroll event with computed geometry values InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = 1; if (m_InvertScrollDirection) { sliceDelta = -1; } // propagate scroll event with a single slice delta (increase) InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = -1; if (m_InvertScrollDirection) { sliceDelta = 1; } // propagate scroll event with a single slice delta (decrease) InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } ScalarType level; ScalarType window; if (m_LevelDirection == "leftright") { level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertLevelWindowDirection) { level *= -1; window *= -1; } level *= static_cast(2); window *= static_cast(2); // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate set level window event with the level and window delta InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window)); } void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { ResetMouseCursor(); } void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = position - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection.as_ref()); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = position; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } // Determine relative mouse movement projected onto world space Point2D position = positionEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = position - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayActionEventBroadcast::IncreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); - auto stepper = sliceNaviController->GetTime(); + auto stepper = sliceNaviController->GetStepper(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayActionEventBroadcast::DecreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); - auto stepper = sliceNaviController->GetTime(); + auto stepper = sliceNaviController->GetStepper(); stepper->SetAutoRepeat(true); stepper->Previous(); } bool mitk::DisplayActionEventBroadcast::GetBoolProperty(PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp index d2ce34a462..41d9b94047 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp @@ -1,320 +1,320 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDisplayActionEventFunctions.h" // mitk core #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkDisplayActionEvents.h" #include "mitkInteractionPositionEvent.h" #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkNodePredicateDataType.h" ////////////////////////////////////////////////////////////////////////// // STANDARD FUNCTIONS ////////////////////////////////////////////////////////////////////////// mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveSenderCameraAction() { mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) { const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } sendingRenderer->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); RenderingManager::GetInstance()->RequestUpdate(sendingRenderer->GetRenderWindow()); } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetCrosshairAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplaySetCrosshairEvent().CheckEvent(&displayInteractorEvent)) { const DisplaySetCrosshairEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } sendingRenderer->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ZoomSenderCameraAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) { const DisplayZoomEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } if (1.0 != displayActionEvent->GetZoomFactor()) { sendingRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); RenderingManager::GetInstance()->RequestUpdate(sendingRenderer->GetRenderWindow()); } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ScrollSliceStepperAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayScrollEvent().CheckEvent(&displayInteractorEvent)) { const DisplayScrollEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } mitk::SliceNavigationController* sliceNavigationController = sendingRenderer->GetSliceNavigationController(); if (nullptr == sliceNavigationController) { return; } if (sliceNavigationController->GetSliceLocked()) { return; } - mitk::Stepper* sliceStepper = sliceNavigationController->GetSlice(); + mitk::Stepper* sliceStepper = sliceNavigationController->GetStepper(); if (nullptr == sliceStepper) { return; } // if only a single slice image was loaded, scrolling will affect the time steps if (sliceStepper->GetSteps() <= 1) { - sliceStepper = sliceNavigationController->GetTime(); + sliceStepper = sliceNavigationController->GetStepper(); } sliceStepper->SetAutoRepeat(displayActionEvent->GetAutoRepeat()); sliceStepper->MoveSlice(displayActionEvent->GetSliceDelta()); } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetLevelWindowAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplaySetLevelWindowEvent().CheckEvent(&displayInteractorEvent)) { const DisplaySetLevelWindowEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } // get the the topmost visible image of the sending renderer DataStorage::Pointer storage = sendingRenderer->GetDataStorage(); DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(NodePredicateDataType::New("Image")); Point3D worldposition; const auto* positionEvent = dynamic_cast(displayActionEvent->GetInteractionEvent()); sendingRenderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); auto globalCurrentTimePoint = sendingRenderer->GetTime(); DataNode::Pointer node = FindTopmostVisibleNode(allImageNodes, worldposition, globalCurrentTimePoint, sendingRenderer); if (node.IsNull()) { return; } LevelWindow levelWindow = LevelWindow(); node->GetLevelWindow(levelWindow); ScalarType level = levelWindow.GetLevel(); ScalarType window = levelWindow.GetWindow(); level += displayActionEvent->GetLevel(); window += displayActionEvent->GetWindow(); levelWindow.SetLevelWindow(level, window); auto* levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (nullptr != levelWindowProperty) { levelWindowProperty->SetLevelWindow(levelWindow); RenderingManager::GetInstance()->RequestUpdateAll(); } } }; return actionFunction; } ////////////////////////////////////////////////////////////////////////// // SYNCHRONIZED FUNCTIONS ////////////////////////////////////////////////////////////////////////// mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveCameraSynchronizedAction() { mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) { const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } auto renderingManager = RenderingManager::GetInstance(); auto allRenderWindows = renderingManager->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer::GetInstance(renderWindow)->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); renderingManager->RequestUpdate(renderWindow); } } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetCrosshairSynchronizedAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplaySetCrosshairEvent().CheckEvent(&displayInteractorEvent)) { const DisplaySetCrosshairEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); } } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ZoomCameraSynchronizedAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) { const DisplayZoomEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } if (1.0 != displayActionEvent->GetZoomFactor()) { auto renderingManager = RenderingManager::GetInstance(); auto allRenderWindows = renderingManager->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer* currentRenderer = BaseRenderer::GetInstance(renderWindow); currentRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); renderingManager->RequestUpdate(currentRenderer->GetRenderWindow()); } } } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ScrollSliceStepperSynchronizedAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayScrollEvent().CheckEvent(&displayInteractorEvent)) { const DisplayScrollEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { SliceNavigationController* sliceNavigationController = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); if (nullptr == sliceNavigationController) { return; } if (sliceNavigationController->GetSliceLocked()) { return; } - mitk::Stepper* sliceStepper = sliceNavigationController->GetSlice(); + mitk::Stepper* sliceStepper = sliceNavigationController->GetStepper(); if (nullptr == sliceStepper) { return; } // if only a single slice image was loaded, scrolling will affect the time steps if (sliceStepper->GetSteps() <= 1) { - sliceStepper = sliceNavigationController->GetTime(); + sliceStepper = sliceNavigationController->GetStepper(); } sliceStepper->SetAutoRepeat(displayActionEvent->GetAutoRepeat()); sliceStepper->MoveSlice(displayActionEvent->GetSliceDelta()); } } } }; return actionFunction; } diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index efb6e6e851..66d1c7d527 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,633 +1,633 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkMxNMultiWidget.h" // mitk core #include #include #include #include #include #include // mitk qt widget #include #include // qt #include #include #include #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, const QString& multiWidgetName/* = "mxn"*/) : QmitkAbstractMultiWidget(parent, f, multiWidgetName) , m_TimeNavigationController(nullptr) , m_SynchronizedWidgetConnector(std::make_unique()) , m_CrosshairVisibility(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkMxNMultiWidget::~QmitkMxNMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } this->SetInitialSelection(); } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { if (synchronized) { SetDisplayActionEventHandler(std::make_unique()); } else { SetDisplayActionEventHandler(std::make_unique()); } auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::AnatomicalPlane& /*orientation*/) const { // currently no mapping between plane orientation and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) { auto* renderingManager = mitk::RenderingManager::GetInstance(); mitk::Point3D currentPosition = mitk::Point3D(); unsigned int imageTimeStep = 0; if (!resetCamera) { // store the current position to set it again later, if the camera should not be reset currentPosition = this->GetSelectedPosition(""); // store the current time step to set it again later, if the camera should not be reset const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (geometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = geometry->TimePointToTimeStep(currentTimePoint); } } // initialize active render window renderingManager->InitializeView( this->GetActiveRenderWindowWidget()->GetRenderWindow()->GetVtkRenderWindow(), geometry, resetCamera); if (!resetCamera) { this->SetSelectedPosition(currentPosition, ""); - renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + renderingManager->GetTimeNavigationController()->GetStepper()->SetPos(imageTimeStep); } } void QmitkMxNMultiWidget::SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) { // Set the interaction reference referenceGeometry for all render windows. auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { auto* baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow()); baseRenderer->SetInteractionReferenceGeometry(referenceGeometry); } } bool QmitkMxNMultiWidget::HasCoupledRenderWindows() const { return false; } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& widgetName) const { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { return renderWindowWidget->GetCrosshairPosition(); } MITK_ERROR << "Crosshair position can not be retrieved."; return mitk::Point3D(0.0); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool visible) { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); renderWindowWidget->SetCrosshairVisibility(visible); } bool QmitkMxNMultiWidget::GetCrosshairVisibility() const { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return false; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); return renderWindowWidget->GetCrosshairVisibility(); } void QmitkMxNMultiWidget::SetCrosshairGap(unsigned int gapSize) { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->SetCrosshairGap(gapSize); } } void QmitkMxNMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } mitk::RenderingManager::GetInstance()->InitializeViewByBoundingObjects(renderWindow->GetVtkRenderWindow(), dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } } mitk::SliceNavigationController* QmitkMxNMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkMxNMultiWidget::EnableCrosshair() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->EnableCrosshair(); } } void QmitkMxNMultiWidget::DisableCrosshair() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->DisableCrosshair(); } } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkMxNMultiWidget::RemoveRenderWindowWidget() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); auto iterator = renderWindowWidgets.find(this->GetNameFromIndex(this->GetNumberOfRenderWindowWidgets() - 1)); if (iterator == renderWindowWidgets.end()) { return; } // disconnect each signal of this render window widget RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; m_TimeNavigationController->Disconnect(renderWindowWidgetToRemove->GetSliceNavigationController()); QmitkAbstractMultiWidget::RemoveRenderWindowWidget(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } QmitkAbstractMultiWidget::RenderWindowWidgetPointer QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); QmitkRenderWindowUtilityWidget* utilityWidget = new QmitkRenderWindowUtilityWidget(this, renderWindow, GetDataStorage()); renderWindowWidget->AddUtilityWidget(utilityWidget); connect(utilityWidget, &QmitkRenderWindowUtilityWidget::SynchronizationToggled, this, &QmitkMxNMultiWidget::ToggleSynchronization); connect(this, &QmitkMxNMultiWidget::UpdateUtilityWidgetViewPlanes, utilityWidget, &QmitkRenderWindowUtilityWidget::UpdateViewPlaneSelection); // needs to be done after 'QmitkRenderWindowUtilityWidget::ToggleSynchronization' has been connected // initially synchronize the node selection widget utilityWidget->ToggleSynchronization(true); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); // connect time navigation controller to react on referenceGeometry time events with the render window's slice naviation controller m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController()); // reverse connection between the render window's slice navigation controller and the time navigation controller renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); return renderWindowWidget; } void QmitkMxNMultiWidget::LoadLayout(const nlohmann::json* jsonData) { if ((*jsonData).is_null()) { QMessageBox::warning(this, "Load layout", "Could not read window layout"); return; } unsigned int windowCounter = 0; try { auto version = jsonData->at("version").get(); if (version != "1.0") { QMessageBox::warning(this, "Load layout", "Unknown layout version, could not load"); return; } delete this->layout(); auto content = BuildLayoutFromJSON(jsonData, &windowCounter); auto hBoxLayout = new QHBoxLayout(this); this->setLayout(hBoxLayout); hBoxLayout->addWidget(content); emit UpdateUtilityWidgetViewPlanes(); } catch (nlohmann::json::out_of_range& e) { MITK_ERROR << "Error in loading window layout from JSON: " << e.what(); return; } while (GetNumberOfRenderWindowWidgets() > windowCounter) { RemoveRenderWindowWidget(); } EnableCrosshair(); emit LayoutChanged(); } void QmitkMxNMultiWidget::SaveLayout(std::ostream* outStream) { if (outStream == nullptr) { return; } auto layout = this->layout(); if (layout == nullptr) return; // There should only ever be one item: a splitter auto widget = layout->itemAt(0)->widget(); auto splitter = dynamic_cast(widget); if (!splitter) { MITK_ERROR << "Tried to save unexpected layout format. Make sure the layout of this instance contains a single QSplitter."; return; } auto layoutJSON = BuildJSONFromLayout(splitter); layoutJSON["version"] = "1.0"; layoutJSON["name"] = "Custom Layout"; *outStream << std::setw(4) << layoutJSON << std::endl; } nlohmann::json QmitkMxNMultiWidget::BuildJSONFromLayout(const QSplitter* splitter) { nlohmann::json resultJSON; resultJSON["isWindow"] = false; resultJSON["vertical"] = (splitter->orientation() == Qt::Vertical) ? true : false; auto sizes = splitter->sizes(); auto content = nlohmann::json::array(); auto countSplitter = splitter->count(); for (int i = 0; i < countSplitter; ++i) { auto widget = splitter->widget(i); nlohmann::json widgetJSON; if (auto widgetSplitter = dynamic_cast(widget); widgetSplitter) { widgetJSON = BuildJSONFromLayout(widgetSplitter); } else if (auto widgetWindow = dynamic_cast(widget); widgetWindow) { widgetJSON["isWindow"] = true; widgetJSON["viewDirection"] = widgetWindow->GetSliceNavigationController()->GetViewDirectionAsString(); } widgetJSON["size"] = sizes[i]; content.push_back(widgetJSON); } resultJSON["content"] = content; return resultJSON; } QSplitter* QmitkMxNMultiWidget::BuildLayoutFromJSON(const nlohmann::json* jsonData, unsigned int* windowCounter, QSplitter* parentSplitter) { bool vertical = jsonData->at("vertical").get(); auto orientation = vertical ? Qt::Vertical : Qt::Horizontal; auto split = new QSplitter(orientation, parentSplitter); QList sizes; for (auto object : jsonData->at("content")) { bool isWindow = object["isWindow"].get(); int size = object["size"].get(); sizes.append(size); if (isWindow) { auto viewDirection = object["viewDirection"].get(); mitk::AnatomicalPlane viewPlane = mitk::AnatomicalPlane::Sagittal; if (viewDirection == "Axial") { viewPlane = mitk::AnatomicalPlane::Axial; } else if (viewDirection == "Coronal") { viewPlane = mitk::AnatomicalPlane::Coronal; } else if (viewDirection == "Original") { viewPlane = mitk::AnatomicalPlane::Original; } else if (viewDirection == "Sagittal") { viewPlane = mitk::AnatomicalPlane::Sagittal; } QmitkAbstractMultiWidget::RenderWindowWidgetPointer window = nullptr; QString renderWindowName; QmitkAbstractMultiWidget::RenderWindowWidgetMap::iterator it; // repurpose existing render windows as far as they already exist if (*windowCounter < GetRenderWindowWidgets().size()) { renderWindowName = this->GetNameFromIndex(*windowCounter); auto renderWindowWidgets = GetRenderWindowWidgets(); it = renderWindowWidgets.find(renderWindowName); if (it != renderWindowWidgets.end()) { window = it->second; } else { MITK_ERROR << "Could not find render window " << renderWindowName.toStdString() << ", although it should be there."; } } if (window == nullptr) { window = CreateRenderWindowWidget(); } window->GetSliceNavigationController()->SetDefaultViewDirection(viewPlane); window->GetSliceNavigationController()->Update(); split->addWidget(window.get()); window->show(); (*windowCounter)++; } else { auto subSplitter = BuildLayoutFromJSON(&object, windowCounter, split); split->addWidget(subSplitter); } } split->setSizes(sizes); return split; } void QmitkMxNMultiWidget::SetInitialSelection() { auto dataStorage = this->GetDataStorage(); if (nullptr == dataStorage) { return; } mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); auto allNodes = dataStorage->GetSubset(noHelperObjects); QmitkSynchronizedNodeSelectionWidget::NodeList currentSelection; for (auto& node : *allNodes) { currentSelection.append(node); } m_SynchronizedWidgetConnector->ChangeSelection(currentSelection); } void QmitkMxNMultiWidget::ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget) { bool synchronized = synchronizedWidget->IsSynchronized(); if (synchronized) { m_SynchronizedWidgetConnector->ConnectWidget(synchronizedWidget); m_SynchronizedWidgetConnector->SynchronizeWidget(synchronizedWidget); } else { m_SynchronizedWidgetConnector->DisconnectWidget(synchronizedWidget); } } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowMenu.cpp b/Modules/QtWidgets/src/QmitkRenderWindowMenu.cpp index ece6a28e45..0ea68a3046 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowMenu.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowMenu.cpp @@ -1,578 +1,578 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRenderWindowMenu.h" // mitk core #include "mitkProperties.h" #include "mitkResliceMethodProperty.h" // qt #include #include #include #include #include #include #include #include #include //#include"iconClose.xpm" #include "iconCrosshairMode.xpm" #include "iconFullScreen.xpm" //#include"iconHoriSplit.xpm" #include "iconSettings.xpm" //#include"iconVertiSplit.xpm" #include "iconLeaveFullScreen.xpm" // c++ #include unsigned int QmitkRenderWindowMenu::m_DefaultThickMode(1); QmitkRenderWindowMenu::QmitkRenderWindowMenu(QWidget* parent, Qt::WindowFlags flags, mitk::BaseRenderer* baseRenderer) : QWidget(parent, flags) , m_LayoutActionsMenu(nullptr) , m_CrosshairMenu(nullptr) , m_FullScreenMode(false) , m_Renderer(baseRenderer) , m_Parent(parent) , m_CrosshairRotationMode(0) , m_CrosshairVisibility(true) , m_Layout(LayoutIndex::Axial) , m_LayoutDesign(LayoutDesign::DEFAULT) , m_OldLayoutDesign(LayoutDesign::DEFAULT) { CreateMenuWidget(); setMinimumWidth(61); // DIRTY.. If you add or remove a button, you need to change the size. setMaximumWidth(61); setAutoFillBackground(true); this->hide(); m_AutoRotationTimer = new QTimer(this); m_AutoRotationTimer->setInterval(75); connect(m_AutoRotationTimer, &QTimer::timeout, this, &QmitkRenderWindowMenu::AutoRotateNextStep); connect(m_Parent, &QObject::destroyed, this, &QmitkRenderWindowMenu::deleteLater); } QmitkRenderWindowMenu::~QmitkRenderWindowMenu() { if (m_AutoRotationTimer->isActive()) { m_AutoRotationTimer->stop(); } } void QmitkRenderWindowMenu::SetLayoutIndex(LayoutIndex layoutIndex) { m_Layout = layoutIndex; } void QmitkRenderWindowMenu::UpdateLayoutDesignList(LayoutDesign layoutDesign) { m_LayoutDesign = layoutDesign; if (nullptr == m_LayoutActionsMenu) { CreateSettingsWidget(); } m_DefaultLayoutAction->setEnabled(true); m_All2DTop3DBottomLayoutAction->setEnabled(true); m_All2DLeft3DRightLayoutAction->setEnabled(true); m_OneBigLayoutAction->setEnabled(true); m_Only2DHorizontalLayoutAction->setEnabled(true); m_Only2DVerticalLayoutAction->setEnabled(true); m_OneTop3DBottomLayoutAction->setEnabled(true); m_OneLeft3DRightLayoutAction->setEnabled(true); m_AllHorizontalLayoutAction->setEnabled(true); m_AllVerticalLayoutAction->setEnabled(true); m_RemoveOneLayoutAction->setEnabled(true); switch (m_LayoutDesign) { case LayoutDesign::DEFAULT: { m_DefaultLayoutAction->setEnabled(false); break; } case LayoutDesign::ALL_2D_TOP_3D_BOTTOM: { m_All2DTop3DBottomLayoutAction->setEnabled(false); break; } case LayoutDesign::ALL_2D_LEFT_3D_RIGHT: { m_All2DLeft3DRightLayoutAction->setEnabled(false); break; } case LayoutDesign::ONE_BIG: { m_OneBigLayoutAction->setEnabled(false); break; } case LayoutDesign::ONLY_2D_HORIZONTAL: { m_Only2DHorizontalLayoutAction->setEnabled(false); break; } case LayoutDesign::ONLY_2D_VERTICAL: { m_Only2DVerticalLayoutAction->setEnabled(false); break; } case LayoutDesign::ONE_TOP_3D_BOTTOM: { m_OneTop3DBottomLayoutAction->setEnabled(false); break; } case LayoutDesign::ONE_LEFT_3D_RIGHT: { m_OneLeft3DRightLayoutAction->setEnabled(false); break; } case LayoutDesign::ALL_HORIZONTAL: { m_AllHorizontalLayoutAction->setEnabled(false); break; } case LayoutDesign::ALL_VERTICAL: { m_AllVerticalLayoutAction->setEnabled(false); break; } case LayoutDesign::REMOVE_ONE: { m_RemoveOneLayoutAction->setEnabled(false); break; } case LayoutDesign::NONE: { break; } } } void QmitkRenderWindowMenu::UpdateCrosshairVisibility(bool visible) { m_CrosshairVisibility = visible; } void QmitkRenderWindowMenu::UpdateCrosshairRotationMode(int mode) { m_CrosshairRotationMode = mode; } void QmitkRenderWindowMenu::MoveWidgetToCorrectPos() { int moveX = floor(static_cast(this->m_Parent->width()) - static_cast(this->width()) - 4.0); this->move(moveX, 3); auto cursorPos = this->mapFromGlobal(QCursor::pos()); if (cursorPos.x() < 0 || cursorPos.x() >= this->width() || cursorPos.y() < 0 || cursorPos.y() >= this->height()) { this->HideMenu(); } else { this->ShowMenu(); } } void QmitkRenderWindowMenu::ShowMenu() { MITK_DEBUG << "menu showMenu"; this->show(); this->raise(); } void QmitkRenderWindowMenu::HideMenu() { MITK_DEBUG << "menu hideEvent"; this->hide(); } void QmitkRenderWindowMenu::paintEvent(QPaintEvent * /*e*/) { QPainter painter(this); QColor semiTransparentColor = Qt::black; semiTransparentColor.setAlpha(255); painter.fillRect(rect(), semiTransparentColor); } void QmitkRenderWindowMenu::CreateMenuWidget() { QHBoxLayout *layout = new QHBoxLayout(this); layout->setAlignment(Qt::AlignRight); layout->setContentsMargins(1, 1, 1, 1); QSize size(13, 13); m_CrosshairMenu = new QMenu(this); connect(m_CrosshairMenu, &QMenu::aboutToShow, this, &QmitkRenderWindowMenu::OnCrosshairMenuAboutToShow); m_CrosshairModeButton = new QToolButton(this); m_CrosshairModeButton->setMaximumSize(15, 15); m_CrosshairModeButton->setIconSize(size); m_CrosshairModeButton->setMenu(m_CrosshairMenu); m_CrosshairModeButton->setIcon(QIcon(QPixmap(iconCrosshairMode_xpm))); m_CrosshairModeButton->setPopupMode(QToolButton::InstantPopup); m_CrosshairModeButton->setStyleSheet("QToolButton::menu-indicator { image: none; }"); m_CrosshairModeButton->setAutoRaise(true); layout->addWidget(m_CrosshairModeButton); m_FullScreenButton = new QToolButton(this); m_FullScreenButton->setMaximumSize(15, 15); m_FullScreenButton->setIconSize(size); m_FullScreenButton->setIcon(QIcon(QPixmap(iconFullScreen_xpm))); m_FullScreenButton->setAutoRaise(true); layout->addWidget(m_FullScreenButton); m_LayoutDesignButton = new QToolButton(this); m_LayoutDesignButton->setMaximumSize(15, 15); m_LayoutDesignButton->setIconSize(size); m_LayoutDesignButton->setIcon(QIcon(QPixmap(iconSettings_xpm))); m_LayoutDesignButton->setAutoRaise(true); layout->addWidget(m_LayoutDesignButton); connect(m_FullScreenButton, &QToolButton::clicked, this, &QmitkRenderWindowMenu::OnFullScreenButton); connect(m_LayoutDesignButton, &QToolButton::clicked, this, &QmitkRenderWindowMenu::OnLayoutDesignButton); } void QmitkRenderWindowMenu::CreateSettingsWidget() { m_LayoutActionsMenu = new QMenu(this); m_DefaultLayoutAction = new QAction("Standard layout", m_LayoutActionsMenu); m_DefaultLayoutAction->setDisabled(true); m_All2DTop3DBottomLayoutAction = new QAction("All 2D top, 3D bottom", m_LayoutActionsMenu); m_All2DTop3DBottomLayoutAction->setDisabled(false); m_All2DLeft3DRightLayoutAction = new QAction("All 2D left, 3D right", m_LayoutActionsMenu); m_All2DLeft3DRightLayoutAction->setDisabled(false); m_OneBigLayoutAction = new QAction("This big", m_LayoutActionsMenu); m_OneBigLayoutAction->setDisabled(false); m_Only2DHorizontalLayoutAction = new QAction("Only 2D horizontal", m_LayoutActionsMenu); m_Only2DHorizontalLayoutAction->setDisabled(false); m_Only2DVerticalLayoutAction = new QAction("Only 2D vertical", m_LayoutActionsMenu); m_Only2DVerticalLayoutAction->setDisabled(false); m_OneTop3DBottomLayoutAction = new QAction("This top, 3D bottom", m_LayoutActionsMenu); m_OneTop3DBottomLayoutAction->setDisabled(false); m_OneLeft3DRightLayoutAction = new QAction("This left, 3D right", m_LayoutActionsMenu); m_OneLeft3DRightLayoutAction->setDisabled(false); m_AllHorizontalLayoutAction = new QAction("All horizontal", m_LayoutActionsMenu); m_AllHorizontalLayoutAction->setDisabled(false); m_AllVerticalLayoutAction = new QAction("All vertical", m_LayoutActionsMenu); m_AllVerticalLayoutAction->setDisabled(false); m_RemoveOneLayoutAction = new QAction("Remove this", m_LayoutActionsMenu); m_RemoveOneLayoutAction->setDisabled(false); m_LayoutActionsMenu->addAction(m_DefaultLayoutAction); m_LayoutActionsMenu->addAction(m_All2DTop3DBottomLayoutAction); m_LayoutActionsMenu->addAction(m_All2DLeft3DRightLayoutAction); m_LayoutActionsMenu->addAction(m_OneBigLayoutAction); m_LayoutActionsMenu->addAction(m_Only2DHorizontalLayoutAction); m_LayoutActionsMenu->addAction(m_Only2DVerticalLayoutAction); m_LayoutActionsMenu->addAction(m_OneTop3DBottomLayoutAction); m_LayoutActionsMenu->addAction(m_OneLeft3DRightLayoutAction); m_LayoutActionsMenu->addAction(m_AllHorizontalLayoutAction); m_LayoutActionsMenu->addAction(m_AllVerticalLayoutAction); m_LayoutActionsMenu->addAction(m_RemoveOneLayoutAction); m_LayoutActionsMenu->setVisible(false); connect(m_DefaultLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::DEFAULT); }); connect(m_All2DTop3DBottomLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ALL_2D_TOP_3D_BOTTOM); }); connect(m_All2DLeft3DRightLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ALL_2D_LEFT_3D_RIGHT); }); connect(m_OneBigLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ONE_BIG); }); connect(m_Only2DHorizontalLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ONLY_2D_HORIZONTAL); }); connect(m_Only2DVerticalLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ONLY_2D_VERTICAL); }); connect(m_OneTop3DBottomLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ONE_TOP_3D_BOTTOM); }); connect(m_OneLeft3DRightLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ONE_LEFT_3D_RIGHT); }); connect(m_AllHorizontalLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ALL_HORIZONTAL); }); connect(m_AllVerticalLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::ALL_VERTICAL); }); connect(m_RemoveOneLayoutAction, &QAction::triggered, [this]() { this->OnSetLayout(LayoutDesign::REMOVE_ONE); }); } void QmitkRenderWindowMenu::ChangeFullScreenIcon() { m_FullScreenButton->setIcon(m_FullScreenMode ? QPixmap(iconLeaveFullScreen_xpm) : QPixmap(iconFullScreen_xpm)); } void QmitkRenderWindowMenu::AutoRotateNextStep() { if (m_Renderer->GetCameraRotationController()) { - m_Renderer->GetCameraRotationController()->GetSlice()->Next(); + m_Renderer->GetCameraRotationController()->GetStepper()->Next(); } } void QmitkRenderWindowMenu::OnAutoRotationActionTriggered() { if (m_AutoRotationTimer->isActive()) { m_AutoRotationTimer->stop(); - m_Renderer->GetCameraRotationController()->GetSlice()->PingPongOff(); + m_Renderer->GetCameraRotationController()->GetStepper()->PingPongOff(); } else { - m_Renderer->GetCameraRotationController()->GetSlice()->PingPongOn(); + m_Renderer->GetCameraRotationController()->GetStepper()->PingPongOn(); m_AutoRotationTimer->start(); } } void QmitkRenderWindowMenu::OnTSNumChanged(int num) { MITK_DEBUG << "Thickslices num: " << num << " on renderer " << m_Renderer.GetPointer(); if (m_Renderer.IsNotNull()) { unsigned int thickSlicesMode = 0; // determine the state of the thick-slice mode mitk::ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if(m_Renderer->GetCurrentWorldPlaneGeometryNode()->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty) { thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); if(thickSlicesMode!=0) m_DefaultThickMode = thickSlicesMode; } if(thickSlicesMode==0 && num>0) //default mode only for single slices { thickSlicesMode = m_DefaultThickMode; //mip default m_Renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.showarea", mitk::BoolProperty::New(true)); } if(num<1) { thickSlicesMode = 0; m_Renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.showarea", mitk::BoolProperty::New(false)); } m_Renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New(thickSlicesMode)); m_Renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(num)); m_TSLabel->setText(QString::number(num * 2 + 1)); m_Renderer->SendUpdateSlice(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkRenderWindowMenu::OnCrosshairMenuAboutToShow() { QMenu *crosshairModesMenu = m_CrosshairMenu; crosshairModesMenu->clear(); QAction *resetViewAction = new QAction(crosshairModesMenu); resetViewAction->setText("Reset view"); crosshairModesMenu->addAction(resetViewAction); connect(resetViewAction, &QAction::triggered, this, &QmitkRenderWindowMenu::ResetView); // Show hide crosshairs { QAction *showHideCrosshairVisibilityAction = new QAction(crosshairModesMenu); showHideCrosshairVisibilityAction->setText("Show crosshair"); showHideCrosshairVisibilityAction->setCheckable(true); showHideCrosshairVisibilityAction->setChecked(m_CrosshairVisibility); crosshairModesMenu->addAction(showHideCrosshairVisibilityAction); connect(showHideCrosshairVisibilityAction, &QAction::toggled, this, &QmitkRenderWindowMenu::OnCrosshairVisibilityChanged); } // Rotation mode { QAction *rotationGroupSeparator = new QAction(crosshairModesMenu); rotationGroupSeparator->setSeparator(true); rotationGroupSeparator->setText("Rotation mode"); crosshairModesMenu->addAction(rotationGroupSeparator); QActionGroup *rotationModeActionGroup = new QActionGroup(crosshairModesMenu); rotationModeActionGroup->setExclusive(true); QAction *noCrosshairRotation = new QAction(crosshairModesMenu); noCrosshairRotation->setActionGroup(rotationModeActionGroup); noCrosshairRotation->setText("No crosshair rotation"); noCrosshairRotation->setCheckable(true); noCrosshairRotation->setChecked(m_CrosshairRotationMode == 0); noCrosshairRotation->setData(0); crosshairModesMenu->addAction(noCrosshairRotation); QAction *singleCrosshairRotation = new QAction(crosshairModesMenu); singleCrosshairRotation->setActionGroup(rotationModeActionGroup); singleCrosshairRotation->setText("Crosshair rotation"); singleCrosshairRotation->setCheckable(true); singleCrosshairRotation->setChecked(m_CrosshairRotationMode == 1); singleCrosshairRotation->setData(1); crosshairModesMenu->addAction(singleCrosshairRotation); QAction *coupledCrosshairRotation = new QAction(crosshairModesMenu); coupledCrosshairRotation->setActionGroup(rotationModeActionGroup); coupledCrosshairRotation->setText("Coupled crosshair rotation"); coupledCrosshairRotation->setCheckable(true); coupledCrosshairRotation->setChecked(m_CrosshairRotationMode == 2); coupledCrosshairRotation->setData(2); crosshairModesMenu->addAction(coupledCrosshairRotation); QAction *swivelMode = new QAction(crosshairModesMenu); swivelMode->setActionGroup(rotationModeActionGroup); swivelMode->setText("Swivel mode"); swivelMode->setCheckable(true); swivelMode->setChecked(m_CrosshairRotationMode == 3); swivelMode->setData(3); crosshairModesMenu->addAction(swivelMode); connect(rotationModeActionGroup, &QActionGroup::triggered, this, &QmitkRenderWindowMenu::OnCrosshairRotationModeSelected); } // auto rotation support if (m_Renderer.IsNotNull() && m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard3D) { QAction *autoRotationGroupSeparator = new QAction(crosshairModesMenu); autoRotationGroupSeparator->setSeparator(true); crosshairModesMenu->addAction(autoRotationGroupSeparator); QAction *autoRotationAction = crosshairModesMenu->addAction("Auto Rotation"); autoRotationAction->setCheckable(true); autoRotationAction->setChecked(m_AutoRotationTimer->isActive()); connect(autoRotationAction, &QAction::triggered, this, &QmitkRenderWindowMenu::OnAutoRotationActionTriggered); } // Thickslices support if (m_Renderer.IsNotNull() && m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { QAction *thickSlicesGroupSeparator = new QAction(crosshairModesMenu); thickSlicesGroupSeparator->setSeparator(true); thickSlicesGroupSeparator->setText("ThickSlices mode"); crosshairModesMenu->addAction(thickSlicesGroupSeparator); QActionGroup *thickSlicesActionGroup = new QActionGroup(crosshairModesMenu); thickSlicesActionGroup->setExclusive(true); int currentMode = 0; { mitk::ResliceMethodProperty::Pointer m = dynamic_cast( m_Renderer->GetCurrentWorldPlaneGeometryNode()->GetProperty("reslice.thickslices")); if (m.IsNotNull()) currentMode = m->GetValueAsId(); } int currentNum = 1; { mitk::IntProperty::Pointer m = dynamic_cast( m_Renderer->GetCurrentWorldPlaneGeometryNode()->GetProperty("reslice.thickslices.num")); if (m.IsNotNull()) { currentNum = m->GetValue(); } } if (currentMode == 0) currentNum = 0; QSlider *m_TSSlider = new QSlider(crosshairModesMenu); m_TSSlider->setMinimum(0); m_TSSlider->setMaximum(50); m_TSSlider->setValue(currentNum); m_TSSlider->setOrientation(Qt::Horizontal); connect(m_TSSlider, &QSlider::valueChanged, this, &QmitkRenderWindowMenu::OnTSNumChanged); QHBoxLayout *tsLayout = new QHBoxLayout; tsLayout->setContentsMargins(4, 4, 4, 4); tsLayout->addWidget(new QLabel("TS: ")); tsLayout->addWidget(m_TSSlider); tsLayout->addWidget(m_TSLabel = new QLabel(QString::number(currentNum * 2 + 1), this)); QWidget *tsWidget = new QWidget; tsWidget->setLayout(tsLayout); QWidgetAction *m_TSSliderAction = new QWidgetAction(crosshairModesMenu); m_TSSliderAction->setDefaultWidget(tsWidget); crosshairModesMenu->addAction(m_TSSliderAction); } } void QmitkRenderWindowMenu::OnCrosshairVisibilityChanged(bool visible) { UpdateCrosshairVisibility(visible); emit CrosshairVisibilityChanged(m_CrosshairVisibility); } void QmitkRenderWindowMenu::OnCrosshairRotationModeSelected(QAction *action) { UpdateCrosshairRotationMode(action->data().toInt()); emit CrosshairRotationModeChanged(m_CrosshairRotationMode); } void QmitkRenderWindowMenu::OnFullScreenButton(bool /*checked*/) { if (!m_FullScreenMode) { m_FullScreenMode = true; m_OldLayoutDesign = m_LayoutDesign; emit LayoutDesignChanged(LayoutDesign::ONE_BIG); } else { m_FullScreenMode = false; emit LayoutDesignChanged(m_OldLayoutDesign); } MoveWidgetToCorrectPos(); ChangeFullScreenIcon(); ShowMenu(); } void QmitkRenderWindowMenu::OnLayoutDesignButton(bool /*checked*/) { if (nullptr == m_LayoutActionsMenu) { CreateSettingsWidget(); } QPoint point = mapToGlobal(m_LayoutDesignButton->geometry().topLeft()); m_LayoutActionsMenu->setVisible(true); m_LayoutActionsMenu->exec(point); } void QmitkRenderWindowMenu::OnSetLayout(LayoutDesign layoutDesign) { m_FullScreenMode = false; ChangeFullScreenIcon(); m_LayoutDesign = layoutDesign; emit LayoutDesignChanged(m_LayoutDesign); ShowMenu(); } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp index 9e08a160f7..7f38f5414e 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp @@ -1,181 +1,181 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRenderWindowUtilityWidget.h" #include // mitk core #include #include #include #include // mitk qt widgets #include #include // itk #include QmitkRenderWindowUtilityWidget::QmitkRenderWindowUtilityWidget( QWidget* parent/* = nullptr */, QmitkRenderWindow* renderWindow/* = nullptr */, mitk::DataStorage* dataStorage/* = nullptr */) : m_NodeSelectionWidget(nullptr) , m_SliceNavigationWidget(nullptr) , m_StepperAdapter(nullptr) , m_ViewDirectionSelector(nullptr) { this->setParent(parent); auto layout = new QHBoxLayout(this); layout->setMargin(0); mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); m_BaseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow()); m_NodeSelectionWidget = new QmitkSynchronizedNodeSelectionWidget(parent); m_NodeSelectionWidget->SetBaseRenderer(m_BaseRenderer); m_NodeSelectionWidget->SetDataStorage(dataStorage); m_NodeSelectionWidget->SetNodePredicate(noHelperObjects); auto menuBar = new QMenuBar(this); menuBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); auto dataMenu = menuBar->addMenu("Data"); QWidgetAction* dataAction = new QWidgetAction(dataMenu); dataAction->setDefaultWidget(m_NodeSelectionWidget); dataMenu->addAction(dataAction); layout->addWidget(menuBar); auto* synchPushButton = new QPushButton(this); auto* synchIcon = new QIcon(); auto synchronizeSvg = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")); auto desynchronizeSvg = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")); synchIcon->addPixmap(synchronizeSvg.pixmap(64), QIcon::Normal, QIcon::On); synchIcon->addPixmap(desynchronizeSvg.pixmap(64), QIcon::Normal, QIcon::Off); synchPushButton->setIcon(*synchIcon); synchPushButton->setToolTip("Synchronize / desynchronize data management"); synchPushButton->setCheckable(true); synchPushButton->setChecked(true); connect(synchPushButton, &QPushButton::clicked, this, &QmitkRenderWindowUtilityWidget::ToggleSynchronization); layout->addWidget(synchPushButton); auto* sliceNavigationController = m_BaseRenderer->GetSliceNavigationController(); m_SliceNavigationWidget = new QmitkSliceNavigationWidget(this); m_StepperAdapter = - new QmitkStepperAdapter(m_SliceNavigationWidget, sliceNavigationController->GetSlice()); + new QmitkStepperAdapter(m_SliceNavigationWidget, sliceNavigationController->GetStepper()); layout->addWidget(m_SliceNavigationWidget); mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer{ m_BaseRenderer }; m_RenderWindowViewDirectionController = std::make_unique(); m_RenderWindowViewDirectionController->SetControlledRenderer(controlledRenderer); m_RenderWindowViewDirectionController->SetDataStorage(dataStorage); m_ViewDirectionSelector = new QComboBox(this); QStringList viewDirections{ "axial", "coronal", "sagittal"}; m_ViewDirectionSelector->insertItems(0, viewDirections); connect(m_ViewDirectionSelector, &QComboBox::currentTextChanged, this, &QmitkRenderWindowUtilityWidget::ChangeViewDirection); UpdateViewPlaneSelection(); layout->addWidget(m_ViewDirectionSelector); // finally add observer, after all relevant objects have been created / initialized sliceNavigationController->ConnectGeometrySendEvent(this); } QmitkRenderWindowUtilityWidget::~QmitkRenderWindowUtilityWidget() { } void QmitkRenderWindowUtilityWidget::ToggleSynchronization(bool synchronized) { m_NodeSelectionWidget->SetSynchronized(synchronized); emit SynchronizationToggled(m_NodeSelectionWidget); } void QmitkRenderWindowUtilityWidget::SetGeometry(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) { return; } const auto* sliceNavigationController = m_BaseRenderer->GetSliceNavigationController(); auto viewDirection = sliceNavigationController->GetViewDirection(); unsigned int axis = 0; switch (viewDirection) { case mitk::AnatomicalPlane::Original: return; case mitk::AnatomicalPlane::Axial: { axis = 2; break; } case mitk::AnatomicalPlane::Coronal: { axis = 1; break; } case mitk::AnatomicalPlane::Sagittal: { axis = 0; break; } } const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); const mitk::BaseGeometry* rendererGeometry = m_BaseRenderer->GetCurrentWorldGeometry(); mitk::TimeStepType timeStep = sliceNavigationController->GetTime()->GetPos(); mitk::BaseGeometry::ConstPointer geometry = inputTimeGeometry->GetGeometryForTimeStep(timeStep); mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); int dominantAxis = itk::Function::Max3(inverseMatrix[0][axis], inverseMatrix[1][axis], inverseMatrix[2][axis]); bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][axis] < 0; bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[axis] < 0; m_SliceNavigationWidget->SetInverseDirection(referenceGeometryAxisInverted != rendererZAxisInverted); } void QmitkRenderWindowUtilityWidget::ChangeViewDirection(const QString& viewDirection) { m_RenderWindowViewDirectionController->SetViewDirectionOfRenderer(viewDirection.toStdString()); } void QmitkRenderWindowUtilityWidget::UpdateViewPlaneSelection() { const auto sliceNavigationController = m_BaseRenderer->GetSliceNavigationController(); const auto viewDirection = sliceNavigationController->GetDefaultViewDirection(); switch (viewDirection) { case mitk::AnatomicalPlane::Axial: m_ViewDirectionSelector->setCurrentIndex(0); break; case mitk::AnatomicalPlane::Coronal: m_ViewDirectionSelector->setCurrentIndex(1); break; case mitk::AnatomicalPlane::Sagittal: m_ViewDirectionSelector->setCurrentIndex(2); break; default: break; } } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp index ae13ec817c..29325720d7 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp @@ -1,322 +1,322 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRenderWindowWidget.h" // vtk #include #include QmitkRenderWindowWidget::QmitkRenderWindowWidget(QWidget* parent/* = nullptr*/, const QString& widgetName/* = ""*/, mitk::DataStorage* dataStorage/* = nullptr*/) : QFrame(parent) , m_WidgetName(widgetName) , m_DataStorage(dataStorage) , m_RenderWindow(nullptr) , m_CrosshairManager(nullptr) { this->InitializeGUI(); } QmitkRenderWindowWidget::~QmitkRenderWindowWidget() { auto sliceNavigationController = this->GetSliceNavigationController(); if (nullptr != sliceNavigationController) { sliceNavigationController->SetCrosshairEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); } this->DisableCrosshair(); } void QmitkRenderWindowWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (dataStorage == m_DataStorage) { return; } m_DataStorage = dataStorage; if (nullptr != m_RenderWindow) { mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow())->SetDataStorage(dataStorage); } } mitk::SliceNavigationController* QmitkRenderWindowWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } void QmitkRenderWindowWidget::RequestUpdate() { mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::ForceImmediateUpdate() { mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::AddUtilityWidget(QWidget* utilityWidget) { m_Layout->insertWidget(0, utilityWidget); } void QmitkRenderWindowWidget::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } m_GradientBackgroundColors.first = upper; m_GradientBackgroundColors.second = lower; vtkRenderer->SetBackground(lower[0], lower[1], lower[2]); vtkRenderer->SetBackground2(upper[0], upper[1], upper[2]); ShowGradientBackground(true); } void QmitkRenderWindowWidget::ShowGradientBackground(bool show) { m_RenderWindow->GetRenderer()->GetVtkRenderer()->SetGradientBackground(show); } bool QmitkRenderWindowWidget::IsGradientBackgroundOn() const { return m_RenderWindow->GetRenderer()->GetVtkRenderer()->GetGradientBackground(); } void QmitkRenderWindowWidget::SetDecorationColor(const mitk::Color& color) { m_DecorationColor = color; m_CornerAnnotation->GetTextProperty()->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); QColor hexColor(m_DecorationColor[0] * 255, m_DecorationColor[1] * 255, m_DecorationColor[2] * 255); setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } void QmitkRenderWindowWidget::ShowColoredRectangle(bool show) { if (show) { setFrameStyle(QFrame::Box | QFrame::Plain); } else { setFrameStyle(NoFrame); } } bool QmitkRenderWindowWidget::IsColoredRectangleVisible() const { return frameStyle() > 0; } void QmitkRenderWindowWidget::ShowCornerAnnotation(bool show) { m_CornerAnnotation->SetVisibility(show); } bool QmitkRenderWindowWidget::IsCornerAnnotationVisible() const { return m_CornerAnnotation->GetVisibility() > 0; } void QmitkRenderWindowWidget::SetCornerAnnotationText(const std::string& cornerAnnotation) { m_CornerAnnotation->SetText(0, cornerAnnotation.c_str()); } std::string QmitkRenderWindowWidget::GetCornerAnnotationText() const { return std::string(m_CornerAnnotation->GetText(0)); } bool QmitkRenderWindowWidget::IsRenderWindowMenuActivated() const { return m_RenderWindow->GetActivateMenuWidgetFlag(); } void QmitkRenderWindowWidget::SetCrosshairVisibility(bool visible) { m_CrosshairManager->SetCrosshairVisibility(visible, m_RenderWindow->GetRenderer()); this->RequestUpdate(); } bool QmitkRenderWindowWidget::GetCrosshairVisibility() { return m_CrosshairManager->GetCrosshairVisibility(m_RenderWindow->GetRenderer()); } void QmitkRenderWindowWidget::SetCrosshairGap(unsigned int gapSize) { m_CrosshairManager->SetCrosshairGap(gapSize); } void QmitkRenderWindowWidget::EnableCrosshair() { m_CrosshairManager->AddCrosshairNodeToDataStorage(m_DataStorage); } void QmitkRenderWindowWidget::DisableCrosshair() { m_CrosshairManager->RemoveCrosshairNodeFromDataStorage(m_DataStorage); } void QmitkRenderWindowWidget::InitializeGUI() { m_Layout = new QVBoxLayout(this); m_Layout->setMargin(0); setLayout(m_Layout); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setContentsMargins(0, 0, 0, 0); if (nullptr == m_DataStorage) { return; } mitk::RenderingManager::GetInstance()->SetDataStorage(m_DataStorage); // create render window for this render window widget m_RenderWindow = new QmitkRenderWindow(this, m_WidgetName, nullptr); m_RenderWindow->SetLayoutIndex(mitk::AnatomicalPlane::Sagittal); connect(m_RenderWindow, &QmitkRenderWindow::ResetGeometry, this, &QmitkRenderWindowWidget::OnResetGeometry); auto* sliceNavigationController = this->GetSliceNavigationController(); sliceNavigationController->SetDefaultViewDirection(mitk::AnatomicalPlane::Sagittal); m_Layout->addWidget(m_RenderWindow); // set colors and corner annotation InitializeDecorations(); // use crosshair manager m_CrosshairManager = mitk::CrosshairManager::New(m_RenderWindow->GetRenderer()); sliceNavigationController->SetCrosshairEvent.AddListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); // finally add observer, after all relevant objects have been created / initialized sliceNavigationController->ConnectGeometrySendEvent(this); sliceNavigationController->ConnectGeometrySliceEvent(this); mitk::TimeGeometry::ConstPointer timeGeometry = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeView(m_RenderWindow->GetVtkRenderWindow(), timeGeometry); } void QmitkRenderWindowWidget::InitializeDecorations() { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } // initialize background color gradients float black[3] = { 0.0f, 0.0f, 0.0f }; SetGradientBackgroundColors(black, black); // initialize annotation text and decoration color setFrameStyle(QFrame::Box | QFrame::Plain); m_CornerAnnotation = vtkSmartPointer::New(); m_CornerAnnotation->SetText(0, "Sagittal"); m_CornerAnnotation->SetMaximumFontSize(12); if (0 == vtkRenderer->HasViewProp(m_CornerAnnotation)) { vtkRenderer->AddViewProp(m_CornerAnnotation); } float white[3] = { 1.0f, 1.0f, 1.0f }; SetDecorationColor(mitk::Color(white)); } void QmitkRenderWindowWidget::SetCrosshairPosition(const mitk::Point3D& newPosition) { m_CrosshairManager->SetCrosshairPosition(newPosition); this->RequestUpdate(); } mitk::Point3D QmitkRenderWindowWidget::GetCrosshairPosition() const { return m_CrosshairManager->GetCrosshairPosition(); } void QmitkRenderWindowWidget::SetGeometry(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) { return; } const auto* planeGeometry = this->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (nullptr == planeGeometry) { mitkThrow() << "No valid plane geometry set. Render window is in an invalid state."; } return SetCrosshairPosition(planeGeometry->GetCenter()); } void QmitkRenderWindowWidget::SetGeometrySlice(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0).CheckEvent(&event)) { return; } const auto* sliceNavigationController = this->GetSliceNavigationController(); m_CrosshairManager->UpdateCrosshairPosition(sliceNavigationController); } void QmitkRenderWindowWidget::OnResetGeometry() { const auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow()); const auto* interactionReferenceGeometry = baseRenderer->GetInteractionReferenceGeometry(); this->ResetGeometry(interactionReferenceGeometry); m_RenderWindow->ShowOverlayMessage(false); } void QmitkRenderWindowWidget::ResetGeometry(const mitk::TimeGeometry* referenceGeometry) { if (nullptr == referenceGeometry) { return; } mitk::TimeStepType imageTimeStep = 0; // store the current position to set it again later, if the camera should not be reset mitk::Point3D currentPosition = this->GetCrosshairPosition(); // store the current time step to set it again later, if the camera should not be reset auto* renderingManager = mitk::RenderingManager::GetInstance(); - const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); + const mitk::TimePointType currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (referenceGeometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); } const auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); // reset position and time step this->GetSliceNavigationController()->SelectSliceByPoint(currentPosition); - renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + renderingManager->GetTimeNavigationController()->GetStepper()->SetPos(imageTimeStep); } diff --git a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp index 3ad055e200..18685549c6 100644 --- a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp @@ -1,742 +1,742 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #define SMW_INFO MITK_INFO("widget.stdmulti") #include "QmitkStdMultiWidget.h" #include "QmitkRenderWindowWidget.h" // mitk core #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qt #include #include #include // vtk #include // c++ #include QmitkStdMultiWidget::QmitkStdMultiWidget(QWidget *parent, Qt::WindowFlags f/* = 0*/, const QString &name/* = "stdmulti"*/) : QmitkAbstractMultiWidget(parent, f, name) , m_TimeNavigationController(nullptr) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkStdMultiWidget::~QmitkStdMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkStdMultiWidget::InitializeMultiWidget() { // yellow is default color for widget4 m_DecorationColorWidget4[0] = 1.0f; m_DecorationColorWidget4[1] = 1.0f; m_DecorationColorWidget4[2] = 0.0f; SetLayout(2, 2); // transfer colors in WorldGeometry-Nodes of the associated Renderer // of widget 1 m_PlaneNode1 = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(GetDecorationColor(0)); // of widget 2 m_PlaneNode2 = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(GetDecorationColor(1)); // of widget 3 m_PlaneNode3 = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(GetDecorationColor(2)); // the parent node m_ParentNodeForGeometryPlanes = mitk::BaseRenderer::GetInstance(GetRenderWindow4()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); AddDisplayPlaneSubTree(); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "Axial" == widgetName) { return GetRenderWindow1(); } if ("sagittal" == widgetName || "Sagittal" == widgetName) { return GetRenderWindow2(); } if ("coronal" == widgetName || "Coronal" == widgetName) { return GetRenderWindow3(); } if ("3d" == widgetName || "3D" == widgetName) { return GetRenderWindow4(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(const mitk::AnatomicalPlane& orientation) const { return GetRenderWindow(static_cast(orientation)); } void QmitkStdMultiWidget::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) { auto* renderingManager = mitk::RenderingManager::GetInstance(); mitk::Point3D currentPosition = mitk::Point3D(); unsigned int imageTimeStep = 0; if (!resetCamera) { // store the current position to set it again later, if the camera should not be reset currentPosition = this->GetSelectedPosition(""); // store the current time step to set it again later, if the camera should not be reset const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (geometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = geometry->TimePointToTimeStep(currentTimePoint); } } // initialize render windows renderingManager->InitializeViews(geometry, mitk::RenderingManager::REQUEST_UPDATE_ALL, resetCamera); if (!resetCamera) { this->SetSelectedPosition(currentPosition, ""); - renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + renderingManager->GetTimeNavigationController()->GetStepper()->SetPos(imageTimeStep); } } void QmitkStdMultiWidget::SetInteractionReferenceGeometry(const mitk::TimeGeometry* /*referenceGeometry*/) { // not implemented on purpose } bool QmitkStdMultiWidget::HasCoupledRenderWindows() const { return true; } void QmitkStdMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& /*widgetName*/) { GetRenderWindow1()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindow2()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindow3()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); RequestUpdateAll(); } const mitk::Point3D QmitkStdMultiWidget::GetSelectedPosition(const QString& /*widgetName*/) const { const mitk::PlaneGeometry* plane1 = GetRenderWindow1()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry* plane2 = GetRenderWindow2()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry* plane3 = GetRenderWindow3()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ((plane1 != nullptr) && (plane2 != nullptr) && (plane1->IntersectionLine(plane2, line))) { mitk::Point3D point; if ((plane3 != nullptr) && (plane3->IntersectionPoint(line, point))) { return point; } } return mitk::Point3D(); } void QmitkStdMultiWidget::SetCrosshairVisibility(bool visible) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible); } emit NotifyCrosshairVisibilityChanged(visible); RequestUpdateAll(); } bool QmitkStdMultiWidget::GetCrosshairVisibility() const { bool crosshairVisibility = true; if (m_PlaneNode1.IsNotNull()) { bool visibilityProperty = false; m_PlaneNode1->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } if (m_PlaneNode2.IsNotNull()) { bool visibilityProperty = false; crosshairVisibility &= m_PlaneNode2->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } if (m_PlaneNode3.IsNotNull()) { bool visibilityProperty = false; crosshairVisibility &= m_PlaneNode3->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } return crosshairVisibility; } void QmitkStdMultiWidget::SetCrosshairGap(unsigned int gapSize) { m_PlaneNode1->SetIntProperty("Crosshair.Gap Size", gapSize); m_PlaneNode2->SetIntProperty("Crosshair.Gap Size", gapSize); m_PlaneNode3->SetIntProperty("Crosshair.Gap Size", gapSize); } void QmitkStdMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkStdMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } emit NotifyCrosshairRotationModeChanged(userMode); } mitk::SliceNavigationController* QmitkStdMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkStdMultiWidget::AddPlanesToDataStorage() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { dataStorage->Add(m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode1, m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode2, m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode3, m_ParentNodeForGeometryPlanes); } } void QmitkStdMultiWidget::RemovePlanesFromDataStorage() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { dataStorage->Remove(m_PlaneNode1); dataStorage->Remove(m_PlaneNode2); dataStorage->Remove(m_PlaneNode3); dataStorage->Remove(m_ParentNodeForGeometryPlanes); } } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(unsigned int number) const { switch (number) { case 0: return GetRenderWindow1(); case 1: return GetRenderWindow2(); case 2: return GetRenderWindow3(); case 3: return GetRenderWindow4(); default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow1() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(0, 0)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow2() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(0, 1)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow3() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(1, 0)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow4() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(1, 1)); } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane1() const { return m_PlaneNode1; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane2() const { return m_PlaneNode2; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane3() const { return m_PlaneNode3; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane(unsigned number) const { switch (number) { case 1: return m_PlaneNode1; case 2: return m_PlaneNode2; case 3: return m_PlaneNode3; default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } void QmitkStdMultiWidget::SetDecorationColor(unsigned int widgetNumber, mitk::Color color) { switch (widgetNumber) { case 0: if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetColor(color); } break; case 1: if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetColor(color); } break; case 2: if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetColor(color); } break; case 3: m_DecorationColorWidget4 = color; break; default: MITK_ERROR << "Decoration color for unknown widget!"; break; } } mitk::Color QmitkStdMultiWidget::GetDecorationColor(unsigned int widgetNumber) { // The implementation looks a bit messy here, but it avoids // synchronization of the color of the geometry nodes and an // internal member here. // Default colors were chosen for decent visibility. // Feel free to change your preferences in the workbench. float tmp[3] = { 0.0f, 0.0f, 0.0f }; switch (widgetNumber) { case 0: { if (m_PlaneNode1.IsNotNull()) { if (m_PlaneNode1->GetColor(tmp)) { return dynamic_cast(m_PlaneNode1->GetProperty("color"))->GetColor(); } } float red[3] = { 0.753f, 0.0f, 0.0f }; // This is #C00000 in hex return mitk::Color(red); } case 1: { if (m_PlaneNode2.IsNotNull()) { if (m_PlaneNode2->GetColor(tmp)) { return dynamic_cast(m_PlaneNode2->GetProperty("color"))->GetColor(); } } float green[3] = { 0.0f, 0.69f, 0.0f }; // This is #00B000 in hex return mitk::Color(green); } case 2: { if (m_PlaneNode3.IsNotNull()) { if (m_PlaneNode3->GetColor(tmp)) { return dynamic_cast(m_PlaneNode3->GetProperty("color"))->GetColor(); } } float blue[3] = { 0.0, 0.502f, 1.0f }; // This is #0080FF in hex return mitk::Color(blue); } case 3: { return m_DecorationColorWidget4; } default: MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = { 0.0f, 0.0f, 0.0f }; return mitk::Color(black); } } void QmitkStdMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkStdMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the Annotation as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkStdMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkStdMultiWidget::Fit() { vtkSmartPointer vtkrenderer; vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow4()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow4()->renderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkObject::SetGlobalWarningDisplay(w); } void QmitkStdMultiWidget::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; // ... of widget 1 mitk::BaseRenderer* renderer1 = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow()); m_PlaneNode1 = renderer1->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New(std::string(renderer1->GetName()) + ".plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 mitk::BaseRenderer* renderer2 = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow()); m_PlaneNode2 = renderer2->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New(std::string(renderer2->GetName()) + ".plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 mitk::BaseRenderer *renderer3 = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow()); m_PlaneNode3 = renderer3->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New(std::string(renderer3->GetName()) + ".plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New("Widgets")); m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); } void QmitkStdMultiWidget::EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p) { mitk::Point2D pointOnDisplay; renderer->WorldToDisplay(p, pointOnDisplay); if (pointOnDisplay[0] < renderer->GetVtkRenderer()->GetOrigin()[0] || pointOnDisplay[1] < renderer->GetVtkRenderer()->GetOrigin()[1] || pointOnDisplay[0] > renderer->GetVtkRenderer()->GetOrigin()[0] + renderer->GetViewportSize()[0] || pointOnDisplay[1] > renderer->GetVtkRenderer()->GetOrigin()[1] + renderer->GetViewportSize()[1]) { mitk::Point2D pointOnPlane; renderer->GetCurrentWorldPlaneGeometry()->Map(p, pointOnPlane); renderer->GetCameraController()->MoveCameraToPoint(pointOnPlane); } } void QmitkStdMultiWidget::SetWidgetPlaneVisibility(const char *widgetName, bool visible, mitk::BaseRenderer *renderer) { auto dataStorage = GetDataStorage(); if (nullptr != dataStorage) { mitk::DataNode* dataNode = dataStorage->GetNamedNode(widgetName); if (dataNode != nullptr) { dataNode->SetVisibility(visible, renderer); } } } void QmitkStdMultiWidget::SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible, renderer); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible, renderer); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible, renderer); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkStdMultiWidget::SetLayoutImpl() { CreateRenderWindowWidgets(); GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); // Initialize views as axial, sagittal, coronal to all data objects in DataStorage auto geo = GetDataStorage()->ComputeBoundingGeometry3D(GetDataStorage()->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); } void QmitkStdMultiWidget::CreateRenderWindowWidgets() { // create axial render window (widget) QString renderWindowWidgetName = GetNameFromIndex(0, 0); RenderWindowWidgetPointer renderWindowWidget1 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow1 = renderWindowWidget1->GetRenderWindow(); renderWindow1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::AnatomicalPlane::Axial); renderWindowWidget1->SetDecorationColor(GetDecorationColor(0)); renderWindowWidget1->SetCornerAnnotationText("Axial"); renderWindowWidget1->GetRenderWindow()->SetLayoutIndex(mitk::AnatomicalPlane::Axial); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget1); // create sagittal render window (widget) renderWindowWidgetName = GetNameFromIndex(0, 1); RenderWindowWidgetPointer renderWindowWidget2 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow2 = renderWindowWidget2->GetRenderWindow(); renderWindow2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::AnatomicalPlane::Sagittal); renderWindowWidget2->SetDecorationColor(GetDecorationColor(1)); renderWindowWidget2->setStyleSheet("border: 0px"); renderWindowWidget2->SetCornerAnnotationText("Sagittal"); renderWindowWidget2->GetRenderWindow()->SetLayoutIndex(mitk::AnatomicalPlane::Sagittal); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget2); // create coronal render window (widget) renderWindowWidgetName = GetNameFromIndex(1, 0); RenderWindowWidgetPointer renderWindowWidget3 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow3 = renderWindowWidget3->GetRenderWindow(); renderWindow3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::AnatomicalPlane::Coronal); renderWindowWidget3->SetDecorationColor(GetDecorationColor(2)); renderWindowWidget3->SetCornerAnnotationText("Coronal"); renderWindowWidget3->GetRenderWindow()->SetLayoutIndex(mitk::AnatomicalPlane::Coronal); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget3); // create 3D render window (widget) renderWindowWidgetName = GetNameFromIndex(1, 1); RenderWindowWidgetPointer renderWindowWidget4 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow4 = renderWindowWidget4->GetRenderWindow(); renderWindow4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::AnatomicalPlane::Original); renderWindowWidget4->SetDecorationColor(GetDecorationColor(3)); renderWindowWidget4->SetCornerAnnotationText("3D"); renderWindowWidget4->GetRenderWindow()->SetLayoutIndex(mitk::AnatomicalPlane::Original); mitk::BaseRenderer::GetInstance(renderWindowWidget4->GetRenderWindow()->renderWindow())->SetMapperID(mitk::BaseRenderer::Standard3D); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget4); SetActiveRenderWindowWidget(renderWindowWidget1); // connect to the "time navigation controller": send time via sliceNavigationControllers m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow1->GetSliceNavigationController()); m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow2->GetSliceNavigationController()); m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow3->GetSliceNavigationController()); m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow4->GetSliceNavigationController()); renderWindow1->GetSliceNavigationController()->ConnectGeometrySendEvent( mitk::BaseRenderer::GetInstance(renderWindow4->renderWindow())); // reverse connection between sliceNavigationControllers and timeNavigationController renderWindow1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); renderWindow2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); renderWindow3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); //renderWindow4->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow1, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow1, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow1, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow1, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow1, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow1, &QmitkRenderWindow::UpdateCrosshairRotationMode); connect(renderWindow2, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow2, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow2, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow2, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow2, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow2, &QmitkRenderWindow::UpdateCrosshairRotationMode); connect(renderWindow3, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow3, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow3, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow3, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow3, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow3, &QmitkRenderWindow::UpdateCrosshairRotationMode); connect(renderWindow4, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow4, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow4, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow4, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow4, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow4, &QmitkRenderWindow::UpdateCrosshairRotationMode); } diff --git a/Modules/QtWidgetsExt/src/QmitkSliceWidget.cpp b/Modules/QtWidgetsExt/src/QmitkSliceWidget.cpp index 3a8462c70b..7cfbb790eb 100644 --- a/Modules/QtWidgetsExt/src/QmitkSliceWidget.cpp +++ b/Modules/QtWidgetsExt/src/QmitkSliceWidget.cpp @@ -1,266 +1,266 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSliceWidget.h" #include "QmitkStepperAdapter.h" #include "mitkCameraController.h" #include "mitkImage.h" #include "mitkNodePredicateDataType.h" #include #include #include #include QmitkSliceWidget::QmitkSliceWidget(QWidget *parent, const char *name, Qt::WindowFlags f) : QWidget(parent, f) { this->setupUi(this); if (name != nullptr) this->setObjectName(name); popUp = new QMenu(this); popUp->addAction("Axial"); popUp->addAction("Coronal"); popUp->addAction("Sagittal"); QObject::connect(popUp, SIGNAL(triggered(QAction *)), this, SLOT(ChangeView(QAction *))); setPopUpEnabled(false); m_SlicedGeometry = nullptr; m_View = mitk::AnatomicalPlane::Axial; QHBoxLayout *hlayout = new QHBoxLayout(container); hlayout->setMargin(0); // create widget QString composedName("QmitkSliceWidget::"); if (!this->objectName().isEmpty()) composedName += this->objectName(); else composedName += "QmitkGLWidget"; m_RenderWindow = new QmitkRenderWindow(container, composedName); m_Renderer = m_RenderWindow->GetRenderer(); hlayout->addWidget(m_RenderWindow); - new QmitkStepperAdapter(sliceNavigationWidget, m_RenderWindow->GetSliceNavigationController()->GetSlice()); + new QmitkStepperAdapter(sliceNavigationWidget, m_RenderWindow->GetSliceNavigationController()->GetStepper()); SetLevelWindowEnabled(true); } mitk::VtkPropRenderer *QmitkSliceWidget::GetRenderer() { return m_Renderer; } QFrame *QmitkSliceWidget::GetSelectionFrame() { return SelectionFrame; } void QmitkSliceWidget::SetDataStorage(mitk::StandaloneDataStorage::Pointer storage) { m_DataStorage = storage; m_Renderer->SetDataStorage(m_DataStorage); } mitk::StandaloneDataStorage *QmitkSliceWidget::GetDataStorage() { return m_DataStorage; } void QmitkSliceWidget::SetData(mitk::DataStorage::SetOfObjects::ConstIterator it) { SetData(it->Value(), m_View); } void QmitkSliceWidget::SetData(mitk::DataStorage::SetOfObjects::ConstIterator it, mitk::AnatomicalPlane view) { SetData(it->Value(), view); } void QmitkSliceWidget::SetData(mitk::DataNode::Pointer node) { try { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(node); } } catch (...) { } SetData(node, m_View); } void QmitkSliceWidget::SetData(mitk::DataNode::Pointer node, mitk::AnatomicalPlane view) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNull()) { MITK_WARN << "QmitkSliceWidget data is not an image!"; return; } m_SlicedGeometry = image->GetSlicedGeometry(); this->InitWidget(view); } void QmitkSliceWidget::InitWidget(mitk::AnatomicalPlane view) { m_View = view; mitk::SliceNavigationController *controller = m_RenderWindow->GetSliceNavigationController(); if (view == mitk::AnatomicalPlane::Axial) { controller->SetViewDirection(mitk::AnatomicalPlane::Axial); } else if (view == mitk::AnatomicalPlane::Coronal) { controller->SetViewDirection(mitk::AnatomicalPlane::Coronal); } // init sagittal view for all other cases ('original' is covered here as well) else { controller->SetViewDirection(mitk::AnatomicalPlane::Sagittal); } if (m_SlicedGeometry.IsNull()) { return; } mitk::BaseGeometry::Pointer geometry = static_cast(m_SlicedGeometry->Clone().GetPointer()); const mitk::BoundingBox::Pointer boundingbox = m_DataStorage->ComputeVisibleBoundingBox(GetRenderer(), nullptr); if (boundingbox->GetPoints()->Size() > 0) { // let's see if we have data with a limited live-span ... mitk::TimeBounds timebounds = m_DataStorage->ComputeTimeBounds(GetRenderer(), nullptr); mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry, 1); { timeGeometry->SetFirstTimePoint(timebounds[0]); timeGeometry->SetStepDuration(1.0); } if (timeGeometry->GetBoundingBoxInWorld()->GetDiagonalLength2() >= mitk::eps) { controller->SetInputWorldTimeGeometry(timeGeometry); controller->Update(); } } GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdate(GetRenderer()->GetRenderWindow()); } void QmitkSliceWidget::UpdateGL() { GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdate(GetRenderer()->GetRenderWindow()); } void QmitkSliceWidget::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::RightButton && popUpEnabled) { popUp->popup(QCursor::pos()); } } void QmitkSliceWidget::wheelEvent(QWheelEvent *e) { int val = sliceNavigationWidget->GetPos(); if (e->orientation() * e->delta() > 0) { sliceNavigationWidget->SetPos(val + 1); } else { if (val > 0) sliceNavigationWidget->SetPos(val - 1); } } void QmitkSliceWidget::ChangeView(QAction *val) { if (val->text() == "Axial") { InitWidget(mitk::AnatomicalPlane::Axial); } else if (val->text() == "Coronal") { InitWidget(mitk::AnatomicalPlane::Coronal); } else if (val->text() == "Sagittal") { InitWidget(mitk::AnatomicalPlane::Sagittal); } } void QmitkSliceWidget::setPopUpEnabled(bool b) { popUpEnabled = b; } QmitkSliceNavigationWidget* QmitkSliceWidget::GetSliceNavigationWidget() { return sliceNavigationWidget; } void QmitkSliceWidget::SetLevelWindowEnabled(bool enable) { levelWindow->setEnabled(enable); if (!enable) { levelWindow->setMinimumWidth(0); levelWindow->setMaximumWidth(0); } else { levelWindow->setMinimumWidth(28); levelWindow->setMaximumWidth(28); } } bool QmitkSliceWidget::IsLevelWindowEnabled() { return levelWindow->isEnabled(); } QmitkRenderWindow *QmitkSliceWidget::GetRenderWindow() { return m_RenderWindow; } mitk::SliceNavigationController *QmitkSliceWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } mitk::CameraRotationController *QmitkSliceWidget::GetCameraRotationController() const { return m_RenderWindow->GetCameraRotationController(); } mitk::BaseController *QmitkSliceWidget::GetController() const { return m_RenderWindow->GetController(); } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index d4c5bb1b6e..5affb3f67a 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,798 +1,798 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" // Include of the new ImageExtractor #include "mitkMorphologicalOperations.h" #include "mitkPlanarCircle.h" #include "usGetModuleContext.h" // Includes for 3DSurfaceInterpolation #include "mitkImageTimeSelector.h" #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" // includes for resling and overwriting #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include #include "mitkAbstractTransformGeometry.h" #include "mitkLabelSetImage.h" #include "mitkContourModelUtils.h" // #include #include #include #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) bool mitk::SegTool2D::m_SurfaceInterpolationEnabled = true; mitk::SegTool2D::SliceInformation::SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep) : slice(aSlice), plane(aPlane), timestep(aTimestep) { } mitk::SegTool2D::SegTool2D(const char *type, const us::Module *interactorModule) : Tool(type, interactorModule), m_Contourmarkername("Position") { Tool::m_EventConfig = "DisplayConfigBlockLMB.xml"; } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::FilterEvents(InteractionEvent *interactionEvent, DataNode *) { const auto *positionEvent = dynamic_cast(interactionEvent); bool isValidEvent = (positionEvent && // Only events of type mitk::InteractionPositionEvent interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D // Only events from the 2D renderwindows ); return isValidEvent; } bool mitk::SegTool2D::DetermineAffectedImageSlice(const Image *image, const PlaneGeometry *plane, int &affectedDimension, int &affectedSlice) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal0.GetVnlVector())); imageNormal1.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal1.GetVnlVector())); imageNormal2.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal2.GetVnlVector())); double eps(0.00001); // axial if (imageNormal2.GetNorm() <= eps) { affectedDimension = 2; } // sagittal else if (imageNormal1.GetNorm() <= eps) { affectedDimension = 1; } // coronal else if (imageNormal0.GetNorm() <= eps) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image BaseGeometry *imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project(testPoint, projectedPoint); Point3D indexPoint; imageGeometry->WorldToIndex(projectedPoint, indexPoint); affectedSlice = ROUND(indexPoint[affectedDimension]); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if (affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension))) return false; return true; } void mitk::SegTool2D::UpdateSurfaceInterpolation(const Image *slice, const Image *workingImage, const PlaneGeometry *plane, bool detectIntersection) { std::vector slices = { SliceInformation(slice, plane, 0)}; Self::UpdateSurfaceInterpolation(slices, workingImage, detectIntersection, 0, 0); } void mitk::SegTool2D::RemoveContourFromInterpolator(const SliceInformation& sliceInfo) { mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.ContourNormal = sliceInfo.plane->GetNormal(); contourInfo.ContourPoint = sliceInfo.plane->GetOrigin(); mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo); } void mitk::SegTool2D::UpdateSurfaceInterpolation(const std::vector& sliceInfos, const Image* workingImage, bool detectIntersection, unsigned int activeLayerID, mitk::Label::PixelType activeLabelValue) { if (!m_SurfaceInterpolationEnabled) return; //Remark: the ImageTimeSelector is just needed to extract a timestep/channel of //the image in order to get the image dimension (time dimension and channel dimension //stripped away). Therfore it is OK to always use time step 0 and channel 0 mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(workingImage); timeSelector->SetTimeNr(0); timeSelector->SetChannelNr(0); timeSelector->Update(); const auto dimRefImg = timeSelector->GetOutput()->GetDimension(); if (dimRefImg != 3) return; std::vector contourList; contourList.reserve(sliceInfos.size()); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); std::vector relevantSlices = sliceInfos; if (detectIntersection) { relevantSlices.clear(); for (const auto& sliceInfo : sliceInfos) { // Test whether there is something to extract or whether the slice just contains intersections of others mitk::Image::Pointer slice2 = sliceInfo.slice->Clone(); mitk::MorphologicalOperations::Erode(slice2, 2, mitk::MorphologicalOperations::Ball); contourExtractor->SetInput(slice2); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { Self::RemoveContourFromInterpolator(sliceInfo); } else { relevantSlices.push_back(sliceInfo); } } } if (relevantSlices.empty()) return; std::vector contourPlanes; for (const auto& sliceInfo : relevantSlices) { contourExtractor->SetInput(sliceInfo.slice); contourExtractor->SetContourValue(activeLabelValue); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { Self::RemoveContourFromInterpolator(sliceInfo); } else { vtkSmartPointer intArray = vtkSmartPointer::New(); intArray->InsertNextValue(activeLabelValue); intArray->InsertNextValue(activeLayerID); contour->GetVtkPolyData()->GetFieldData()->AddArray(intArray); contour->DisconnectPipeline(); contourList.push_back(contour); contourPlanes.push_back(sliceInfo.plane); } } mitk::SurfaceInterpolationController::GetInstance()->AddNewContours(contourList, contourPlanes); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component /*= 0*/) { if (!positionEvent) { return nullptr; } assert(positionEvent->GetSender()); // sure, right? const auto timeStep = positionEvent->GetSender()->GetTimeStep(image); // get the timestep of the visible part (time-wise) of the image return GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep, component); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, const Image* image, TimePointType timePoint, unsigned int component /*= 0*/) { if (!image || !planeGeometry) { return nullptr; } if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; return SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint), component); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, TimeStepType timeStep, unsigned int component /*= 0*/) { if (!image || !planeGeometry) { return nullptr; } // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); // use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(image); extractor->SetTimeStep(timeStep); extractor->SetWorldGeometry(planeGeometry); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); // additionally extract the given component // default is 0; the extractor checks for multi-component images extractor->SetComponent(component); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent *positionEvent) const { const auto workingNode = this->GetWorkingDataNode(); if (!workingNode) { return nullptr; } const auto *workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) { return nullptr; } return GetAffectedImageSliceAs2DImage(positionEvent, workingImage); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent *positionEvent) const { DataNode* referenceNode = this->GetReferenceDataNode(); if (!referenceNode) { return nullptr; } auto *referenceImage = dynamic_cast(referenceNode->GetData()); if (!referenceImage) { return nullptr; } int displayedComponent = 0; if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent)) { // found the displayed component return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage, displayedComponent); } else { return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage); } } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const { DataNode* referenceNode = this->GetReferenceDataNode(); if (!referenceNode) { return nullptr; } auto* referenceImage = dynamic_cast(referenceNode->GetData()); if (!referenceImage) { return nullptr; } int displayedComponent = 0; if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent)) { // found the displayed component return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep, displayedComponent); } else { return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep); } } void mitk::SegTool2D::Activated() { Superclass::Activated(); this->GetToolManager()->SelectedTimePointChanged += mitk::MessageDelegate(this, &mitk::SegTool2D::OnTimePointChangedInternal); m_LastTimePointTriggered = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); } void mitk::SegTool2D::Deactivated() { this->GetToolManager()->SelectedTimePointChanged -= mitk::MessageDelegate(this, &mitk::SegTool2D::OnTimePointChangedInternal); Superclass::Deactivated(); } void mitk::SegTool2D::OnTimePointChangedInternal() { if (m_IsTimePointChangeAware && nullptr != this->GetWorkingDataNode()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (timePoint != m_LastTimePointTriggered) { m_LastTimePointTriggered = timePoint; this->OnTimePointChanged(); } } } void mitk::SegTool2D::OnTimePointChanged() { //default implementation does nothing } mitk::DataNode* mitk::SegTool2D::GetWorkingDataNode() const { if (nullptr != this->GetToolManager()) { return this->GetToolManager()->GetWorkingData(0); } return nullptr; } mitk::Image* mitk::SegTool2D::GetWorkingData() const { auto node = this->GetWorkingDataNode(); if (nullptr != node) { return dynamic_cast(node->GetData()); } return nullptr; } mitk::DataNode* mitk::SegTool2D::GetReferenceDataNode() const { if (nullptr != this->GetToolManager()) { return this->GetToolManager()->GetReferenceData(0); } return nullptr; } mitk::Image* mitk::SegTool2D::GetReferenceData() const { auto node = this->GetReferenceDataNode(); if (nullptr != node) { return dynamic_cast(node->GetData()); } return nullptr; } void mitk::SegTool2D::WriteBackSegmentationResult(const InteractionPositionEvent *positionEvent, const Image * segmentationResult) { if (!positionEvent) return; const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); const auto *abstractTransformGeometry( dynamic_cast(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (planeGeometry && segmentationResult && !abstractTransformGeometry) { const auto workingNode = this->GetWorkingDataNode(); auto *image = dynamic_cast(workingNode->GetData()); const auto timeStep = positionEvent->GetSender()->GetTimeStep(image); this->WriteBackSegmentationResult(planeGeometry, segmentationResult, timeStep); } } void mitk::SegTool2D::WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep) { if (!planeGeometry || !segmentationResult) return; SliceInformation sliceInfo(segmentationResult, const_cast(planeGeometry), timeStep); Self::WriteBackSegmentationResults(workingNode, { sliceInfo }, true); } void mitk::SegTool2D::WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, const Image * segmentationResult, TimeStepType timeStep) { if (!planeGeometry || !segmentationResult) return; if(m_LastEventSender == nullptr) { return; } unsigned int currentSlicePosition = m_LastEventSender->GetSliceNavigationController()->GetSlice()->GetPos(); SliceInformation sliceInfo(segmentationResult, const_cast(planeGeometry), timeStep); sliceInfo.slicePosition = currentSlicePosition; WriteBackSegmentationResults({ sliceInfo }, true); } void mitk::SegTool2D::WriteBackSegmentationResults(const std::vector &sliceList, bool writeSliceToVolume) { if (sliceList.empty()) { return; } if (nullptr == m_LastEventSender) { MITK_WARN << "Cannot write tool results. Tool seems to be in an invalid state, as no interaction event was recieved but is expected."; return; } const auto workingNode = this->GetWorkingDataNode(); // the first geometry is needed otherwise restoring the position is not working const auto* plane3 = dynamic_cast(dynamic_cast( m_LastEventSender->GetSliceNavigationController()->GetCurrentGeometry3D()) ->GetPlaneGeometry(0)); - unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetSlice()->GetPos(); + unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetStepper()->GetPos(); mitk::SegTool2D::WriteBackSegmentationResults(workingNode, sliceList, writeSliceToVolume); /* A cleaner solution would be to add a contour marker for each slice info. It currently does not work as the contour markers expect that the plane is always the plane of slice 0. Had not the time to do it properly no. Should be solved by T28146*/ this->AddContourmarker(plane3, slicePosition); } void mitk::SegTool2D::WriteBackSegmentationResults(const DataNode* workingNode, const std::vector& sliceList, bool writeSliceToVolume) { if (sliceList.empty()) { return; } if (nullptr == workingNode) { mitkThrow() << "Cannot write slice to working node. Working node is invalid."; } auto image = dynamic_cast(workingNode->GetData()); mitk::Label::PixelType activeLabelValue = 0; unsigned int activeLayerID = 0; try{ auto labelSetImage = dynamic_cast(workingNode->GetData()); activeLayerID = labelSetImage->GetActiveLayer(); activeLabelValue = labelSetImage->GetActiveLabelSet()->GetActiveLabel()->GetValue(); } catch(...) { mitkThrow() << "Working node does not contain labelSetImage."; } if (nullptr == image) { mitkThrow() << "Cannot write slice to working node. Working node does not contain an image."; } for (const auto& sliceInfo : sliceList) { if (writeSliceToVolume && nullptr != sliceInfo.plane && sliceInfo.slice.IsNotNull()) { SegTool2D::WriteSliceToVolume(image, sliceInfo, true); } } SegTool2D::UpdateSurfaceInterpolation(sliceList, image, false, activeLayerID, activeLabelValue); // also mark its node as modified (T27308). Can be removed if T27307 // is properly solved if (workingNode != nullptr) workingNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo) { SliceInformation sliceInfo(slice, planeGeometry, timeStep); WriteSliceToVolume(workingImage, sliceInfo, allowUndo); } void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo) { if (nullptr == workingImage) { mitkThrow() << "Cannot write slice to working node. Working node does not contain an image."; } DiffSliceOperation* undoOperation = nullptr; if (allowUndo) { /*============= BEGIN undo/redo feature block ========================*/ // Create undo operation by caching the not yet modified slices mitk::Image::Pointer originalSlice = GetAffectedImageSliceAs2DImage(sliceInfo.plane, workingImage, sliceInfo.timestep); undoOperation = new DiffSliceOperation(workingImage, originalSlice, dynamic_cast(originalSlice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); /*============= END undo/redo feature block ========================*/ } // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk // reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set the slice as 'input' // casting const away is needed and OK as long the OverwriteMode of // mitkVTKImageOverwrite is true. // Reason: because then the input slice is not touched but // used to overwrite the input of the ExtractSliceFilter. auto noneConstSlice = const_cast(sliceInfo.slice.GetPointer()); reslice->SetInputSlice(noneConstSlice->GetVtkImageData()); // set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(workingImage); extractor->SetTimeStep(sliceInfo.timestep); extractor->SetWorldGeometry(sliceInfo.plane); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(workingImage->GetGeometry(sliceInfo.timestep)); extractor->Modified(); extractor->Update(); // the image was modified within the pipeline, but not marked so workingImage->Modified(); workingImage->GetVtkImageData()->Modified(); if (allowUndo) { /*============= BEGIN undo/redo feature block ========================*/ // specify the redo operation with the edited slice auto* doOperation = new DiffSliceOperation(workingImage, extractor->GetOutput(), dynamic_cast(sliceInfo.slice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); // create an operation event for the undo stack OperationEvent* undoStackItem = new OperationEvent(DiffSliceOperationApplier::GetInstance(), doOperation, undoOperation, "Segmentation"); // add it to the undo controller UndoStackItem::IncCurrObjectEventId(); UndoStackItem::IncCurrGroupEventId(); UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem); /*============= END undo/redo feature block ========================*/ } } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled) { m_SurfaceInterpolationEnabled = enabled; } int mitk::SegTool2D::AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex) { if (planeGeometry == nullptr) return -1; us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); PlanePositionManagerService *service = us::GetModuleContext()->GetService(serviceRef); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(planeGeometry, sliceIndex); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); mitk::Point2D p1; planeGeometry->Map(planeGeometry->GetCenter(), p1); contourMarker->SetPlaneGeometry(planeGeometry->Clone()); contourMarker->PlaceFigure(p1); contourMarker->SetCurrentControlPoint(p1); contourMarker->SetProperty("initiallyplaced", mitk::BoolProperty::New(true)); std::stringstream markerStream; auto workingNode = this->GetWorkingDataNode(); markerStream << m_Contourmarkername; markerStream << " "; markerStream << id + 1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty("name", StringProperty::New(markerStream.str())); rotatedContourNode->SetProperty("isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty("PlanarFigureInitializedWindow", true, m_LastEventSender); rotatedContourNode->SetProperty("includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty("helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); rotatedContourNode->SetProperty("planarfigure.drawcontrolpoints", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawname", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawoutline", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawshadow", BoolProperty::New(false)); if (planeGeometry) { if (id == size) { this->GetToolManager()->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = this->GetToolManager()->GetDataStorage()->GetDerivations(workingNode, isMarker); for (auto iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t + 1).c_str()) - 1; if (id == markerId) { return id; } } this->GetToolManager()->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage(const std::string &message) const { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the " "button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " https://phabricator.mitk.org/" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } void mitk::SegTool2D::WritePreviewOnWorkingImage( Image *targetSlice, const Image *sourceSlice, const Image *workingImage, int paintingPixelValue) { if (nullptr == targetSlice) { mitkThrow() << "Cannot write preview on working image. Target slice does not point to a valid instance."; } if (nullptr == sourceSlice) { mitkThrow() << "Cannot write preview on working image. Source slice does not point to a valid instance."; } if (nullptr == workingImage) { mitkThrow() << "Cannot write preview on working image. Working image does not point to a valid instance."; } auto constVtkSource = sourceSlice->GetVtkImageData(); /*Need to const cast because Vtk interface does not support const correctly. (or I am not experienced enough to use it correctly)*/ auto nonConstVtkSource = const_cast(constVtkSource); ContourModelUtils::FillSliceInSlice(nonConstVtkSource, targetSlice->GetVtkImageData(), workingImage, paintingPixelValue, 1.0); } bool mitk::SegTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent* positionEvent, const mitk::BaseData* data) { bool isPositionEventInsideImageRegion = nullptr != data && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); if (!isPositionEventInsideImageRegion) MITK_WARN("EditableContourTool") << "PositionEvent is outside ImageRegion!"; return isPositionEventInsideImageRegion; } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp index 25cdf72dc0..4af6c5fc88 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp @@ -1,113 +1,113 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // mitk core #include #include // mitk gui common plugin #include // namespace that contains the concrete action namespace ResetGeometryAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, const mitk::TimeGeometry* referenceGeometry, mitk::BaseRenderer* baseRenderer /*= nullptr*/) { if (workbenchPartSite.IsNull()) { return; } auto* renderWindowPart = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); if (nullptr == renderWindowPart) { renderWindowPart = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); if (nullptr == renderWindowPart) { // no render window available return; } } if (nullptr == referenceGeometry) { return; } mitk::TimeStepType imageTimeStep = 0; // store the current position to set it again later, if the camera should not be reset mitk::Point3D currentPosition = renderWindowPart->GetSelectedPosition(); // store the current time step to set it again later, if the camera should not be reset auto* renderingManager = mitk::RenderingManager::GetInstance(); const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (referenceGeometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); } if (nullptr == baseRenderer) { renderingManager->InitializeViews(referenceGeometry, mitk::RenderingManager::REQUEST_UPDATE_ALL, false); } else { renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); } renderWindowPart->SetSelectedPosition(currentPosition); - renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + renderingManager->GetTimeNavigationController()->GetStepper()->SetPos(imageTimeStep); } } // namespace ResetGeometryAction QmitkDataNodeResetGeometryAction::QmitkDataNodeResetGeometryAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { this->setText(tr("Reset geometry")); this->InitializeAction(); } QmitkDataNodeResetGeometryAction::QmitkDataNodeResetGeometryAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QmitkDataNodeResetGeometryAction(parent, berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { } void QmitkDataNodeResetGeometryAction::InitializeAction() { connect(this, &QmitkDataNodeResetGeometryAction::triggered, this, &QmitkDataNodeResetGeometryAction::OnActionTriggered); } void QmitkDataNodeResetGeometryAction::OnActionTriggered(bool /*checked*/) { auto workbenchPartSite = m_WorkbenchPartSite.Lock(); if (workbenchPartSite.IsNull()) { return; } auto baseRenderer = this->GetBaseRenderer(); auto selectedNode = this->GetSelectedNode(); mitk::Image::ConstPointer selectedImage = dynamic_cast(selectedNode->GetData()); if (selectedImage.IsNull()) { return; } ResetGeometryAction::Run(workbenchPartSite, selectedImage->GetTimeGeometry(), baseRenderer); } diff --git a/Plugins/org.mitk.gui.qt.basicimageprocessing/src/internal/QmitkBasicImageProcessingView.cpp b/Plugins/org.mitk.gui.qt.basicimageprocessing/src/internal/QmitkBasicImageProcessingView.cpp index 20fc78bccc..c73170f9ab 100644 --- a/Plugins/org.mitk.gui.qt.basicimageprocessing/src/internal/QmitkBasicImageProcessingView.cpp +++ b/Plugins/org.mitk.gui.qt.basicimageprocessing/src/internal/QmitkBasicImageProcessingView.cpp @@ -1,1345 +1,1345 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkBasicImageProcessingView.h" // QT includes (GUI) #include #include #include #include #include #include #include // MITK includes (general) #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include // ITK includes (general) #include #include // Morphological Operations #include #include #include #include #include // Smoothing #include #include #include // Threshold #include // Inversion #include // Derivatives #include #include #include // Resampling #include #include #include #include #include // Image Arithmetics #include #include #include #include // Boolean operations #include #include #include // Flip Image #include #include #include // Convenient Definitions typedef itk::Image ImageType; typedef itk::Image SegmentationImageType; typedef itk::Image DoubleImageType; typedef itk::Image, 3> VectorImageType; typedef itk::BinaryBallStructuringElement BallType; typedef itk::GrayscaleDilateImageFilter DilationFilterType; typedef itk::GrayscaleErodeImageFilter ErosionFilterType; typedef itk::GrayscaleMorphologicalOpeningImageFilter OpeningFilterType; typedef itk::GrayscaleMorphologicalClosingImageFilter ClosingFilterType; typedef itk::MedianImageFilter< ImageType, ImageType > MedianFilterType; typedef itk::DiscreteGaussianImageFilter< ImageType, ImageType> GaussianFilterType; typedef itk::TotalVariationDenoisingImageFilter TotalVariationFilterType; typedef itk::TotalVariationDenoisingImageFilter VectorTotalVariationFilterType; typedef itk::BinaryThresholdImageFilter< ImageType, ImageType > ThresholdFilterType; typedef itk::InvertIntensityImageFilter< ImageType, ImageType > InversionFilterType; typedef itk::GradientMagnitudeRecursiveGaussianImageFilter< ImageType, ImageType > GradientFilterType; typedef itk::LaplacianImageFilter< DoubleImageType, DoubleImageType > LaplacianFilterType; typedef itk::SobelEdgeDetectionImageFilter< DoubleImageType, DoubleImageType > SobelFilterType; typedef itk::ResampleImageFilter< ImageType, ImageType > ResampleImageFilterType; typedef itk::ResampleImageFilter< ImageType, ImageType > ResampleImageFilterType2; typedef itk::CastImageFilter< ImageType, DoubleImageType > ImagePTypeToFloatPTypeCasterType; typedef itk::AddImageFilter< ImageType, ImageType, ImageType > AddFilterType; typedef itk::SubtractImageFilter< ImageType, ImageType, ImageType > SubtractFilterType; typedef itk::MultiplyImageFilter< ImageType, ImageType, ImageType > MultiplyFilterType; typedef itk::DivideImageFilter< ImageType, ImageType, DoubleImageType > DivideFilterType; typedef itk::OrImageFilter< ImageType, ImageType > OrImageFilterType; typedef itk::AndImageFilter< ImageType, ImageType > AndImageFilterType; typedef itk::XorImageFilter< ImageType, ImageType > XorImageFilterType; typedef itk::FlipImageFilter< ImageType > FlipImageFilterType; typedef itk::LinearInterpolateImageFunction< ImageType, double > LinearInterpolatorType; typedef itk::NearestNeighborInterpolateImageFunction< ImageType, double > NearestInterpolatorType; const std::string QmitkBasicImageProcessing::VIEW_ID = "org.mitk.views.basicimageprocessing"; QmitkBasicImageProcessing::QmitkBasicImageProcessing() : QmitkAbstractView() , m_Controls(new Ui::QmitkBasicImageProcessingViewControls) , m_TimeStepperAdapter(nullptr) { auto isImage = mitk::TNodePredicateDataType::New(); auto isNotHelperObject = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); auto dimensionPredicate = mitk::NodePredicateOr::New( mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)); m_IsImagePredicate = mitk::NodePredicateAnd::New( isImage, isNotHelperObject, dimensionPredicate); } QmitkBasicImageProcessing::~QmitkBasicImageProcessing() { } void QmitkBasicImageProcessing::CreateQtPartControl(QWidget *parent) { m_Controls->setupUi(parent); m_Controls->selectedImageWidget->SetDataStorage(this->GetDataStorage()); m_Controls->selectedImageWidget->SetNodePredicate(m_IsImagePredicate); m_Controls->selectedImageWidget->SetSelectionIsOptional(true); m_Controls->selectedImageWidget->SetAutoSelectNewNodes(true); m_Controls->selectedImageWidget->SetEmptyInfo(QString("Please select a 3D / 4D image")); m_Controls->selectedImageWidget->SetPopUpTitel(QString("Select an image")); m_Controls->selectedImageWidget_2->SetDataStorage(this->GetDataStorage()); m_Controls->selectedImageWidget_2->SetNodePredicate(m_IsImagePredicate); m_Controls->selectedImageWidget_2->SetSelectionIsOptional(true); m_Controls->selectedImageWidget_2->SetAutoSelectNewNodes(true); m_Controls->selectedImageWidget_2->SetEmptyInfo(QString("Please select a 3D / 4D image")); m_Controls->selectedImageWidget_2->SetPopUpTitel(QString("Select an image")); m_Controls->gbTwoImageOps->hide(); m_Controls->cbWhat1->clear(); m_Controls->cbWhat1->insertItem(NOACTIONSELECTED, "Please select an operation"); m_Controls->cbWhat1->insertItem(CATEGORY_DENOISING, "--- Denoising ---"); m_Controls->cbWhat1->insertItem(GAUSSIAN, "Gaussian"); m_Controls->cbWhat1->insertItem(MEDIAN, "Median"); m_Controls->cbWhat1->insertItem(TOTALVARIATION, "Total Variation"); m_Controls->cbWhat1->insertItem(CATEGORY_MORPHOLOGICAL, "--- Morphological ---"); m_Controls->cbWhat1->insertItem(DILATION, "Dilation"); m_Controls->cbWhat1->insertItem(EROSION, "Erosion"); m_Controls->cbWhat1->insertItem(OPENING, "Opening"); m_Controls->cbWhat1->insertItem(CLOSING, "Closing"); m_Controls->cbWhat1->insertItem(CATEGORY_EDGE_DETECTION, "--- Edge Detection ---"); m_Controls->cbWhat1->insertItem(GRADIENT, "Gradient"); m_Controls->cbWhat1->insertItem(LAPLACIAN, "Laplacian (2nd Derivative)"); m_Controls->cbWhat1->insertItem(SOBEL, "Sobel Operator"); m_Controls->cbWhat1->insertItem(CATEGORY_MISC, "--- Misc ---"); m_Controls->cbWhat1->insertItem(THRESHOLD, "Threshold"); m_Controls->cbWhat1->insertItem(INVERSION, "Image Inversion"); m_Controls->cbWhat1->insertItem(DOWNSAMPLING, "Downsampling"); m_Controls->cbWhat1->insertItem(FLIPPING, "Flipping"); m_Controls->cbWhat1->insertItem(RESAMPLING, "Resample to"); m_Controls->cbWhat1->insertItem(RESCALE, "Rescale values to interval"); m_Controls->cbWhat1->insertItem(RESCALE2, "Rescale values by scalar"); m_Controls->cbWhat2->clear(); m_Controls->cbWhat2->insertItem(TWOIMAGESNOACTIONSELECTED, "Please select an operation"); m_Controls->cbWhat2->insertItem(CATEGORY_ARITHMETIC, "--- Arithmetric operations ---"); m_Controls->cbWhat2->insertItem(ADD, "Add to Image 1:"); m_Controls->cbWhat2->insertItem(SUBTRACT, "Subtract from Image 1:"); m_Controls->cbWhat2->insertItem(MULTIPLY, "Multiply with Image 1:"); m_Controls->cbWhat2->insertItem(RESAMPLE_TO, "Resample Image 1 to fit geometry:"); m_Controls->cbWhat2->insertItem(DIVIDE, "Divide Image 1 by:"); m_Controls->cbWhat2->insertItem(CATEGORY_BOOLEAN, "--- Boolean operations ---"); m_Controls->cbWhat2->insertItem(AND, "AND"); m_Controls->cbWhat2->insertItem(OR, "OR"); m_Controls->cbWhat2->insertItem(XOR, "XOR"); m_Controls->cbParam4->clear(); m_Controls->cbParam4->insertItem(LINEAR, "Linear"); m_Controls->cbParam4->insertItem(NEAREST, "Nearest neighbor"); m_Controls->dsbParam1->hide(); m_Controls->dsbParam2->hide(); m_Controls->dsbParam3->hide(); m_Controls->tlParam3->hide(); m_Controls->tlParam4->hide(); m_Controls->cbParam4->hide(); this->CreateConnections(); } void QmitkBasicImageProcessing::CreateConnections() { connect(m_Controls->cbWhat1, QOverload::of(&QComboBox::activated), this, &QmitkBasicImageProcessing::SelectAction); connect(m_Controls->btnDoIt, &QPushButton::clicked, this, &QmitkBasicImageProcessing::StartButtonClicked); connect(m_Controls->cbWhat2, QOverload::of(&QComboBox::activated), this, &QmitkBasicImageProcessing::SelectAction2); connect(m_Controls->btnDoIt2, &QPushButton::clicked, this, &QmitkBasicImageProcessing::StartButton2Clicked); connect(m_Controls->rBOneImOp, &QRadioButton::clicked, this, &QmitkBasicImageProcessing::ChangeGUI); connect(m_Controls->rBTwoImOp, &QRadioButton::clicked, this, &QmitkBasicImageProcessing::ChangeGUI); connect(m_Controls->cbParam4, QOverload::of(&QComboBox::activated), this, &QmitkBasicImageProcessing::SelectInterpolator); connect(m_Controls->selectedImageWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkBasicImageProcessing::OnCurrentSelectionChanged); connect(m_Controls->selectedImageWidget_2, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkBasicImageProcessing::OnCurrentSelectionChanged); } void QmitkBasicImageProcessing::InternalGetTimeNavigationController() { auto renwin_part = GetRenderWindowPart(); if( renwin_part != nullptr ) { auto tnc = renwin_part->GetTimeNavigationController(); if( tnc != nullptr ) { - m_TimeStepperAdapter = new QmitkStepperAdapter(m_Controls->timeSliceNavigationWidget, tnc->GetTime()); + m_TimeStepperAdapter = new QmitkStepperAdapter(m_Controls->timeSliceNavigationWidget, tnc->GetStepper()); } } } void QmitkBasicImageProcessing::SetFocus() { m_Controls->rBOneImOp->setFocus(); } void QmitkBasicImageProcessing::OnCurrentSelectionChanged(const QList& nodes) { if (nodes.empty() || nodes.front().IsNull()) { m_Controls->timeSliceNavigationWidget->setEnabled(false); m_Controls->tlTime->setEnabled(false); m_Controls->tlWhat1->setEnabled(false); m_Controls->cbWhat1->setEnabled(false); m_Controls->tlWhat2->setEnabled(false); m_Controls->cbWhat2->setEnabled(false); return; } auto selectedImage = dynamic_cast(nodes.front()->GetData()); if (nullptr == selectedImage) { return; } if (selectedImage->GetDimension() > 3) { // try to retrieve the TNC (for 4-D Processing ) this->InternalGetTimeNavigationController(); m_Controls->timeSliceNavigationWidget->setEnabled(true); m_Controls->tlTime->setEnabled(true); } m_Controls->tlWhat1->setEnabled(true); m_Controls->cbWhat1->setEnabled(true); m_Controls->tlWhat2->setEnabled(true); m_Controls->cbWhat2->setEnabled(true); } void QmitkBasicImageProcessing::ChangeGUI() { if(m_Controls->rBOneImOp->isChecked()) { m_Controls->gbTwoImageOps->hide(); m_Controls->gbOneImageOps->show(); } else if(m_Controls->rBTwoImOp->isChecked()) { m_Controls->gbOneImageOps->hide(); m_Controls->gbTwoImageOps->show(); } } void QmitkBasicImageProcessing::ResetParameterPanel() { m_Controls->tlParam->setEnabled(false); m_Controls->tlParam1->setEnabled(false); m_Controls->tlParam2->setEnabled(false); m_Controls->tlParam3->setEnabled(false); m_Controls->tlParam4->setEnabled(false); m_Controls->sbParam1->setEnabled(false); m_Controls->sbParam2->setEnabled(false); m_Controls->dsbParam1->setEnabled(false); m_Controls->dsbParam2->setEnabled(false); m_Controls->dsbParam3->setEnabled(false); m_Controls->cbParam4->setEnabled(false); m_Controls->sbParam1->setValue(0); m_Controls->sbParam2->setValue(0); m_Controls->dsbParam1->setValue(0); m_Controls->dsbParam2->setValue(0); m_Controls->dsbParam3->setValue(0); m_Controls->sbParam1->show(); m_Controls->sbParam2->show(); m_Controls->dsbParam1->hide(); m_Controls->dsbParam2->hide(); m_Controls->dsbParam3->hide(); m_Controls->cbParam4->hide(); m_Controls->tlParam3->hide(); m_Controls->tlParam4->hide(); } void QmitkBasicImageProcessing::SelectAction(int action) { auto selectedImage = m_Controls->selectedImageWidget->GetSelectedNode(); if (selectedImage.IsNull()) { return; } // Prepare GUI this->ResetParameterPanel(); m_Controls->btnDoIt->setEnabled(false); m_Controls->cbHideOrig->setEnabled(false); QString text1 = tr("No Parameters"); QString text2 = text1; QString text3 = text1; QString text4 = text1; if (action != 19) { m_Controls->dsbParam1->hide(); m_Controls->dsbParam2->hide(); m_Controls->dsbParam3->hide(); m_Controls->tlParam1->show(); m_Controls->tlParam2->show(); m_Controls->tlParam3->hide(); m_Controls->tlParam4->hide(); m_Controls->sbParam1->show(); m_Controls->sbParam2->show(); m_Controls->cbParam4->hide(); } switch (action) { case 2: { m_SelectedAction = GAUSSIAN; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->hide(); m_Controls->dsbParam1->show(); m_Controls->dsbParam1->setEnabled(true); text1 = tr("&Variance:"); m_Controls->tlParam2->hide(); m_Controls->sbParam2->hide(); m_Controls->dsbParam1->setMinimum( 0 ); m_Controls->dsbParam1->setMaximum( 200 ); m_Controls->dsbParam1->setValue( 2 ); break; } case 3: { m_SelectedAction = MEDIAN; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("&Radius:"); m_Controls->sbParam1->setMinimum( 0 ); m_Controls->sbParam1->setMaximum( 200 ); m_Controls->sbParam1->setValue( 3 ); break; } case 4: { m_SelectedAction = TOTALVARIATION; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); m_Controls->tlParam2->setEnabled(true); m_Controls->sbParam2->setEnabled(true); text1 = tr("Number Iterations:"); text2 = tr("Regularization\n(Lambda/1000):"); m_Controls->sbParam1->setMinimum( 1 ); m_Controls->sbParam1->setMaximum( 1000 ); m_Controls->sbParam1->setValue( 40 ); m_Controls->sbParam2->setMinimum( 0 ); m_Controls->sbParam2->setMaximum( 100000 ); m_Controls->sbParam2->setValue( 1 ); break; } case 6: { m_SelectedAction = DILATION; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("&Radius:"); m_Controls->sbParam1->setMinimum( 0 ); m_Controls->sbParam1->setMaximum( 200 ); m_Controls->sbParam1->setValue( 3 ); break; } case 7: { m_SelectedAction = EROSION; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("&Radius:"); m_Controls->sbParam1->setMinimum( 0 ); m_Controls->sbParam1->setMaximum( 200 ); m_Controls->sbParam1->setValue( 3 ); break; } case 8: { m_SelectedAction = OPENING; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("&Radius:"); m_Controls->sbParam1->setMinimum( 0 ); m_Controls->sbParam1->setMaximum( 200 ); m_Controls->sbParam1->setValue( 3 ); break; } case 9: { m_SelectedAction = CLOSING; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("&Radius:"); m_Controls->sbParam1->setMinimum( 0 ); m_Controls->sbParam1->setMaximum( 200 ); m_Controls->sbParam1->setValue( 3 ); break; } case 11: { m_SelectedAction = GRADIENT; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->hide(); m_Controls->dsbParam1->show(); m_Controls->dsbParam1->setEnabled(true); text1 = tr("Sigma of Gaussian Kernel:\n(in Image Spacing Units)"); m_Controls->tlParam2->hide(); m_Controls->sbParam2->hide(); m_Controls->dsbParam1->setMinimum( 0 ); m_Controls->dsbParam1->setMaximum( 200 ); m_Controls->dsbParam1->setValue( 2 ); break; } case 12: { m_SelectedAction = LAPLACIAN; break; } case 13: { m_SelectedAction = SOBEL; break; } case 15: { m_SelectedAction = THRESHOLD; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); m_Controls->tlParam2->setEnabled(true); m_Controls->sbParam2->setEnabled(true); text1 = tr("Lower threshold:"); text2 = tr("Upper threshold:"); m_Controls->sbParam1->setMinimum( -100000 ); m_Controls->sbParam1->setMaximum( 100000 ); m_Controls->sbParam1->setValue( 0 ); m_Controls->sbParam2->setMinimum( -100000 ); m_Controls->sbParam2->setMaximum( 100000 ); m_Controls->sbParam2->setValue( 300 ); break; } case 16: { m_SelectedAction = INVERSION; break; } case 17: { m_SelectedAction = DOWNSAMPLING; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("Downsampling by Factor:"); m_Controls->sbParam1->setMinimum( 1 ); m_Controls->sbParam1->setMaximum( 100 ); m_Controls->sbParam1->setValue( 2 ); break; } case 18: { m_SelectedAction = FLIPPING; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(true); text1 = tr("Flip across axis:"); m_Controls->sbParam1->setMinimum( 0 ); m_Controls->sbParam1->setMaximum( 2 ); m_Controls->sbParam1->setValue( 1 ); break; } case 19: { m_SelectedAction = RESAMPLING; m_Controls->tlParam1->setEnabled(true); m_Controls->sbParam1->setEnabled(false); m_Controls->sbParam1->hide(); m_Controls->dsbParam1->show(); m_Controls->dsbParam1->setEnabled(true); m_Controls->tlParam2->setEnabled(true); m_Controls->sbParam2->setEnabled(false); m_Controls->sbParam2->hide(); m_Controls->dsbParam2->show(); m_Controls->dsbParam2->setEnabled(true); m_Controls->tlParam3->show(); m_Controls->tlParam3->setEnabled(true); m_Controls->dsbParam3->show(); m_Controls->dsbParam3->setEnabled(true); m_Controls->tlParam4->show(); m_Controls->tlParam4->setEnabled(true); m_Controls->cbParam4->show(); m_Controls->cbParam4->setEnabled(true); m_Controls->dsbParam1->setMinimum(0.01); m_Controls->dsbParam1->setMaximum(10.0); m_Controls->dsbParam1->setSingleStep(0.1); m_Controls->dsbParam1->setValue(0.3); m_Controls->dsbParam2->setMinimum(0.01); m_Controls->dsbParam2->setMaximum(10.0); m_Controls->dsbParam2->setSingleStep(0.1); m_Controls->dsbParam2->setValue(0.3); m_Controls->dsbParam3->setMinimum(0.01); m_Controls->dsbParam3->setMaximum(10.0); m_Controls->dsbParam3->setSingleStep(0.1); m_Controls->dsbParam3->setValue(1.5); text1 = tr("x-spacing:"); text2 = tr("y-spacing:"); text3 = tr("z-spacing:"); text4 = tr("Interplation:"); break; } case 20: { m_SelectedAction = RESCALE; m_Controls->dsbParam1->show(); m_Controls->tlParam1->show(); m_Controls->dsbParam1->setEnabled(true); m_Controls->tlParam1->setEnabled(true); m_Controls->dsbParam2->show(); m_Controls->tlParam2->show(); m_Controls->dsbParam2->setEnabled(true); m_Controls->tlParam2->setEnabled(true); text1 = tr("Output minimum:"); text2 = tr("Output maximum:"); break; } case 21: { m_SelectedAction = RESCALE2; m_Controls->dsbParam1->show(); m_Controls->tlParam1->show(); m_Controls->dsbParam1->setEnabled(true); m_Controls->tlParam1->setEnabled(true); text1 = tr("Scaling value:"); break; } default: return; } m_Controls->tlParam->setEnabled(true); m_Controls->tlParam1->setText(text1); m_Controls->tlParam2->setText(text2); m_Controls->tlParam3->setText(text3); m_Controls->tlParam4->setText(text4); m_Controls->btnDoIt->setEnabled(true); m_Controls->cbHideOrig->setEnabled(true); } void QmitkBasicImageProcessing::StartButtonClicked() { auto selectedNode = m_Controls->selectedImageWidget->GetSelectedNode(); if (selectedNode.IsNull()) { return; } this->BusyCursorOn(); mitk::Image::Pointer newImage; try { newImage = dynamic_cast(selectedNode->GetData()); } catch ( std::exception &e ) { QString exceptionString = tr("An error occured during image loading:\n"); exceptionString.append( e.what() ); QMessageBox::warning( nullptr, "Basic Image Processing", exceptionString , QMessageBox::Ok, QMessageBox::NoButton ); this->BusyCursorOff(); return; } // check if input image is valid, casting does not throw exception when casting from 'nullptr-Object' if ( (! newImage) || (newImage->IsInitialized() == false) ) { this->BusyCursorOff(); QMessageBox::warning( nullptr, "Basic Image Processing", tr("Input image is broken or not initialized. Returning."), QMessageBox::Ok, QMessageBox::NoButton ); return; } // check if operation is done on 4D a image time step if(newImage->GetDimension() > 3) { auto timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(newImage); timeSelector->SetTimeNr(m_Controls->timeSliceNavigationWidget->GetPos() ); timeSelector->Update(); newImage = timeSelector->GetOutput(); } // check if image or vector image auto itkImage = ImageType::New(); auto itkVecImage = VectorImageType::New(); int isVectorImage = newImage->GetPixelType().GetNumberOfComponents(); if(isVectorImage > 1) { CastToItkImage( newImage, itkVecImage ); } else { CastToItkImage( newImage, itkImage ); } std::stringstream nameAddition(""); int param1 = m_Controls->sbParam1->value(); int param2 = m_Controls->sbParam2->value(); double dparam1 = m_Controls->dsbParam1->value(); double dparam2 = m_Controls->dsbParam2->value(); double dparam3 = m_Controls->dsbParam3->value(); try { switch (m_SelectedAction) { case GAUSSIAN: { GaussianFilterType::Pointer gaussianFilter = GaussianFilterType::New(); gaussianFilter->SetInput( itkImage ); gaussianFilter->SetVariance( dparam1 ); gaussianFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(gaussianFilter->GetOutput())->Clone(); nameAddition << "_Gaussian_var_" << dparam1; std::cout << "Gaussian filtering successful." << std::endl; break; } case MEDIAN: { MedianFilterType::Pointer medianFilter = MedianFilterType::New(); MedianFilterType::InputSizeType size; size.Fill(param1); medianFilter->SetRadius( size ); medianFilter->SetInput(itkImage); medianFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(medianFilter->GetOutput())->Clone(); nameAddition << "_Median_radius_" << param1; std::cout << "Median Filtering successful." << std::endl; break; } case TOTALVARIATION: { if(isVectorImage > 1) { VectorTotalVariationFilterType::Pointer TVFilter = VectorTotalVariationFilterType::New(); TVFilter->SetInput( itkVecImage.GetPointer() ); TVFilter->SetNumberIterations(param1); TVFilter->SetLambda(double(param2)/1000.); TVFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(TVFilter->GetOutput())->Clone(); } else { ImagePTypeToFloatPTypeCasterType::Pointer floatCaster = ImagePTypeToFloatPTypeCasterType::New(); floatCaster->SetInput( itkImage ); floatCaster->Update(); DoubleImageType::Pointer fImage = floatCaster->GetOutput(); TotalVariationFilterType::Pointer TVFilter = TotalVariationFilterType::New(); TVFilter->SetInput( fImage.GetPointer() ); TVFilter->SetNumberIterations(param1); TVFilter->SetLambda(double(param2)/1000.); TVFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(TVFilter->GetOutput())->Clone(); } nameAddition << "_TV_Iter_" << param1 << "_L_" << param2; std::cout << "Total Variation Filtering successful." << std::endl; break; } case DILATION: { BallType binaryBall; binaryBall.SetRadius( param1 ); binaryBall.CreateStructuringElement(); DilationFilterType::Pointer dilationFilter = DilationFilterType::New(); dilationFilter->SetInput( itkImage ); dilationFilter->SetKernel( binaryBall ); dilationFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(dilationFilter->GetOutput())->Clone(); nameAddition << "_Dilated_by_" << param1; std::cout << "Dilation successful." << std::endl; break; } case EROSION: { BallType binaryBall; binaryBall.SetRadius( param1 ); binaryBall.CreateStructuringElement(); ErosionFilterType::Pointer erosionFilter = ErosionFilterType::New(); erosionFilter->SetInput( itkImage ); erosionFilter->SetKernel( binaryBall ); erosionFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(erosionFilter->GetOutput())->Clone(); nameAddition << "_Eroded_by_" << param1; std::cout << "Erosion successful." << std::endl; break; } case OPENING: { BallType binaryBall; binaryBall.SetRadius( param1 ); binaryBall.CreateStructuringElement(); OpeningFilterType::Pointer openFilter = OpeningFilterType::New(); openFilter->SetInput( itkImage ); openFilter->SetKernel( binaryBall ); openFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(openFilter->GetOutput())->Clone(); nameAddition << "_Opened_by_" << param1; std::cout << "Opening successful." << std::endl; break; } case CLOSING: { BallType binaryBall; binaryBall.SetRadius( param1 ); binaryBall.CreateStructuringElement(); ClosingFilterType::Pointer closeFilter = ClosingFilterType::New(); closeFilter->SetInput( itkImage ); closeFilter->SetKernel( binaryBall ); closeFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(closeFilter->GetOutput())->Clone(); nameAddition << "_Closed_by_" << param1; std::cout << "Closing successful." << std::endl; break; } case GRADIENT: { GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetInput( itkImage ); gradientFilter->SetSigma( dparam1 ); gradientFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(gradientFilter->GetOutput())->Clone(); nameAddition << "_Gradient_sigma_" << dparam1; std::cout << "Gradient calculation successful." << std::endl; break; } case LAPLACIAN: { // the laplace filter requires a float type image as input, we need to cast the itkImage // to correct type ImagePTypeToFloatPTypeCasterType::Pointer caster = ImagePTypeToFloatPTypeCasterType::New(); caster->SetInput( itkImage ); caster->Update(); DoubleImageType::Pointer fImage = caster->GetOutput(); LaplacianFilterType::Pointer laplacianFilter = LaplacianFilterType::New(); laplacianFilter->SetInput( fImage ); laplacianFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(laplacianFilter->GetOutput())->Clone(); nameAddition << "_Second_Derivative"; std::cout << "Laplacian filtering successful." << std::endl; break; } case SOBEL: { // the sobel filter requires a float type image as input, we need to cast the itkImage // to correct type ImagePTypeToFloatPTypeCasterType::Pointer caster = ImagePTypeToFloatPTypeCasterType::New(); caster->SetInput( itkImage ); caster->Update(); DoubleImageType::Pointer fImage = caster->GetOutput(); SobelFilterType::Pointer sobelFilter = SobelFilterType::New(); sobelFilter->SetInput( fImage ); sobelFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(sobelFilter->GetOutput())->Clone(); nameAddition << "_Sobel"; std::cout << "Edge Detection successful." << std::endl; break; } case THRESHOLD: { ThresholdFilterType::Pointer thFilter = ThresholdFilterType::New(); thFilter->SetLowerThreshold(param1 < param2 ? param1 : param2); thFilter->SetUpperThreshold(param2 > param1 ? param2 : param1); thFilter->SetInsideValue(1); thFilter->SetOutsideValue(0); thFilter->SetInput(itkImage); thFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(thFilter->GetOutput())->Clone(); nameAddition << "_Threshold"; std::cout << "Thresholding successful." << std::endl; break; } case INVERSION: { InversionFilterType::Pointer invFilter = InversionFilterType::New(); mitk::ScalarType min = newImage->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = newImage->GetStatistics()->GetScalarValueMax(); invFilter->SetMaximum( max + min ); invFilter->SetInput(itkImage); invFilter->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(invFilter->GetOutput())->Clone(); nameAddition << "_Inverted"; std::cout << "Image inversion successful." << std::endl; break; } case DOWNSAMPLING: { ResampleImageFilterType::Pointer downsampler = ResampleImageFilterType::New(); downsampler->SetInput( itkImage ); NearestInterpolatorType::Pointer interpolator = NearestInterpolatorType::New(); downsampler->SetInterpolator( interpolator ); downsampler->SetDefaultPixelValue( 0 ); ResampleImageFilterType::SpacingType spacing = itkImage->GetSpacing(); spacing *= (double) param1; downsampler->SetOutputSpacing( spacing ); downsampler->SetOutputOrigin( itkImage->GetOrigin() ); downsampler->SetOutputDirection( itkImage->GetDirection() ); ResampleImageFilterType::SizeType size = itkImage->GetLargestPossibleRegion().GetSize(); for ( int i = 0; i < 3; ++i ) { size[i] /= param1; } downsampler->SetSize( size ); downsampler->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(downsampler->GetOutput())->Clone(); nameAddition << "_Downsampled_by_" << param1; std::cout << "Downsampling successful." << std::endl; break; } case FLIPPING: { FlipImageFilterType::Pointer flipper = FlipImageFilterType::New(); flipper->SetInput( itkImage ); itk::FixedArray flipAxes; for(int i=0; i<3; ++i) { if(i == param1) { flipAxes[i] = true; } else { flipAxes[i] = false; } } flipper->SetFlipAxes(flipAxes); flipper->UpdateLargestPossibleRegion(); newImage = mitk::ImportItkImage(flipper->GetOutput())->Clone(); std::cout << "Image flipping successful." << std::endl; break; } case RESAMPLING: { std::string selectedInterpolator; ResampleImageFilterType::Pointer resampler = ResampleImageFilterType::New(); switch (m_SelectedInterpolation) { case LINEAR: { LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); resampler->SetInterpolator(interpolator); selectedInterpolator = "Linear"; break; } case NEAREST: { NearestInterpolatorType::Pointer interpolator = NearestInterpolatorType::New(); resampler->SetInterpolator(interpolator); selectedInterpolator = "Nearest"; break; } default: { LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); resampler->SetInterpolator(interpolator); selectedInterpolator = "Linear"; break; } } resampler->SetInput( itkImage ); resampler->SetOutputOrigin( itkImage->GetOrigin() ); ImageType::SizeType input_size = itkImage->GetLargestPossibleRegion().GetSize(); ImageType::SpacingType input_spacing = itkImage->GetSpacing(); ImageType::SizeType output_size; ImageType::SpacingType output_spacing; output_size[0] = input_size[0] * (input_spacing[0] / dparam1); output_size[1] = input_size[1] * (input_spacing[1] / dparam2); output_size[2] = input_size[2] * (input_spacing[2] / dparam3); output_spacing [0] = dparam1; output_spacing [1] = dparam2; output_spacing [2] = dparam3; resampler->SetSize( output_size ); resampler->SetOutputSpacing( output_spacing ); resampler->SetOutputDirection( itkImage->GetDirection() ); resampler->UpdateLargestPossibleRegion(); ImageType::Pointer resampledImage = resampler->GetOutput(); newImage = mitk::ImportItkImage( resampledImage )->Clone(); nameAddition << "_Resampled_" << selectedInterpolator; std::cout << "Resampling successful." << std::endl; break; } case RESCALE: { DoubleImageType::Pointer floatImage = DoubleImageType::New(); CastToItkImage( newImage, floatImage ); itk::RescaleIntensityImageFilter::Pointer filter = itk::RescaleIntensityImageFilter::New(); filter->SetInput(0, floatImage); filter->SetOutputMinimum(dparam1); filter->SetOutputMaximum(dparam2); filter->Update(); floatImage = filter->GetOutput(); newImage = mitk::Image::New(); newImage->InitializeByItk(floatImage.GetPointer()); newImage->SetVolume(floatImage->GetBufferPointer()); nameAddition << "_Rescaled"; std::cout << "Rescaling successful." << std::endl; break; } case RESCALE2: { DoubleImageType::Pointer floatImage = DoubleImageType::New(); CastToItkImage( newImage, floatImage ); itk::ShiftScaleImageFilter::Pointer filter = itk::ShiftScaleImageFilter::New(); filter->SetInput(0, floatImage); filter->SetScale(dparam1); filter->Update(); floatImage = filter->GetOutput(); newImage = mitk::Image::New(); newImage->InitializeByItk(floatImage.GetPointer()); newImage->SetVolume(floatImage->GetBufferPointer()); nameAddition << "_Rescaled"; std::cout << "Rescaling successful." << std::endl; break; } default: this->BusyCursorOff(); return; } } catch (...) { this->BusyCursorOff(); QMessageBox::warning(nullptr, "Warning", "Problem when applying filter operation. Check your input..."); return; } newImage->DisconnectPipeline(); // adjust level/window to new image mitk::LevelWindow levelwindow; levelwindow.SetAuto( newImage ); auto levWinProp = mitk::LevelWindowProperty::New(); levWinProp->SetLevelWindow( levelwindow ); // compose new image name std::string name = selectedNode->GetName(); if (name.find(".nrrd") == name.size() -5 ) { name = name.substr(0,name.size() -5); } name.append( nameAddition.str() ); // create final result MITK data storage node auto result = mitk::DataNode::New(); result->SetProperty( "levelwindow", levWinProp ); result->SetProperty( "name", mitk::StringProperty::New( name.c_str() ) ); result->SetData( newImage ); // for vector images, a different mapper is needed if(isVectorImage > 1) { auto mapper = mitk::VectorImageMapper2D::New(); result->SetMapper(1,mapper); } // add new image to data storage and set as active to ease further processing GetDataStorage()->Add(result, selectedNode); if (m_Controls->cbHideOrig->isChecked() == true) { selectedNode->SetProperty("visible", mitk::BoolProperty::New(false)); } // show the results mitk::RenderingManager::GetInstance()->RequestUpdateAll(); this->BusyCursorOff(); } void QmitkBasicImageProcessing::SelectAction2(int operation) { switch (operation) { case 2: m_SelectedOperation = ADD; break; case 3: m_SelectedOperation = SUBTRACT; break; case 4: m_SelectedOperation = MULTIPLY; break; case 5: m_SelectedOperation = DIVIDE; break; case 6: m_SelectedOperation = RESAMPLE_TO; break; case 8: m_SelectedOperation = AND; break; case 9: m_SelectedOperation = OR; break; case 10: m_SelectedOperation = XOR; break; default: return; } m_Controls->selectedImageLabel_2->setEnabled(true); m_Controls->selectedImageWidget_2->setEnabled(true); m_Controls->btnDoIt2->setEnabled(true); } void QmitkBasicImageProcessing::StartButton2Clicked() { auto selectedNode = m_Controls->selectedImageWidget->GetSelectedNode(); if (selectedNode.IsNull()) { return; } auto selectedNode2 = m_Controls->selectedImageWidget_2->GetSelectedNode(); if (selectedNode2.IsNull()) { return; } mitk::Image::Pointer newImage1 = dynamic_cast(selectedNode->GetData()); mitk::Image::Pointer newImage2 = dynamic_cast(selectedNode2->GetData()); // check if images are valid if(newImage1.IsNull() || newImage2.IsNull() || false == newImage1->IsInitialized() || false == newImage2->IsInitialized()) { itkGenericExceptionMacro(<< "At least one of the input images is broken or not initialized."); return; } this->BusyCursorOn(); // check if 4D image and use filter on correct time step if(newImage1->GetDimension() > 3) { auto timeSelector = mitk::ImageTimeSelector::New(); auto sn_widget = static_cast(m_Controls->timeSliceNavigationWidget); int time = 0; if( sn_widget != nullptr ) time = sn_widget->GetPos(); timeSelector->SetInput(newImage1); timeSelector->SetTimeNr( time ); timeSelector->UpdateLargestPossibleRegion(); newImage1 = timeSelector->GetOutput(); newImage1->DisconnectPipeline(); timeSelector->SetInput(newImage2); timeSelector->SetTimeNr( time ); timeSelector->UpdateLargestPossibleRegion(); newImage2 = timeSelector->GetOutput(); newImage2->DisconnectPipeline(); } auto itkImage1 = ImageType::New(); auto itkImage2 = ImageType::New(); CastToItkImage( newImage1, itkImage1 ); CastToItkImage( newImage2, itkImage2 ); std::string nameAddition = ""; try { switch (m_SelectedOperation) { case ADD: { AddFilterType::Pointer addFilter = AddFilterType::New(); addFilter->SetInput1( itkImage1 ); addFilter->SetInput2( itkImage2 ); addFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(addFilter->GetOutput())->Clone(); nameAddition = "_Added"; } break; case SUBTRACT: { SubtractFilterType::Pointer subFilter = SubtractFilterType::New(); subFilter->SetInput1( itkImage1 ); subFilter->SetInput2( itkImage2 ); subFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(subFilter->GetOutput())->Clone(); nameAddition = "_Subtracted"; } break; case MULTIPLY: { MultiplyFilterType::Pointer multFilter = MultiplyFilterType::New(); multFilter->SetInput1( itkImage1 ); multFilter->SetInput2( itkImage2 ); multFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(multFilter->GetOutput())->Clone(); nameAddition = "_Multiplied"; } break; case DIVIDE: { DivideFilterType::Pointer divFilter = DivideFilterType::New(); divFilter->SetInput1( itkImage1 ); divFilter->SetInput2( itkImage2 ); divFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(divFilter->GetOutput())->Clone(); nameAddition = "_Divided"; } break; case AND: { AndImageFilterType::Pointer andFilter = AndImageFilterType::New(); andFilter->SetInput1( itkImage1 ); andFilter->SetInput2( itkImage2 ); andFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(andFilter->GetOutput())->Clone(); nameAddition = "_AND"; break; } case OR: { OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput1( itkImage1 ); orFilter->SetInput2( itkImage2 ); orFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(orFilter->GetOutput())->Clone(); nameAddition = "_OR"; break; } case XOR: { XorImageFilterType::Pointer xorFilter = XorImageFilterType::New(); xorFilter->SetInput1( itkImage1 ); xorFilter->SetInput2( itkImage2 ); xorFilter->UpdateLargestPossibleRegion(); newImage1 = mitk::ImportItkImage(xorFilter->GetOutput())->Clone(); nameAddition = "_XOR"; break; } case RESAMPLE_TO: { itk::BSplineInterpolateImageFunction::Pointer bspl_interpolator = itk::BSplineInterpolateImageFunction::New(); bspl_interpolator->SetSplineOrder( 3 ); itk::NearestNeighborInterpolateImageFunction< DoubleImageType >::Pointer nn_interpolator = itk::NearestNeighborInterpolateImageFunction< DoubleImageType>::New(); DoubleImageType::Pointer itkImage1 = DoubleImageType::New(); DoubleImageType::Pointer itkImage2 = DoubleImageType::New(); CastToItkImage( newImage1, itkImage1 ); CastToItkImage( newImage2, itkImage2 ); itk::ResampleImageFilter< DoubleImageType, DoubleImageType >::Pointer resampleFilter = itk::ResampleImageFilter< DoubleImageType, DoubleImageType >::New(); resampleFilter->SetInput( itkImage1 ); resampleFilter->SetReferenceImage( itkImage2 ); resampleFilter->SetUseReferenceImage( true ); // use NN interp with binary images if(selectedNode->GetProperty("binary") ) resampleFilter->SetInterpolator( nn_interpolator ); else resampleFilter->SetInterpolator( bspl_interpolator ); resampleFilter->SetDefaultPixelValue( 0 ); try { resampleFilter->UpdateLargestPossibleRegion(); } catch( const itk::ExceptionObject &e) { MITK_WARN << "Updating resampling filter failed. "; MITK_WARN << "REASON: " << e.what(); } DoubleImageType::Pointer resampledImage = resampleFilter->GetOutput(); newImage1 = mitk::ImportItkImage( resampledImage )->Clone(); nameAddition = "_Resampled"; break; } default: std::cout << "Something went wrong..." << std::endl; this->BusyCursorOff(); return; } } catch (const itk::ExceptionObject& e ) { this->BusyCursorOff(); QMessageBox::warning(nullptr, "ITK Exception", e.what() ); QMessageBox::warning(nullptr, "Warning", tr("Problem when applying arithmetic operation to two images. Check dimensions of input images.")); return; } // disconnect pipeline; images will not be reused newImage1->DisconnectPipeline(); itkImage1 = nullptr; itkImage2 = nullptr; // adjust level/window to new image and compose new image name mitk::LevelWindow levelwindow; levelwindow.SetAuto( newImage1 ); auto levWinProp = mitk::LevelWindowProperty::New(); levWinProp->SetLevelWindow( levelwindow ); std::string name = selectedNode->GetName(); if (name.find(".nrrd") == name.size() -5 ) { name = name.substr(0,name.size() -5); } // create final result MITK data storage node auto result = mitk::DataNode::New(); result->SetProperty( "levelwindow", levWinProp ); result->SetProperty( "name", mitk::StringProperty::New( (name + nameAddition ).c_str() )); result->SetData( newImage1 ); this->GetDataStorage()->Add(result, selectedNode); if (m_Controls->cbHideOrig->isChecked() == true) { selectedNode->SetProperty("visible", mitk::BoolProperty::New(false)); selectedNode2->SetProperty("visible", mitk::BoolProperty::New(false)); } // show the newly created image mitk::RenderingManager::GetInstance()->RequestUpdateAll(); this->BusyCursorOff(); } void QmitkBasicImageProcessing::SelectInterpolator(int interpolator) { switch (interpolator) { case 0: { m_SelectedInterpolation = LINEAR; break; } case 1: { m_SelectedInterpolation = NEAREST; break; } } } diff --git a/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp b/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp index 926a63b541..cfdecd38f2 100644 --- a/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp +++ b/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp @@ -1,152 +1,152 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "InteractionEventRecorder.h" // Qt #include #include // us #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usModuleResource.h" #include #include #include #include "QmitkRenderWindow.h" US_INITIALIZE_MODULE const std::string InteractionEventRecorder::VIEW_ID = "org.mitk.views.interactioneventrecorder"; void InteractionEventRecorder::SetFocus() { m_Controls.textFileName->setFocus(); } void InteractionEventRecorder::StartRecording() { MITK_INFO << "Start Recording"; MITK_INFO << "Performing Reinit"; mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(this->GetDataStorage()); m_CurrentObserver->SetOutputFile(m_Controls.textFileName->text().toStdString()); m_CurrentObserver->StartRecording(); } void InteractionEventRecorder::StopRecording() { MITK_INFO << "Stop Recording"; m_CurrentObserver->StopRecording(); } void InteractionEventRecorder::Play() { std::ifstream xmlStream(m_Controls.textFileName->text().toStdString().c_str()); mitk::XML2EventParser parser(xmlStream); mitk::XML2EventParser::EventContainerType events = parser.GetInteractions(); MITK_INFO << "parsed events"; for (std::size_t i=0; i < events.size(); ++i) events.at(i)->GetSender()->GetDispatcher()->ProcessEvent(events.at(i)); MITK_INFO << "DONE"; } void InteractionEventRecorder::OpenFile() { QString fn = QFileDialog::getOpenFileName(nullptr, "Open File...", QString(), "All Files (*)"); if (!fn.isEmpty()) this->m_Controls.textFileName->setText(fn); } void InteractionEventRecorder::RotatePlanes() { mitk::Point3D center; center.Fill(0); mitk::Vector3D firstVec; mitk::Vector3D secondVec; firstVec[0] = 1.0; firstVec[1] = .5; firstVec[2] = .25; secondVec[0] = 1; secondVec[1] = .25; secondVec[2] = 1; // Rotate Planes to a predefined state which can later be used again in tests auto* axialRenderWindow = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("axial"); axialRenderWindow->GetSliceNavigationController()->ReorientSlices( center, firstVec,secondVec ); axialRenderWindow->GetRenderer()->RequestUpdate(); } void InteractionEventRecorder::RotateView() { // Rotate the view in 3D to a predefined state which can later be used again in tests // this simulates a prior VTK Interaction auto allRenderWindows = mitk::BaseRenderer::GetAll3DRenderWindows(); for (auto mapit = allRenderWindows.begin(); mapit != allRenderWindows.end(); ++mapit) { vtkRenderWindow* renderWindow = mapit->first; if (renderWindow == nullptr) continue; - mitk::Stepper* stepper = mapit->second->GetCameraRotationController()->GetSlice(); + mitk::Stepper* stepper = mapit->second->GetCameraRotationController()->GetStepper(); if (stepper == nullptr) return; unsigned int newPos = 17; stepper->SetPos(newPos); } } void InteractionEventRecorder::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); connect( m_Controls.btnStopRecording, SIGNAL(clicked()), this, SLOT(StopRecording()) ); connect( m_Controls.btnStartRecording, SIGNAL(clicked()), this, SLOT(StartRecording()) ); connect( m_Controls.btnPlay, SIGNAL(clicked()), this, SLOT(Play()) ); connect( m_Controls.btnOpenFile, SIGNAL(clicked()), this, SLOT(OpenFile()) ); connect( m_Controls.rotatePlanes, SIGNAL(clicked()), this, SLOT(RotatePlanes()) ); connect( m_Controls.rotate3D, SIGNAL(clicked()), this, SLOT(RotateView()) ); m_CurrentObserver = new mitk::EventRecorder(); // Register as listener via micro services us::ServiceProperties props; props["name"] = std::string("EventRecorder"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(m_CurrentObserver,props); /* delete m_CurrentObserverDEBUG; m_ServiceRegistrationDEBUG.Unregister(); */ } diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp index 2a44448ab0..6e608fbb8f 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp @@ -1,920 +1,919 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include #include // mitk #include // Qt #include #include #include #include #include "QmitkPlotWidget.h" #include "mitkNodePredicateFunction.h" #include "mitkScalarListLookupTableProperty.h" #include "mitkModelFitConstants.h" #include "mitkExtractTimeGrid.h" #include "mitkModelGenerator.h" #include "mitkModelFitException.h" #include "mitkModelFitParameterValueExtraction.h" #include "mitkTimeGridHelper.h" #include "mitkModelFitResultRelationRule.h" #include "mitkModelFitPlotDataHelper.h" #include "ModelFitInspectorView.h" const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.views.fit.inspector"; const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 10; const std::string DEFAULT_X_AXIS = "Time [s]"; ModelFitInspectorView::ModelFitInspectorView() : m_renderWindowPart(nullptr), m_internalUpdateFlag(false), m_currentFit(nullptr), m_currentModelParameterizer(nullptr), m_currentModelProviderService(nullptr), m_currentSelectedTimeStep(0), m_currentSelectedNode(nullptr) { m_currentSelectedPosition.Fill(0.0); m_modelfitList.clear(); } ModelFitInspectorView::~ModelFitInspectorView() { } void ModelFitInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_renderWindowPart != renderWindowPart) { m_renderWindowPart = renderWindowPart; } this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { m_renderWindowPart = nullptr; this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void ModelFitInspectorView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_SelectionServiceConnector = std::make_unique(); m_SelectionServiceConnector->AddPostSelectionListener(this->GetSite()->GetWorkbenchWindow()->GetSelectionService()); m_Controls.inputNodeSelector->SetDataStorage(GetDataStorage()); m_Controls.inputNodeSelector->SetEmptyInfo(QString("Please select input data to be viewed.")); m_Controls.inputNodeSelector->SetInvalidInfo(QString("No input data is selected")); m_Controls.inputNodeSelector->SetPopUpTitel(QString("Choose 3D+t input data that should be viewed!")); m_Controls.inputNodeSelector->SetSelectionIsOptional(false); m_Controls.inputNodeSelector->SetSelectOnlyVisibleNodes(true); auto predicate = mitk::NodePredicateFunction::New([](const mitk::DataNode *node) { bool isModelFitNode = node->GetData() && node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str()).IsNotNull(); return isModelFitNode || (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); }); m_Controls.inputNodeSelector->SetNodePredicate(predicate); connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.inputNodeSelector, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ModelFitInspectorView::OnInputChanged); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); connect(m_Controls.cmbFit, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFitSelectionChanged(int))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.btnScaleToData, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedYChecked(bool))); connect(m_Controls.btnScaleToData, SIGNAL(clicked()), this, SLOT(OnScaleToDataYClicked())); connect(m_Controls.sbFixMax, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.sbFixMin, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.btnScaleToData_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedXChecked(bool))); connect(m_Controls.btnScaleToData_x, SIGNAL(clicked()), this, SLOT(OnScaleToDataXClicked())); connect(m_Controls.sbFixMax_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.sbFixMin_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.btnFullPlot, SIGNAL(clicked(bool)), this, SLOT(OnFullPlotClicked(bool))); this->EnsureBookmarkPointSet(); m_Controls.inspectionPositionWidget->SetPositionBookmarkNode(m_PositionBookmarksNode.Lock()); connect(m_Controls.inspectionPositionWidget, SIGNAL(PositionBookmarksChanged()), this, SLOT(OnPositionBookmarksChanged())); // For some reason this needs to be called to set the plot widget's minimum width to an // acceptable level (since Qwt 6). // Otherwise it tries to keep both axes equal in length, resulting in a minimum width of // 400-500px which is way too much. m_Controls.widgetPlot->GetPlot()->updateAxes(); m_Controls.cmbFit->clear(); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::SetFocus() { } void ModelFitInspectorView::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_currentSelectedNode) { QmitkSingleNodeSelectionWidget::NodeList emptylist; this->m_Controls.inputNodeSelector->SetCurrentSelection(emptylist); } } void ModelFitInspectorView::OnScaleFixedYChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::yLeft, !checked); if (checked) { OnScaleToDataYClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleFixedXChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::xBottom, !checked); if (checked) { OnScaleToDataXClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleToDataYClicked() { auto minmax = this->m_PlotCurves.GetYMinMax(); auto min = minmax.first - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::abs(minmax.second) * 0.01; m_Controls.sbFixMin->setValue(min); m_Controls.sbFixMax->setValue(max); }; void ModelFitInspectorView::OnScaleToDataXClicked() { auto minmax = this->m_PlotCurves.GetXMinMax(); auto min = minmax.first - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::abs(minmax.second) * 0.01; m_Controls.sbFixMin_x->setValue(min); m_Controls.sbFixMax_x->setValue(max); }; void ModelFitInspectorView::OnFixedScalingYChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::yLeft, m_Controls.sbFixMin->value(), m_Controls.sbFixMax->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFixedScalingXChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::xBottom, m_Controls.sbFixMin_x->value(), m_Controls.sbFixMax_x->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFullPlotClicked(bool checked) { m_Controls.tabWidget->setVisible(!checked); }; int ModelFitInspectorView::ActualizeFitSelectionWidget() { mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = false; if (this->m_Controls.inputNodeSelector->GetSelectedNode().IsNotNull()) { isModelFitNode = this->m_Controls.inputNodeSelector->GetSelectedNode()->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); } mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::modelFit::NodeUIDSetType fitUIDs = mitk::modelFit::GetFitUIDsOfNode( this->m_currentSelectedNode, storage); this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); for (const auto & fitUID : fitUIDs) { mitk::modelFit::ModelFitInfo::ConstPointer info = mitk::modelFit::CreateFitInfoFromNode(fitUID, storage).GetPointer(); if (info.IsNotNull()) { this->m_modelfitList.insert(std::make_pair(info->uid, info)); std::ostringstream nameStrm; if (info->fitName.empty()) { nameStrm << info->uid; } else { nameStrm << info->fitName; } nameStrm << " (" << info->modelName << ")"; QVariant data(info->uid.c_str()); m_Controls.cmbFit->addItem(QString::fromStdString(nameStrm.str()), data); } else { MITK_ERROR << "Was not able to extract model fit information from storage. Node properties in storage may be invalid. Failed fit UID:" << fitUID; } } int cmbIndex = 0; if (m_modelfitList.empty()) { cmbIndex = -1; }; if (isModelFitNode) { //model was selected, thus select this one in combobox QVariant data(selectedFitUD.c_str()); cmbIndex = m_Controls.cmbFit->findData(data); if (cmbIndex == -1) { MITK_WARN << "Model fit Inspector in invalid state. Selected fit seems to be not avaible in plugin selection. Failed fit UID:" << selectedFitUD; } }; m_Controls.cmbFit->setCurrentIndex(cmbIndex); return cmbIndex; } void ModelFitInspectorView::OnInputChanged(const QList& nodes) { if (nodes.size() > 0) { if (nodes.front() != this->m_currentSelectedNode) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nodes.front(); mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); if (isModelFitNode) { this->m_currentSelectedNode = this->GetInputNode(this->m_currentSelectedNode); if (this->m_currentSelectedNode.IsNull()) { MITK_WARN << "Model fit Inspector in invalid state. Input image for selected fit cannot be found in data storage. Failed fit UID:" << selectedFitUD; } } auto cmbIndex = ActualizeFitSelectionWidget(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); if (cmbIndex == -1) { //only raw 4D data selected. Just update plots for current position m_currentFit = nullptr; m_currentFitTime.Modified(); OnSliceChanged(); m_Controls.plotDataWidget->SetXName(DEFAULT_X_AXIS); } else { //refresh fit selection (and implicitly update plots) OnFitSelectionChanged(cmbIndex); } } } else { if (this->m_currentSelectedNode.IsNotNull()) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nullptr; this->m_currentFit = nullptr; this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); OnFitSelectionChanged(0); RefreshPlotData(); m_Controls.plotDataWidget->SetPlotData(&(this->m_PlotCurves)); m_Controls.fitParametersWidget->setFits(QmitkFitParameterModel::FitVectorType()); RenderPlot(); } } } mitk::DataNode::ConstPointer ModelFitInspectorView::GetInputNode(mitk::DataNode::ConstPointer node) { if (node.IsNotNull()) { std::string selectedFitUD = ""; auto rule = mitk::ModelFitResultRelationRule::New(); auto predicate = rule->GetDestinationsDetector(node); mitk::DataStorage::SetOfObjects::ConstPointer parentNodeList = GetDataStorage()->GetSubset(predicate); if (parentNodeList->size() > 0) { return parentNodeList->front().GetPointer(); } } return mitk::DataNode::ConstPointer(); } void ModelFitInspectorView::ValidateAndSetCurrentPosition() { - mitk::Point3D currentSelectedPosition = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(nullptr); - unsigned int currentSelectedTimestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()-> - GetPos(); + const mitk::Point3D currentSelectedPosition = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(nullptr); + const unsigned int currentSelectedTimestep = m_renderWindowPart->GetTimeNavigationController()->GetStepper()->GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimestep || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimestep; m_currentPositionTime.Modified(); m_validSelectedPosition = false; auto inputImage = this->GetCurrentInputImage(); if (inputImage.IsNull()) { return; } mitk::BaseGeometry::ConstPointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep( m_currentSelectedTimeStep).GetPointer(); // check for invalid time step if (geometry.IsNull()) { geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); } if (geometry.IsNull()) { return; } m_validSelectedPosition = geometry->IsInside(m_currentSelectedPosition); } } mitk::Image::ConstPointer ModelFitInspectorView::GetCurrentInputImage() const { mitk::Image::ConstPointer result = nullptr; if (this->m_currentFit.IsNotNull()) { result = m_currentFit->inputImage; } else if (this->m_currentSelectedNode.IsNotNull()) { result = dynamic_cast(this->m_currentSelectedNode->GetData()); if (result.IsNotNull() && result->GetTimeSteps() <= 1) { //if the image is not dynamic, we can't use it. result = nullptr; } } return result; }; const mitk::ModelBase::TimeGridType ModelFitInspectorView::GetCurrentTimeGrid() const { if (m_currentModelProviderService && m_currentFit.IsNotNull()) { return m_currentModelProviderService->GetVariableGrid(m_currentFit); } else { //fall back if there is no model provider we assume to use the normal time grid. return ExtractTimeGrid(GetCurrentInputImage()); } }; void ModelFitInspectorView::OnSliceChanged() { ValidateAndSetCurrentPosition(); m_Controls.widgetPlot->setEnabled(m_validSelectedPosition); if (m_currentSelectedNode.IsNotNull()) { m_Controls.inspectionPositionWidget->SetCurrentPosition(m_currentSelectedPosition); if (RefreshPlotData()) { RenderPlot(); m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } } void ModelFitInspectorView::OnPositionBookmarksChanged() { if (RefreshPlotData()) { RenderPlot(); m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } void ModelFitInspectorView::OnFitSelectionChanged(int index) { if (!m_internalUpdateFlag) { MITK_DEBUG << "selected fit index: " << index; std::string uid = ""; if (m_Controls.cmbFit->count() > index) { uid = m_Controls.cmbFit->itemData(index).toString().toStdString(); } mitk::modelFit::ModelFitInfo::ConstPointer newFit = nullptr; ModelFitInfoListType::iterator finding = m_modelfitList.find(uid); if (finding != m_modelfitList.end()) { newFit = finding->second; } if (m_currentFit != newFit) { m_currentModelParameterizer = nullptr; m_currentModelProviderService = nullptr; if (newFit.IsNotNull()) { m_currentModelParameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*newFit); m_currentModelProviderService = mitk::ModelGenerator::GetProviderService(newFit->functionClassID); } m_currentFit = newFit; m_currentFitTime.Modified(); auto name = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { name += " [" + m_currentFit->xAxisUnit + "]"; } m_Controls.plotDataWidget->SetXName(name); OnSliceChanged(); } } } mitk::PlotDataCurveCollection::Pointer ModelFitInspectorView::RefreshPlotDataCurveCollection(const mitk::Point3D& position, const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer) { mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New(); //sample curve if (input) { result->InsertElement(mitk::MODEL_FIT_PLOT_SAMPLE_NAME(), GenerateImageSamplePlotData(position, input, timeGrid)); } //model signal curve if (fitInfo) { // Interpolate time grid (x values) so the curve looks smooth const mitk::ModelBase::TimeGridType interpolatedTimeGrid = mitk::GenerateSupersampledTimeGrid(timeGrid, INTERPOLATION_STEPS); auto hires_curve = mitk::GenerateModelSignalPlotData(position, fitInfo, interpolatedTimeGrid, parameterizer); result->InsertElement(mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME(), hires_curve); auto curve = mitk::GenerateModelSignalPlotData(position, fitInfo, timeGrid, parameterizer); result->InsertElement(mitk::MODEL_FIT_PLOT_SIGNAL_NAME(), curve); } return result; }; bool ModelFitInspectorView::RefreshPlotData() { bool changed = false; if (m_currentSelectedNode.IsNull()) { this->m_PlotCurves = mitk::ModelFitPlotData(); changed = m_selectedNodeTime > m_lastRefreshTime; m_lastRefreshTime.Modified(); } else { assert(GetRenderWindowPart() != NULL); const mitk::Image* input = GetCurrentInputImage(); const mitk::ModelBase::TimeGridType timeGrid = GetCurrentTimeGrid(); if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime) { if (m_validSelectedPosition) { m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition,input,m_currentFit, timeGrid, m_currentModelParameterizer); } else { m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New(); } changed = true; } auto bookmarks = m_PositionBookmarks.Lock(); if (bookmarks.IsNotNull()) { if (m_currentFitTime > m_lastRefreshTime || bookmarks->GetMTime() > m_lastRefreshTime) { m_PlotCurves.positionalPlots.clear(); auto endIter = bookmarks->End(); for (auto iter = bookmarks->Begin(); iter != endIter; iter++) { auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer); m_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection)); } changed = true; } } else { m_PlotCurves.positionalPlots.clear(); } // input data curve if (m_currentFitTime > m_lastRefreshTime) { m_PlotCurves.staticPlots->clear(); if (m_currentFit.IsNotNull()) { m_PlotCurves.staticPlots = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid); } changed = true; } m_lastRefreshTime.Modified(); } return changed; } void ModelFitInspectorView::RenderFitInfo() { assert(m_renderWindowPart != nullptr); // configure fit information if (m_currentFit.IsNull()) { m_Controls.lFitType->setText(""); m_Controls.lFitUID->setText(""); m_Controls.lModelName->setText(""); m_Controls.lModelType->setText(""); } else { m_Controls.lFitType->setText(QString::fromStdString(m_currentFit->fitType)); m_Controls.lFitUID->setText(QString::fromStdString(m_currentFit->uid)); m_Controls.lModelName->setText(QString::fromStdString(m_currentFit->modelName)); m_Controls.lModelType->setText(QString::fromStdString(m_currentFit->modelType)); } // print results std::stringstream infoOutput; m_Controls.fitParametersWidget->setVisible(false); m_Controls.groupSettings->setVisible(false); if (m_currentFit.IsNull()) { infoOutput << "No fit selected. Only raw image data is plotted."; } else if (!m_validSelectedPosition) { infoOutput << "Current position is outside of the input image of the selected fit.\nInspector is deactivated."; } else { m_Controls.fitParametersWidget->setVisible(true); m_Controls.fitParametersWidget->setFits({ m_currentFit }); m_Controls.fitParametersWidget->setPositionBookmarks(m_PositionBookmarks.Lock()); m_Controls.fitParametersWidget->setCurrentPosition(m_currentSelectedPosition); } // configure data table m_Controls.tableInputData->clearContents(); if (m_currentFit.IsNull()) { infoOutput << "No fit selected. Only raw image data is plotted."; } else { m_Controls.groupSettings->setVisible(true); m_Controls.tableInputData->setRowCount(m_PlotCurves.staticPlots->size()); unsigned int rowIndex = 0; for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); pos != m_PlotCurves.staticPlots->end(); ++pos, ++rowIndex) { QColor dataColor; if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); } else { //Use HSV schema of QColor to calculate a different color depending on the //number of already existing free iso lines. dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 255); } QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first)); m_Controls.tableInputData->setItem(rowIndex, 0, newItem); newItem = new QTableWidgetItem(); newItem->setBackgroundColor(dataColor); m_Controls.tableInputData->setItem(rowIndex, 1, newItem); } } m_Controls.lInfo->setText(QString::fromStdString(infoOutput.str())); } void ModelFitInspectorView::RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString) { auto sampleCurve = mitk::ModelFitPlotData::GetSamplePlot(curveCollection); if (sampleCurve) { std::string name = mitk::MODEL_FIT_PLOT_SAMPLE_NAME() + posString; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, sampleCurve->GetValues()); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Diamond, sampleColor, sampleColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } //draw model curve auto signalCurve = mitk::ModelFitPlotData::GetInterpolatedSignalPlot(curveCollection); if (signalCurve) { std::string name = mitk::MODEL_FIT_PLOT_SIGNAL_NAME() + posString; QPen pen; pen.setColor(signalColor); pen.setWidth(2); unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, signalCurve->GetValues()); m_Controls.widgetPlot->SetCurvePen(curveId, pen); // Manually set the legend attribute to use the symbol as the legend icon and alter its // size. Otherwise it would revert to default which is drawing a square which is the color // of the curve's pen, so in this case none which defaults to black. // Unfortunately, QmitkPlotWidget offers no way to set the legend attribute and icon size so // this looks a bit hacky. QwtPlotCurve* fitCurve = dynamic_cast(m_Controls.widgetPlot->GetPlot()-> itemList(QwtPlotItem::Rtti_PlotCurve).back()); fitCurve->setLegendAttribute(QwtPlotCurve::LegendShowLine); } } void ModelFitInspectorView::RenderPlot() { m_Controls.widgetPlot->Clear(); std::string xAxis = DEFAULT_X_AXIS; std::string yAxis = "Intensity"; std::string plotTitle = "Raw data plot: no data"; if (m_currentSelectedNode.IsNotNull()) { plotTitle = "Raw data plot: " + m_currentSelectedNode->GetName(); } if (m_currentFit.IsNotNull()) { plotTitle = m_currentFit->modelName.c_str(); xAxis = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { xAxis += " [" + m_currentFit->xAxisUnit + "]"; } yAxis = m_currentFit->yAxisName; if (!m_currentFit->yAxisUnit.empty()) { yAxis += " [" + m_currentFit->yAxisUnit + "]"; } } m_Controls.widgetPlot->SetAxisTitle(QwtPlot::xBottom, xAxis.c_str()); m_Controls.widgetPlot->SetAxisTitle(QwtPlot::yLeft, yAxis.c_str()); m_Controls.widgetPlot->SetPlotTitle(plotTitle.c_str()); // Draw static curves unsigned int colorIndex = 0; for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); pos != m_PlotCurves.staticPlots->end(); ++pos) { QColor dataColor; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(pos->first.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, pos->second->GetValues()); if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); QPen pen; pen.setColor(dataColor); pen.setStyle(Qt::SolidLine); m_Controls.widgetPlot->SetCurvePen(curveId, pen); } else { //Use HSV schema of QColor to calculate a different color depending on the //number of already existing curves. dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); } // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Triangle, dataColor, dataColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } // Draw positional curves for (const auto& posIter : this->m_PlotCurves.positionalPlots) { QColor dataColor; dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); this->RenderPlotCurve(posIter.second.second, dataColor, dataColor, " @ "+mitk::ModelFitPlotData::GetPositionalCollectionName(posIter)); } // Draw current pos curve this->RenderPlotCurve(m_PlotCurves.currentPositionPlots, QColor(Qt::red), QColor(Qt::black), ""); QwtLegend* legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); m_Controls.widgetPlot->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.widgetPlot->Replot(); } void ModelFitInspectorView::EnsureBookmarkPointSet() { if (m_PositionBookmarks.IsExpired() || m_PositionBookmarksNode.IsExpired()) { const char* nodeName = "org.mitk.gui.qt.fit.inspector.positions"; mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName); if (!node) { node = mitk::DataNode::New(); node->SetName(nodeName); node->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(node); } m_PositionBookmarksNode = node; mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { pointSet = mitk::PointSet::New(); node->SetData(pointSet); } m_PositionBookmarks = pointSet; } } diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp index e3d982e82d..7b4ecf686f 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp @@ -1,457 +1,457 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageNavigatorView.h" #include #include #include #include #include #include const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator"; QmitkImageNavigatorView::QmitkImageNavigatorView() : m_AxialStepperAdapter(nullptr) , m_SagittalStepperAdapter(nullptr) , m_CoronalStepperAdapter(nullptr) , m_TimeStepperAdapter(nullptr) , m_Parent(nullptr) , m_IRenderWindowPart(nullptr) { } QmitkImageNavigatorView::~QmitkImageNavigatorView() { } void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent) { // create GUI widgets m_Parent = parent; m_Controls.setupUi(parent); connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); m_Parent->setEnabled(false); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); this->RenderWindowPartActivated(renderPart); } void QmitkImageNavigatorView::SetFocus () { m_Controls.m_XWorldCoordinateSpinBox->setFocus(); } void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (this->m_IRenderWindowPart != renderWindowPart) { this->m_IRenderWindowPart = renderWindowPart; this->m_Parent->setEnabled(true); QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { if (m_AxialStepperAdapter) m_AxialStepperAdapter->deleteLater(); m_AxialStepperAdapter = new QmitkStepperAdapter(m_Controls.m_AxialSliceNavigationWidget, - renderWindow->GetSliceNavigationController()->GetSlice()); + renderWindow->GetSliceNavigationController()->GetStepper()); m_Controls.m_AxialSliceNavigationWidget->setEnabled(true); m_Controls.m_AxialLabel->setEnabled(true); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true); connect(m_AxialStepperAdapter, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { m_Controls.m_AxialSliceNavigationWidget->setEnabled(false); m_Controls.m_AxialLabel->setEnabled(false); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { if (m_SagittalStepperAdapter) m_SagittalStepperAdapter->deleteLater(); m_SagittalStepperAdapter = new QmitkStepperAdapter(m_Controls.m_SagittalSliceNavigationWidget, - renderWindow->GetSliceNavigationController()->GetSlice()); + renderWindow->GetSliceNavigationController()->GetStepper()); m_Controls.m_SagittalSliceNavigationWidget->setEnabled(true); m_Controls.m_SagittalLabel->setEnabled(true); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true); connect(m_SagittalStepperAdapter, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { m_Controls.m_SagittalSliceNavigationWidget->setEnabled(false); m_Controls.m_SagittalLabel->setEnabled(false); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { if (m_CoronalStepperAdapter) m_CoronalStepperAdapter->deleteLater(); m_CoronalStepperAdapter = new QmitkStepperAdapter(m_Controls.m_CoronalSliceNavigationWidget, - renderWindow->GetSliceNavigationController()->GetSlice()); + renderWindow->GetSliceNavigationController()->GetStepper()); m_Controls.m_CoronalSliceNavigationWidget->setEnabled(true); m_Controls.m_CoronalLabel->setEnabled(true); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true); connect(m_CoronalStepperAdapter, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { m_Controls.m_CoronalSliceNavigationWidget->setEnabled(false); m_Controls.m_CoronalLabel->setEnabled(false); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false); } mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController(); if (timeController) { if (m_TimeStepperAdapter) m_TimeStepperAdapter->deleteLater(); m_TimeStepperAdapter = new QmitkStepperAdapter(m_Controls.m_TimeSliceNavigationWidget, - timeController->GetTime()); + timeController->GetStepper()); m_Controls.m_TimeSliceNavigationWidget->setEnabled(true); m_Controls.m_TimeLabel->setEnabled(true); } else { m_Controls.m_TimeSliceNavigationWidget->setEnabled(false); m_Controls.m_TimeLabel->setEnabled(false); } this->OnRefetch(); } } void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_IRenderWindowPart = nullptr; m_Parent->setEnabled(false); } int QmitkImageNavigatorView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return 200; } else { return preferredResult; } } int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmitkImageNavigatorView::SetBorderColors() { if (m_IRenderWindowPart) { QString decoColor; QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } } } QString QmitkImageNavigatorView::GetDecorationColorOfGeometry(QmitkRenderWindow* renderWindow) { QColor color; float rgb[3] = {1.0f, 1.0f, 1.0f}; float rgbMax = 255.0f; mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow())->GetCurrentWorldPlaneGeometryNode()->GetColor(rgb); color.setRed(static_cast(rgb[0]*rgbMax + 0.5)); color.setGreen(static_cast(rgb[1]*rgbMax + 0.5)); color.setBlue(static_cast(rgb[2]*rgbMax + 0.5)); QString colorAsString = QString(color.name()); return colorAsString; } void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString) { if (axis == 0) { this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 1) { this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 2) { this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString); } } void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString) { assert(spinBox); spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";"); } void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged() { if (m_IRenderWindowPart) { mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value(); positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value(); positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value(); m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates); } } } void QmitkImageNavigatorView::OnRefetch() { if (nullptr == m_IRenderWindowPart) { return; } mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (timeGeometry.IsNull()) { return; } SetVisibilityOfTimeSlider(timeGeometry->CountTimeSteps()); - mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); + mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetStepper()->GetPos(); mitk::BaseGeometry::Pointer geometry = timeGeometry->GetGeometryForTimeStep(timeStep); if (geometry.IsNotNull()) { mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::Point3D cornerPoint1InIndexCoordinates; cornerPoint1InIndexCoordinates[0] = bounds[0]; cornerPoint1InIndexCoordinates[1] = bounds[2]; cornerPoint1InIndexCoordinates[2] = bounds[4]; mitk::Point3D cornerPoint2InIndexCoordinates; cornerPoint2InIndexCoordinates[0] = bounds[1]; cornerPoint2InIndexCoordinates[1] = bounds[3]; cornerPoint2InIndexCoordinates[2] = bounds[5]; if (!geometry->GetImageGeometry()) { cornerPoint1InIndexCoordinates[0] += 0.5; cornerPoint1InIndexCoordinates[1] += 0.5; cornerPoint1InIndexCoordinates[2] += 0.5; cornerPoint2InIndexCoordinates[0] -= 0.5; cornerPoint2InIndexCoordinates[1] -= 0.5; cornerPoint2InIndexCoordinates[2] -= 0.5; } mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); mitk::Point3D cornerPoint1InWorldCoordinates; mitk::Point3D cornerPoint2InWorldCoordinates; geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_XWorldCoordinateSpinBox->setMinimum( std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMinimum( std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMinimum( std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setMaximum( std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMaximum( std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMaximum( std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); /// Calculating 'inverse direction' property. mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); for (int worldAxis = 0; worldAxis < 3; ++worldAxis) { QmitkRenderWindow *renderWindow = worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") : worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") : m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { const mitk::BaseGeometry* rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry(); /// Because of some problems with the current way of event signalling, /// 'Modified' events are sent out from the stepper while the renderer /// does not have a geometry yet. Therefore, we do a nullptr check here. /// See bug T22122. This check can be resolved after T22122 got fixed. if (rendererGeometry) { int dominantAxis = itk::Function::Max3(inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0; bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0; /// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis /// of the reference geometry is flipped compared to the 'world direction' or not. /// /// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is /// flipped compared to the 'world direction' or not. This is the same as the indexing /// direction in the slice navigation controller and matches the 'top' property when /// initialising the renderer planes. (If 'top' was true then the direction is /// inverted.) /// /// The world direction can be +1 ('up') that means right, anterior or superior, or /// it can be -1 ('down') that means left, posterior or inferior, respectively. /// /// If these two do not match, we have to invert the index between the slice navigation /// controller and the slider navigator widget, so that the user can see and control /// the index according to the reference geometry, rather than the slice navigation /// controller. The index in the slice navigation controller depends on in which way /// the reference geometry has been resliced for the renderer, and it does not necessarily /// match neither the world direction, nor the direction of the corresponding axis of /// the reference geometry. Hence, it is a merely internal information that should not /// be exposed to the GUI. /// /// So that one can navigate in the same world direction by dragging the slider /// right, regardless of the direction of the corresponding axis of the reference /// geometry, we invert the direction of the controls if the reference geometry axis /// is inverted but the direction is not ('inversDirection' is false) or the other /// way around. bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted; QmitkSliceNavigationWidget* sliceNavigationWidget = worldAxis == 0 ? m_Controls.m_SagittalSliceNavigationWidget : worldAxis == 1 ? m_Controls.m_CoronalSliceNavigationWidget : m_Controls.m_AxialSliceNavigationWidget; sliceNavigationWidget->SetInverseDirection(inverseDirection); // This should be a preference (see T22254) // bool invertedControls = referenceGeometryAxisInverted != inverseDirection; // navigatorWidget->SetInvertedControls(invertedControls); } } } } this->SetBorderColors(); } void QmitkImageNavigatorView::SetVisibilityOfTimeSlider(std::size_t timeSteps) { m_Controls.m_TimeSliceNavigationWidget->setVisible(timeSteps > 1); m_Controls.m_TimeLabel->setVisible(timeSteps > 1); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkOrbitAnimationItem.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkOrbitAnimationItem.cpp index d920eaec5d..654f2696b1 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkOrbitAnimationItem.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkOrbitAnimationItem.cpp @@ -1,70 +1,70 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkOrbitAnimationItem.h" #include QmitkOrbitAnimationItem::QmitkOrbitAnimationItem(int orbit, bool reverse, double duration, double delay, bool startWithPrevious) : QmitkAnimationItem("Orbit", duration, delay, startWithPrevious) { this->SetOrbit(orbit); this->SetReverse(reverse); } QmitkOrbitAnimationItem::~QmitkOrbitAnimationItem() { } int QmitkOrbitAnimationItem::GetOrbit() const { return this->data(OrbitRole).toInt(); } void QmitkOrbitAnimationItem::SetOrbit(int angle) { this->setData(angle, OrbitRole); } bool QmitkOrbitAnimationItem::GetReverse() const { return this->data(ReverseRole).toBool(); } void QmitkOrbitAnimationItem::SetReverse(bool reverse) { this->setData(reverse, ReverseRole); } void QmitkOrbitAnimationItem::Animate(double s) { vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"); if (renderWindow == nullptr) return; - mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetCameraRotationController()->GetSlice(); + mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetCameraRotationController()->GetStepper(); if (stepper == nullptr) return; int newPos = this->GetReverse() ? 180 - this->GetOrbit() * s : 180 + this->GetOrbit() * s; while (newPos < 0) newPos += 360; while (newPos > 360) newPos -= 360; stepper->SetPos(static_cast(newPos)); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationItem.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationItem.cpp index c7135fcea9..23fd4aabb9 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationItem.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationItem.cpp @@ -1,87 +1,87 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSliceAnimationItem.h" #include QmitkSliceAnimationItem::QmitkSliceAnimationItem(int renderWindow, int from, int to, bool reverse, double duration, double delay, bool startWithPrevious) : QmitkAnimationItem("Slice", duration, delay, startWithPrevious) { this->SetRenderWindow(renderWindow); this->SetFrom(from); this->SetTo(to); this->SetReverse(reverse); } QmitkSliceAnimationItem::~QmitkSliceAnimationItem() { } int QmitkSliceAnimationItem::GetRenderWindow() const { return this->data(RenderWindowRole).toInt(); } void QmitkSliceAnimationItem::SetRenderWindow(int renderWindow) { this->setData(renderWindow, RenderWindowRole); } int QmitkSliceAnimationItem::GetFrom() const { return this->data(FromRole).toInt(); } void QmitkSliceAnimationItem::SetFrom(int from) { this->setData(from, FromRole); } int QmitkSliceAnimationItem::GetTo() const { return this->data(ToRole).toInt(); } void QmitkSliceAnimationItem::SetTo(int to) { this->setData(to, ToRole); } bool QmitkSliceAnimationItem::GetReverse() const { return this->data(ReverseRole).toBool(); } void QmitkSliceAnimationItem::SetReverse(bool reverse) { this->setData(reverse, ReverseRole); } void QmitkSliceAnimationItem::Animate(double s) { const QString renderWindowName = QString("stdmulti.widget%1").arg(this->GetRenderWindow()); vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName(renderWindowName.toStdString()); if (renderWindow == nullptr) return; - mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController()->GetSlice(); + mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController()->GetStepper(); if (stepper == nullptr) return; int newPos = this->GetReverse() ? this->GetTo() - static_cast((this->GetTo() - this->GetFrom()) * s) : this->GetFrom() + static_cast((this->GetTo() - this->GetFrom()) * s); stepper->SetPos(static_cast(newPos)); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationWidget.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationWidget.cpp index bca0c6de77..49b5dcfc94 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationWidget.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkSliceAnimationWidget.cpp @@ -1,127 +1,127 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSliceAnimationItem.h" #include "QmitkSliceAnimationWidget.h" #include #include namespace { int GetNumberOfSlices(int renderWindow) { const QString renderWindowName = QString("stdmulti.widget%1").arg(renderWindow); vtkRenderWindow* theRenderWindow = mitk::BaseRenderer::GetRenderWindowByName(renderWindowName.toStdString()); if (theRenderWindow != nullptr) { - mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(theRenderWindow)->GetSliceNavigationController()->GetSlice(); + mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(theRenderWindow)->GetSliceNavigationController()->GetStepper(); if (stepper != nullptr) return std::max(1, static_cast(stepper->GetSteps())); } return 1; } } QmitkSliceAnimationWidget::QmitkSliceAnimationWidget(QWidget* parent) : QmitkAnimationWidget(parent), m_Ui(new Ui::QmitkSliceAnimationWidget), m_AnimationItem(nullptr) { m_Ui->setupUi(this); connect(m_Ui->windowComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnRenderWindowChanged(int))); connect(m_Ui->sliceRangeWidget, SIGNAL(minimumValueChanged(double)), this, SLOT(OnFromChanged(double))); connect(m_Ui->sliceRangeWidget, SIGNAL(maximumValueChanged(double)), this, SLOT(OnToChanged(double))); connect(m_Ui->reverseCheckBox, SIGNAL(clicked(bool)), this, SLOT(OnReverseChanged(bool))); } QmitkSliceAnimationWidget::~QmitkSliceAnimationWidget() { } void QmitkSliceAnimationWidget::SetAnimationItem(QmitkAnimationItem* sliceAnimationItem) { m_AnimationItem = dynamic_cast(sliceAnimationItem); if (nullptr == m_AnimationItem) return; m_Ui->windowComboBox->setCurrentIndex(m_AnimationItem->GetRenderWindow()); const int maximum = GetNumberOfSlices(m_AnimationItem->GetRenderWindow()) - 1; const int from = std::min(m_AnimationItem->GetFrom(), maximum); int to = std::max(from, std::min(m_AnimationItem->GetTo(), maximum)); if (0 == to) to = maximum; m_AnimationItem->SetFrom(from); m_AnimationItem->SetTo(to); m_Ui->sliceRangeWidget->setMaximum(maximum); m_Ui->sliceRangeWidget->setValues(from, to); m_Ui->reverseCheckBox->setChecked(m_AnimationItem->GetReverse()); } void QmitkSliceAnimationWidget::OnRenderWindowChanged(int renderWindow) { if (nullptr == m_AnimationItem) return; const int lastSlice = static_cast(GetNumberOfSlices(renderWindow) - 1); if (lastSlice < m_AnimationItem->GetFrom()) m_AnimationItem->SetFrom(lastSlice); if (lastSlice < m_AnimationItem->GetTo()) m_AnimationItem->SetTo(lastSlice); m_Ui->sliceRangeWidget->setMaximum(lastSlice); m_Ui->sliceRangeWidget->setValues(m_AnimationItem->GetFrom(), m_AnimationItem->GetTo()); if (m_AnimationItem->GetRenderWindow() != renderWindow) m_AnimationItem->SetRenderWindow(renderWindow); } void QmitkSliceAnimationWidget::OnFromChanged(double from) { if (nullptr == m_AnimationItem) return; int intFrom = static_cast(from); if (m_AnimationItem->GetFrom() != intFrom) m_AnimationItem->SetFrom(intFrom); } void QmitkSliceAnimationWidget::OnToChanged(double to) { if (nullptr == m_AnimationItem) return; int intTo = static_cast(to); if (m_AnimationItem->GetTo() != intTo) m_AnimationItem->SetTo(intTo); } void QmitkSliceAnimationWidget::OnReverseChanged(bool reverse) { if (nullptr == m_AnimationItem) return; if (m_AnimationItem->GetReverse() != reverse) m_AnimationItem->SetReverse(reverse); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationItem.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationItem.cpp index c2a4e7d339..827c994c39 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationItem.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationItem.cpp @@ -1,70 +1,70 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkTimeSliceAnimationItem.h" #include QmitkTimeSliceAnimationItem::QmitkTimeSliceAnimationItem(int from, int to, bool reverse, double duration, double delay, bool startWithPrevious) : QmitkAnimationItem("Time", duration, delay, startWithPrevious) { this->SetFrom(from); this->SetTo(to); this->SetReverse(reverse); } QmitkTimeSliceAnimationItem::~QmitkTimeSliceAnimationItem() { } int QmitkTimeSliceAnimationItem::GetFrom() const { return this->data(FromRole).toInt(); } void QmitkTimeSliceAnimationItem::SetFrom(int from) { this->setData(from, FromRole); } int QmitkTimeSliceAnimationItem::GetTo() const { return this->data(ToRole).toInt(); } void QmitkTimeSliceAnimationItem::SetTo(int to) { this->setData(to, ToRole); } bool QmitkTimeSliceAnimationItem::GetReverse() const { return this->data(ReverseRole).toBool(); } void QmitkTimeSliceAnimationItem::SetReverse(bool reverse) { this->setData(reverse, ReverseRole); } void QmitkTimeSliceAnimationItem::Animate(double s) { - mitk::Stepper* stepper = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime(); + mitk::Stepper* stepper = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetStepper(); if (stepper == nullptr) return; int newPos = this->GetReverse() ? this->GetTo() - static_cast((this->GetTo() - this->GetFrom()) * s) : this->GetFrom() + static_cast((this->GetTo() - this->GetFrom()) * s); stepper->SetPos(static_cast(newPos)); } diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationWidget.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationWidget.cpp index a5f2e52f7f..f7d158f949 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationWidget.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkTimeSliceAnimationWidget.cpp @@ -1,99 +1,99 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkTimeSliceAnimationItem.h" #include "QmitkTimeSliceAnimationWidget.h" #include #include #include #include namespace { int GetNumberOfSlices() { - mitk::Stepper* stepper = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime(); + mitk::Stepper* stepper = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetStepper(); if (stepper != nullptr) return std::max(1, static_cast(stepper->GetSteps())); return 1; } } QmitkTimeSliceAnimationWidget::QmitkTimeSliceAnimationWidget(QWidget* parent) : QmitkAnimationWidget(parent), m_Ui(new Ui::QmitkTimeSliceAnimationWidget) { m_Ui->setupUi(this); connect(m_Ui->sliceRangeWidget, SIGNAL(minimumValueChanged(double)), this, SLOT(OnFromChanged(double))); connect(m_Ui->sliceRangeWidget, SIGNAL(maximumValueChanged(double)), this, SLOT(OnToChanged(double))); connect(m_Ui->reverseCheckBox, SIGNAL(clicked(bool)), this, SLOT(OnReverseChanged(bool))); } QmitkTimeSliceAnimationWidget::~QmitkTimeSliceAnimationWidget() { } void QmitkTimeSliceAnimationWidget::SetAnimationItem(QmitkAnimationItem* sliceAnimationItem) { m_AnimationItem = dynamic_cast(sliceAnimationItem); if (nullptr == m_AnimationItem) return; const int maximum = GetNumberOfSlices() - 1; const int from = std::min(m_AnimationItem->GetFrom(), maximum); int to = std::max(from, std::min(m_AnimationItem->GetTo(), maximum)); if (0 == to) to = maximum; m_AnimationItem->SetFrom(from); m_AnimationItem->SetTo(to); m_Ui->sliceRangeWidget->setMaximum(maximum); m_Ui->sliceRangeWidget->setValues(from, to); m_Ui->reverseCheckBox->setChecked(m_AnimationItem->GetReverse()); } void QmitkTimeSliceAnimationWidget::OnFromChanged(double from) { if (nullptr == m_AnimationItem) return; int intFrom = static_cast(from); if (m_AnimationItem->GetFrom() != intFrom) m_AnimationItem->SetFrom(intFrom); } void QmitkTimeSliceAnimationWidget::OnToChanged(double to) { if (nullptr == m_AnimationItem) return; int intTo = static_cast(to); if (m_AnimationItem->GetTo() != intTo) m_AnimationItem->SetTo(intTo); } void QmitkTimeSliceAnimationWidget::OnReverseChanged(bool reverse) { if (nullptr == m_AnimationItem) return; if (m_AnimationItem->GetReverse() != reverse) m_AnimationItem->SetReverse(reverse); } diff --git a/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp index 313a67c79c..39c1b8a713 100644 --- a/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp +++ b/Plugins/org.mitk.gui.qt.preprocessing.resampling/src/internal/QmitkPreprocessingResamplingView.cpp @@ -1,456 +1,456 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkPreprocessingResamplingView.h" // QT includes (GUI) #include #include #include #include #include #include #include // Berry includes (selection service) #include #include // MITK includes (GUI) #include "QmitkDataNodeSelectionProvider.h" #include "mitkDataNodeObject.h" // MITK includes (general) #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateAnd.h" #include "mitkImageTimeSelector.h" #include "mitkVectorImageMapper2D.h" #include "mitkProperties.h" #include "mitkLevelWindowProperty.h" // Includes for image casting between ITK and MITK #include "mitkImageCast.h" #include "mitkITKImageImport.h" // ITK includes (general) #include #include // Resampling #include #include #include #include #include #include #include // STD #include // Convenient Definitions typedef itk::Image ImageType; typedef itk::Image SegmentationImageType; typedef itk::Image DoubleImageType; typedef itk::Image, 3> VectorImageType; typedef itk::ResampleImageFilter< ImageType, ImageType > ResampleImageFilterType; typedef itk::ResampleImageFilter< ImageType, ImageType > ResampleImageFilterType2; typedef itk::CastImageFilter< ImageType, DoubleImageType > ImagePTypeToFloatPTypeCasterType; typedef itk::LinearInterpolateImageFunction< ImageType, double > LinearInterpolatorType; typedef itk::NearestNeighborInterpolateImageFunction< ImageType, double > NearestInterpolatorType; typedef itk::BSplineInterpolateImageFunction BSplineInterpolatorType; QmitkPreprocessingResampling::QmitkPreprocessingResampling() : QmitkAbstractView(), m_Controls(nullptr), m_SelectedImageNode(nullptr), m_TimeStepperAdapter(nullptr) { } QmitkPreprocessingResampling::~QmitkPreprocessingResampling() { } void QmitkPreprocessingResampling::CreateQtPartControl(QWidget *parent) { if (m_Controls == nullptr) { m_Controls = new Ui::QmitkPreprocessingResamplingViewControls; m_Controls->setupUi(parent); this->CreateConnections(); mitk::NodePredicateDimension::Pointer dimensionPredicate = mitk::NodePredicateDimension::New(3); mitk::NodePredicateDataType::Pointer imagePredicate = mitk::NodePredicateDataType::New("Image"); } m_SelectedImageNode = mitk::DataStorageSelection::New(this->GetDataStorage(), false); // Setup Controls this->m_Controls->cbParam4->clear(); this->m_Controls->cbParam4->insertItem(LINEAR, "Linear"); this->m_Controls->cbParam4->insertItem(NEAREST, "Nearest neighbor"); this->m_Controls->cbParam4->insertItem(SPLINE, "B-Spline"); } void QmitkPreprocessingResampling::CreateConnections() { if ( m_Controls ) { connect((QObject*)(m_Controls->btnDoIt), SIGNAL(clicked()), (QObject*) this, SLOT(StartButtonClicked())); connect((QObject*)(m_Controls->buttonExecuteOnMultipleImages), SIGNAL(clicked()), (QObject*) this, SLOT(StartMultipleImagesButtonClicked())); connect( (QObject*)(m_Controls->cbParam4), SIGNAL( activated(int) ), this, SLOT( SelectInterpolator(int) ) ); } } void QmitkPreprocessingResampling::InternalGetTimeNavigationController() { auto renwin_part = GetRenderWindowPart(); if( renwin_part != nullptr ) { auto tnc = renwin_part->GetTimeNavigationController(); if( tnc != nullptr ) { - m_TimeStepperAdapter = new QmitkStepperAdapter(m_Controls->timeSliceNavigationWidget, tnc->GetTime()); + m_TimeStepperAdapter = new QmitkStepperAdapter(m_Controls->timeSliceNavigationWidget, tnc->GetStepper()); } } } void QmitkPreprocessingResampling::SetFocus() { } //datamanager selection changed void QmitkPreprocessingResampling::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) { ResetOneImageOpPanel(); //any nodes there? if (!nodes.empty()) { // reset GUI m_Controls->timeSliceNavigationWidget->setEnabled(false); m_Controls->leImage1->setText(tr("Select an Image in Data Manager")); m_SelectedNodes.clear(); for (mitk::DataNode* _DataNode : nodes) { m_SelectedImageNode->RemoveAllNodes(); *m_SelectedImageNode = _DataNode; mitk::Image::Pointer tempImage = dynamic_cast(m_SelectedImageNode->GetNode()->GetData()); //no image if (tempImage.IsNull() || (tempImage->IsInitialized() == false)) { if (m_SelectedNodes.size() < 1) { m_Controls->leImage1->setText(tr("Not an image.")); } continue; } //2D image if (tempImage->GetDimension() < 3) { if (m_SelectedNodes.size() < 1) { m_Controls->leImage1->setText(tr("2D images are not supported.")); } continue; } if (m_SelectedNodes.size() < 1) { m_Controls->leImage1->setText(QString(m_SelectedImageNode->GetNode()->GetName().c_str())); mitk::Vector3D aSpacing = tempImage->GetGeometry()->GetSpacing(); std::string text("x-spacing (" + std::to_string(aSpacing[0]) + ")"); m_Controls->tlParam1->setText(text.c_str()); text = "y-spacing (" + std::to_string(aSpacing[1]) + ")"; m_Controls->tlParam2->setText(text.c_str()); text = "z-spacing (" + std::to_string(aSpacing[2]) + ")"; m_Controls->tlParam3->setText(text.c_str()); if (tempImage->GetDimension() > 3) { // try to retrieve the TNC (for 4-D Processing ) this->InternalGetTimeNavigationController(); m_Controls->timeSliceNavigationWidget->setEnabled(true); m_Controls->tlTime->setEnabled(true); } } m_SelectedNodes.push_back(_DataNode); } if (m_SelectedNodes.size() > 0) { *m_SelectedImageNode = m_SelectedNodes[0]; } ResetParameterPanel(); } } void QmitkPreprocessingResampling::ResetOneImageOpPanel() { m_Controls->tlTime->setEnabled(false); m_Controls->btnDoIt->setEnabled(false); m_Controls->buttonExecuteOnMultipleImages->setEnabled(false); m_Controls->cbHideOrig->setEnabled(false); m_Controls->leImage1->setText(tr("Select an Image in Data Manager")); m_Controls->tlParam1->setText("x-spacing"); m_Controls->tlParam1->setText("y-spacing"); m_Controls->tlParam1->setText("z-spacing"); } void QmitkPreprocessingResampling::ResetParameterPanel() { m_Controls->btnDoIt->setEnabled(true); m_Controls->buttonExecuteOnMultipleImages->setEnabled(true); m_Controls->cbHideOrig->setEnabled(true); } void QmitkPreprocessingResampling::ResetTwoImageOpPanel() { } void QmitkPreprocessingResampling::StartMultipleImagesButtonClicked() { for (auto currentSelectedNode : m_SelectedNodes) { m_SelectedImageNode->RemoveAllNodes(); *m_SelectedImageNode = currentSelectedNode; StartButtonClicked(); } } void QmitkPreprocessingResampling::StartButtonClicked() { if(!m_SelectedImageNode->GetNode()) return; this->BusyCursorOn(); mitk::Image::Pointer newImage; try { newImage = dynamic_cast(m_SelectedImageNode->GetNode()->GetData()); } catch ( std::exception &e ) { QString exceptionString = tr("An error occured during image loading:\n"); exceptionString.append( e.what() ); QMessageBox::warning( nullptr, "Preprocessing - Resampling: ", exceptionString , QMessageBox::Ok, QMessageBox::NoButton ); this->BusyCursorOff(); return; } // check if input image is valid, casting does not throw exception when casting from 'NULL-Object' if ( (! newImage) || (newImage->IsInitialized() == false) ) { this->BusyCursorOff(); QMessageBox::warning( nullptr, "Preprocessing - Resampling", tr("Input image is broken or not initialized. Returning."), QMessageBox::Ok, QMessageBox::NoButton ); return; } // check if operation is done on 4D a image time step if(newImage->GetDimension() > 3) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(newImage); timeSelector->SetTimeNr(m_Controls->timeSliceNavigationWidget->GetPos()); timeSelector->Update(); newImage = timeSelector->GetOutput(); } // check if image or vector image ImageType::Pointer itkImage = ImageType::New(); VectorImageType::Pointer itkVecImage = VectorImageType::New(); int isVectorImage = newImage->GetPixelType().GetNumberOfComponents(); if(isVectorImage > 1) { CastToItkImage( newImage, itkVecImage ); } else { CastToItkImage( newImage, itkImage ); } std::stringstream nameAddition(""); double dparam1 = m_Controls->dsbParam1->value(); double dparam2 = m_Controls->dsbParam2->value(); double dparam3 = m_Controls->dsbParam3->value(); try{ std::string selectedInterpolator; ResampleImageFilterType::Pointer resampler = ResampleImageFilterType::New(); switch (m_SelectedInterpolation) { case LINEAR: { LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); resampler->SetInterpolator(interpolator); selectedInterpolator = "Linear"; break; } case NEAREST: { NearestInterpolatorType::Pointer interpolator = NearestInterpolatorType::New(); resampler->SetInterpolator(interpolator); selectedInterpolator = "Nearest"; break; } case SPLINE: { BSplineInterpolatorType::Pointer interpolator = BSplineInterpolatorType::New(); interpolator->SetSplineOrder(3); resampler->SetInterpolator(interpolator); selectedInterpolator = "B-Spline"; break; } default: { LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); resampler->SetInterpolator(interpolator); selectedInterpolator = "Linear"; break; } } resampler->SetInput( itkImage ); resampler->SetOutputOrigin( itkImage->GetOrigin() ); ImageType::SizeType input_size = itkImage->GetLargestPossibleRegion().GetSize(); ImageType::SpacingType input_spacing = itkImage->GetSpacing(); ImageType::SizeType output_size; ImageType::SpacingType output_spacing; if (dparam1 > 0) { output_size[0] = std::ceil(input_size[0] * (input_spacing[0] / dparam1)); output_spacing[0] = dparam1; } else { output_size[0] = std::ceil(input_size[0] * (-1.0 / dparam1)); output_spacing[0] = -1.0*input_spacing[0] * dparam1; } if (dparam2 > 0) { output_size[1] = std::ceil(input_size[1] * (input_spacing[1] / dparam2)); output_spacing[1] = dparam2; } else { output_size[1] = std::ceil(input_size[1] * (-1.0 / dparam2)); output_spacing[1] = -1.0*input_spacing[1] * dparam2; } if (dparam3 > 0) { output_size[2] = std::ceil(input_size[2] * (input_spacing[2] / dparam3)); output_spacing[2] = dparam3; } else { output_size[2] = std::ceil(input_size[2] * (-1.0 / dparam3)); output_spacing[2] = -1.0*input_spacing[2] * dparam3; } resampler->SetSize( output_size ); resampler->SetOutputSpacing( output_spacing ); resampler->SetOutputDirection( itkImage->GetDirection() ); resampler->UpdateLargestPossibleRegion(); ImageType::Pointer resampledImage = resampler->GetOutput(); newImage = mitk::ImportItkImage( resampledImage )->Clone(); nameAddition << "_Resampled_" << selectedInterpolator; std::cout << "Resampling successful." << std::endl; } catch (...) { this->BusyCursorOff(); QMessageBox::warning(nullptr, "Warning", "Problem when applying filter operation. Check your input..."); return; } newImage->DisconnectPipeline(); // adjust level/window to new image mitk::LevelWindow levelwindow; levelwindow.SetAuto( newImage ); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); levWinProp->SetLevelWindow( levelwindow ); // compose new image name std::string name = m_SelectedImageNode->GetNode()->GetName(); if (name.find(".nrrd") == name.size() -5 ) { name = name.substr(0,name.size() -5); } name.append( nameAddition.str() ); // create final result MITK data storage node mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetProperty( "levelwindow", levWinProp ); result->SetProperty( "name", mitk::StringProperty::New( name.c_str() ) ); result->SetData( newImage ); // for vector images, a different mapper is needed if(isVectorImage > 1) { mitk::VectorImageMapper2D::Pointer mapper = mitk::VectorImageMapper2D::New(); result->SetMapper(1,mapper); } // add new image to data storage and set as active to ease further processing GetDataStorage()->Add( result, m_SelectedImageNode->GetNode() ); if ( m_Controls->cbHideOrig->isChecked() == true ) m_SelectedImageNode->GetNode()->SetProperty( "visible", mitk::BoolProperty::New(false) ); // show the results mitk::RenderingManager::GetInstance()->RequestUpdateAll(); this->BusyCursorOff(); } void QmitkPreprocessingResampling::SelectInterpolator(int interpolator) { switch (interpolator) { case 0: { m_SelectedInterpolation = LINEAR; break; } case 1: { m_SelectedInterpolation = NEAREST; break; } case 2: { m_SelectedInterpolation = SPLINE; } } }