diff --git a/Modules/Core/TestingHelper/include/mitkRenderingTestHelper.h b/Modules/Core/TestingHelper/include/mitkRenderingTestHelper.h index ff0ccf657b..89fe0f38d2 100644 --- a/Modules/Core/TestingHelper/include/mitkRenderingTestHelper.h +++ b/Modules/Core/TestingHelper/include/mitkRenderingTestHelper.h @@ -1,215 +1,215 @@ /*============================================================================ 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 mitkRenderingTestHelper_h #define mitkRenderingTestHelper_h #include #include #include #include #include class vtkRenderWindow; class vtkRenderer; namespace mitk { class MITKTESTINGHELPER_EXPORT RenderingTestHelper { public: /** @brief Generate a rendering test helper object including a render window of the size width * height (in pixel). @param width @param height @param argc Number of parameters. (here: Images) "Usage: [filename1 filenam2 -V referenceScreenshot (optional -T /directory/to/save/differenceImage)] @param argv Given parameters. If no data is inserted via commandline, you can add data later via AddNodeToDataStorage(). @param antiAliasing The anti-aliasing mode. **/ RenderingTestHelper( int width, int height, int argc, char *argv[], AntiAliasing antiAliasing = AntiAliasing::None); /** @brief Generate a rendering test helper object including a render window of the size width * height (in * pixel).*/ RenderingTestHelper( int width, int height, AntiAliasing antiAliasing = AntiAliasing::None); /** Default destructor */ ~RenderingTestHelper(); /** @brief Getter for the vtkRenderer. **/ vtkRenderer *GetVtkRenderer(); /** @brief Getter for the vtkRenderWindow which should be used to call vtkRegressionTestImage. **/ vtkRenderWindow *GetVtkRenderWindow(); /** @brief Method can be used to save a screenshot (e.g. reference screenshot as a .png file. @param fileName The filename of the new screenshot (including path). **/ void SaveAsPNG(std::string fileName); /** - * @brief SetStopRenderWindow Convenience method to make the renderwindow hold after rendering. Usefull for + * @brief SetStopRenderWindow Convenience method to make the renderwindow hold after rendering. Useful for * debugging. * @param automaticallyCloseRenderWindow Flag indicating whether the renderwindow should automatically close (false, default) or stay open - * (true). Usefull for debugging. + * (true). Useful for debugging. */ void SetAutomaticallyCloseRenderWindow(bool automaticallyCloseRenderWindow); /** @brief This method set the property of the member datastorage @param propertyKey @param property Set a property for each image in the datastorage m_DataStorage. If you want to set the property for a single data node, use GetDataStorage() and set the property yourself for the destinct node. **/ void SetImageProperty(const char *propertyKey, mitk::BaseProperty *property); /** @brief Set the view direction of the renderwindow (e.g. sagittal, coronal, axial) **/ void SetViewDirection(mitk::SliceNavigationController::ViewDirection viewDirection); /** @brief Reorient the slice (e.g. rotation and translation like the swivel mode). **/ void ReorientSlices(mitk::Point3D origin, mitk::Vector3D rotation); /** @brief Render everything into an mitkRenderWindow. Call SetViewDirection() and SetProperty() before this method. **/ void Render(); /** @brief Returns the datastorage, in order to modify the data inside a rendering test. **/ mitk::DataStorage::Pointer GetDataStorage(); /** * @brief SetMapperID Change between Standard2D and 3D mappers. * @param id Enum mitk::BaseRenderer::StandardMapperSlot which defines the mapper. */ void SetMapperID(mitk::BaseRenderer::StandardMapperSlot id); /** * @brief AddNodeToStorage Add a node to the datastorage and perform a reinit which is necessary for rendering. * @param node The data you want to add. */ void AddNodeToStorage(mitk::DataNode::Pointer node); /** * @brief SetMapperIDToRender3D Convenience method to render in a 3D renderwindow. * @warning Does not add helper objects like the image planes to render images in 3D. */ void SetMapperIDToRender3D(); /** * @brief SetMapperIDToRender2D Convenience method to render in a 2D renderwindow. */ void SetMapperIDToRender2D(); /** * @brief SaveReferenceScreenShot Convenience method to save a reference screen shot. * @param fileName Path/to/save/the/png/file. */ void SaveReferenceScreenShot(std::string fileName); /** * @brief CompareRenderWindowAgainstReference Convenience method to compare the image rendered in the internal renderwindow against a reference screen shot. * Usage of vtkTesting::Test: vtkTesting::Test( argc, argv, vtkRenderWindow, threshold ) Set a vtkRenderWindow containing the desired scene. This is automatically rendered. vtkTesting::Test() automatically searches in argc and argv[] for a path a valid image with -V. If the test failed with the first image (foo.png) it checks if there are images of the form foo_N.png (where N=1,2,3...) and compare against them. This allows for multiple valid images. * @param argc Number of arguments. * @param argv Arguments must(!) contain the term "-V Path/To/Valid/Image.png" * @param threshold Allowed difference between two images. Default = 10.0 and was taken from VTK. * @return True if the images are equal regarding the threshold. False in all other cases. */ bool CompareRenderWindowAgainstReference(int argc, char *argv[], double threshold = 10.0); /** - * @brief The ArgcHelperClass class is a convinience class to convert a vector + * @brief The ArgcHelperClass class is a convenience class to convert a vector * of strings to the standard c++ argv and argc arguments. This is necessary for * the vtkTesting::Test, since is requires the reference image (and other * optional parameters) via command line. */ class ArgcHelperClass { private: /** * Members for conversion. */ std::vector argv; std::vector> argvec; public: ArgcHelperClass(const std::vector &argstrings) : argv(argstrings.size() + 1), argvec(argstrings.size() + 1) { std::vector cmdArgs; cmdArgs.push_back(mitk::IOUtil::GetProgramPath()); cmdArgs.insert(cmdArgs.end(), argstrings.begin(), argstrings.end()); for (std::size_t i = 0; i < cmdArgs.size(); ++i) { argvec[i].assign(cmdArgs[i].begin(), cmdArgs[i].end()); argvec[i].push_back('\0'); argv[i] = &argvec[i][0]; } } char **GetArgv() { return &argv[0]; } int GetArgc() { return argv.size(); } }; protected: /** * @brief Initialize Internal method to initialize the renderwindow and set the datastorage. * @param width Height of renderwindow. * @param height Width of renderwindow. * @param antiAliasing The anti-aliasing mode. */ void Initialize( int width, int height, AntiAliasing antiAliasing = AntiAliasing::None); /** @brief This method tries to load the given file into a member datastorage, in order to render it. @param filename The filename of the file to be loaded (including path). **/ void AddToStorage(const std::string &filename); /** @brief This method tries to parse the given argv for files (e.g. images) and load them into a member datastorage, in order to render it. @param argc Number of parameters. @param argv Given parameters. **/ void SetInputFileNames(int argc, char *argv[]); mitk::RenderWindow::Pointer m_RenderWindow; //<< Contains the mitkRenderWindow into which the test renders the data mitk::DataStorage::Pointer m_DataStorage; //<< Contains the mitkDataStorage which contains the data to be rendered bool m_AutomaticallyCloseRenderWindow; //<< Flag indicating whether the renderwindow should automatically close (true, - // default) or stay open (false). Usefull for debugging. + // default) or stay open (false). Useful for debugging. }; } // namespace mitk #endif diff --git a/Modules/Core/TestingHelper/include/mitkTestingMacros.h b/Modules/Core/TestingHelper/include/mitkTestingMacros.h index f838eeffe0..9c7b982eb7 100644 --- a/Modules/Core/TestingHelper/include/mitkTestingMacros.h +++ b/Modules/Core/TestingHelper/include/mitkTestingMacros.h @@ -1,392 +1,392 @@ /*============================================================================ 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 mitkTestingMacros_h #define mitkTestingMacros_h #include #include #include #include #include #include #include #include #include #include namespace mitk { /** @brief Indicate a failed test. */ class TestFailedException : public std::exception { public: TestFailedException() {} }; } /** * @brief Output some text without generating a terminating newline. Include * * @ingroup MITKTestingAPI */ #define MITK_TEST_OUTPUT_NO_ENDL(x) std::cout x; /** * @brief Output some text. * * @ingroup MITKTestingAPI */ #define MITK_TEST_OUTPUT(x) MITK_TEST_OUTPUT_NO_ENDL(x << "\n") /** * @brief Do some general test preparations. Must be called first in the * main test function. * * @deprecatedSince{2013_09} Use MITK_TEST_SUITE_REGISTRATION instead. * @ingroup MITKTestingAPI */ #define MITK_TEST_BEGIN(testName) \ std::string mitkTestName(#testName); \ mitk::TestManager::GetInstance()->Initialize(); \ try \ { /** * @brief Fail and finish test with message MSG * * @deprecatedSince{2013_09} Use CPPUNIT_FAIL instead * @ingroup MITKTestingAPI */ #define MITK_TEST_FAILED_MSG(MSG) \ MITK_TEST_OUTPUT(MSG) \ throw mitk::TestFailedException(); /** * @brief Must be called last in the main test function. * * @deprecatedSince{2013_09} Use MITK_TEST_SUITE_REGISTRATION instead. * @ingroup MITKTestingAPI */ #define MITK_TEST_END() \ } \ catch (const mitk::TestFailedException &) \ { \ MITK_TEST_OUTPUT(<< "Further test execution skipped.") \ mitk::TestManager::GetInstance()->TestFailed(); \ } \ catch (const std::exception &ex) \ { \ - MITK_TEST_OUTPUT(<< "std::exception occured " << ex.what()) \ + MITK_TEST_OUTPUT(<< "std::exception occurred " << ex.what()) \ mitk::TestManager::GetInstance()->TestFailed(); \ } \ if (mitk::TestManager::GetInstance()->NumberOfFailedTests() > 0) \ { \ MITK_TEST_OUTPUT(<< mitkTestName << ": [DONE FAILED] , subtests passed: " \ << mitk::TestManager::GetInstance()->NumberOfPassedTests() \ << " failed: " \ << mitk::TestManager::GetInstance()->NumberOfFailedTests()) \ return EXIT_FAILURE; \ } \ else \ { \ MITK_TEST_OUTPUT(<< mitkTestName << ": " << mitk::TestManager::GetInstance()->NumberOfPassedTests() \ << " tests [DONE PASSED]") \ return EXIT_SUCCESS; \ } /** * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT or CPPUNIT_ASSERT_MESSAGE instead. */ #define MITK_TEST_CONDITION(COND, MSG) \ MITK_TEST_OUTPUT_NO_ENDL(<< MSG) \ if (!(COND)) \ { \ mitk::TestManager::GetInstance()->TestFailed(); \ MITK_TEST_OUTPUT(<< " [FAILED]\n" \ << "In " \ << __FILE__ \ << ", line " \ << __LINE__ \ << ": " #COND " : [FAILED]") \ } \ else \ { \ MITK_TEST_OUTPUT(<< " [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT or CPPUNIT_ASSERT_MESSAGE instead. */ #define MITK_TEST_CONDITION_REQUIRED(COND, MSG) \ MITK_TEST_OUTPUT_NO_ENDL(<< MSG) \ if (!(COND)) \ { \ MITK_TEST_FAILED_MSG(<< " [FAILED]\n" \ << " +--> in " \ << __FILE__ \ << ", line " \ << __LINE__ \ << ", expression is false: \"" #COND "\"") \ } \ else \ { \ MITK_TEST_OUTPUT(<< " [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * \brief Begin block which should be checked for exceptions * * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT_THROW instead. * @ingroup MITKTestingAPI * * This macro, together with MITK_TEST_FOR_EXCEPTION_END, can be used * to test whether a code block throws an expected exception. The test FAILS if the * exception is NOT thrown. A simple example: * MITK_TEST_FOR_EXCEPTION_BEGIN(itk::ImageFileReaderException) typedef itk::ImageFileReader< itk::Image > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName("/tmp/not-existing"); reader->Update(); MITK_TEST_FOR_EXCEPTION_END(itk::ImageFileReaderException) * */ #define MITK_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ try \ { /** * @deprecatedSince{2013_09} */ #define MITK_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) \ mitk::TestManager::GetInstance()->TestFailed(); \ MITK_TEST_OUTPUT(<< "Expected an '" << #EXCEPTIONCLASS << "' exception. [FAILED]") \ } \ catch (const EXCEPTIONCLASS &) \ { \ MITK_TEST_OUTPUT(<< "Caught an expected '" << #EXCEPTIONCLASS << "' exception. [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * @brief Simplified version of MITK_TEST_FOR_EXCEPTION_BEGIN / END for * a single statement * * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT_THROW instead. * @ingroup MITKTestingAPI */ #define MITK_TEST_FOR_EXCEPTION(EXCEPTIONCLASS, STATEMENT) \ MITK_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ STATEMENT; \ MITK_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) /** * @brief Testing macro to test if two objects are equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * Feel free to implement mitk::Equal for your own datatype or purpose. * * @param EXPECTED First object. * @param ACTUAL Second object. * @param MSG Message to appear with the test. * @throw Throws mitkException if a nullptr pointer is given as input. */ #define MITK_ASSERT_EQUAL(EXPECTED, ACTUAL, MSG) \ if (((EXPECTED).IsNull()) || ((ACTUAL).IsNull())) \ { \ mitkThrow() << "mitk::Equal does not work with nullptr pointer input."; \ } \ CPPUNIT_ASSERT_MESSAGE(MSG, mitk::Equal(*(EXPECTED), *(ACTUAL), mitk::eps, true)) /** * @brief Testing macro to test if two objects are not equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * * @deprecatedSince{2013_09} Use MITK_ASSERT_NOT_EQUAL instead. * * @param OBJ1 First object. * @param OBJ2 Second object. * @param MSG Message to appear with the test. * * \sa MITK_TEST_EQUAL */ #define MITK_TEST_NOT_EQUAL(OBJ1, OBJ2, MSG) \ CPPUNIT_ASSERT_MESSAGE(MSG, !mitk::Equal(*(OBJ1), *(OBJ2), mitk::eps, true)) /** * @brief Testing macro to test if two objects are not equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * * @param OBJ1 First object. * @param OBJ2 Second object. * @param MSG Message to appear with the test. * @throw Throws mitkException if a nullptr pointer is given as input. * * \sa MITK_ASSERT_EQUAL */ #define MITK_ASSERT_NOT_EQUAL(OBJ1, OBJ2, MSG) \ if (((OBJ1).IsNull()) || ((OBJ2).IsNull())) \ { \ mitkThrow() << "mitk::Equal does not work with nullptr pointer input."; \ } \ CPPUNIT_ASSERT_MESSAGE(MSG, !mitk::Equal(*(OBJ1), *(OBJ2), mitk::eps, true)) /** * @brief Registers the given test suite. * * @ingroup MITKTestingAPI * * @param TESTSUITE_NAME The name of the test suite class, without "TestSuite" * at the end. */ #define MITK_TEST_SUITE_REGISTRATION(TESTSUITE_NAME) \ int TESTSUITE_NAME##Test(int /*argc*/, char * /*argv*/ []) \ { \ int result = 0; \ try \ { \ CppUnit::TextUi::TestRunner runner; \ runner.addTest(TESTSUITE_NAME##TestSuite::suite()); \ result = runner.run() ? 0 : 1; \ } \ catch (const mitk::TestNotRunException& e) \ { \ MITK_WARN << "Test not run: " << e.GetDescription(); \ result = 77; \ } \ return result; \ } /** * @brief Adds a test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases. * The macro internally just calls the CPPUNIT_TEST macro. * - * @param TESTMETHOD The name of the member funtion test. + * @param TESTMETHOD The name of the member function test. */ #define MITK_TEST(TESTMETHOD) CPPUNIT_TEST(TESTMETHOD) /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need custom parameters. * * @param TESTMETHOD The name of the member function test. * @param ARGS A std::vector object containing test parameter. * * @note Use the macro MITK_PARAMETERIZED_TEST only if you know what * you are doing. If you are not sure, use MITK_TEST instead. */ #define MITK_PARAMETERIZED_TEST(TESTMETHOD, ARGS) \ \ { \ std::string testName = #TESTMETHOD; \ for (std::size_t i = 0; i < ARGS.size(); ++i) \ { \ testName += "_" + ARGS[i]; \ } \ CPPUNIT_TEST_SUITE_ADD_TEST((new mitk::TestCaller( \ context.getTestNameFor(testName), &TestFixtureType::TESTMETHOD, context.makeFixture(), args))); \ } /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need parameters from the command line. * * @warning Use the macro MITK_PARAMETERIZED_CMD_LINE_TEST only * if you know what you are doing. If you are not sure, use * MITK_TEST instead. MITK_PARAMETERIZED_CMD_LINE_TEST is meant * for migrating from ctest to CppUnit. If you implement new * tests, the MITK_TEST macro will be sufficient. * * @param TESTMETHOD The name of the member function test. */ #define MITK_PARAMETERIZED_CMD_LINE_TEST(TESTMETHOD) \ CPPUNIT_TEST_SUITE_ADD_TEST((new mitk::TestCaller( \ context.getTestNameFor(#TESTMETHOD), &TestFixtureType::TESTMETHOD, context.makeFixture()))); /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need one custom parameter. * * @param TESTMETHOD The name of the member function test. * @param arg1 A custom string parameter being passed to the fixture. * * @note Use the macro MITK_PARAMETERIZED_TEST_1 only if you know what * you are doing. If you are not sure, use MITK_TEST instead. * * @see MITK_PARAMETERIZED_TEST */ #define MITK_PARAMETERIZED_TEST_1(TESTMETHOD, arg1) \ \ { \ std::vector args; \ args.push_back(arg1); \ MITK_PARAMETERIZED_TEST(TESTMETHOD, args) \ } /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need two custom parameter. * * @param TESTMETHOD The name of the member function test. * @param arg1 A custom string parameter being passed to the fixture. * @param arg2 * * @note Use the macro MITK_PARAMETERIZED_TEST_2 only if you know what * you are doing. If you are not sure, use MITK_TEST instead. * * @see MITK_PARAMETERIZED_TEST */ #define MITK_PARAMETERIZED_TEST_2(TESTMETHOD, arg1, arg2) \ \ { \ std::vector args; \ args.push_back(arg1); \ args.push_back(arg2); \ MITK_PARAMETERIZED_TEST(TESTMETHOD, args) \ } #endif diff --git a/Modules/Core/doc/Doxygen/ModuleAdaptors.dox b/Modules/Core/doc/Doxygen/ModuleAdaptors.dox index 7b94a57e62..4e6e90f6c3 100644 --- a/Modules/Core/doc/Doxygen/ModuleAdaptors.dox +++ b/Modules/Core/doc/Doxygen/ModuleAdaptors.dox @@ -1,139 +1,139 @@ /** \defgroup Adaptor Adaptor Classes \ingroup ProcessAdaptor \brief This subcategory includes adaptor classes for the integration of algorithms from other toolkits, especially ITK. The task of most of the classes in this category is to deal with the conversion between the (templated) itk::Image and the (not-templated) mitk::Image. Methods for conversion are provided for both directions: \li \ref MitkToItk \li \ref ItkToMitk Care has to be taken regarding the involved coordinate systems, see \ref ItkToMitkCoordinateSystems. For limitations on ITK-type conversion see the section \ref ModuleAdaptorLimitations. VTK-based access to MITK images is straightforward: simply ask your mitk::Image for a -\a vtkImageData by calling Image:GetVtkImageData. Similarily, to get a \a vtkPolyData +\a vtkImageData by calling Image:GetVtkImageData. Similarly, to get a \a vtkPolyData from the MITK class for storing surfaces, mitk::Surface, call Surface::GetVtkPolyData. \section MitkToItk MITK to ITK adaptors Pixel type and dimension of MITK images can be specified at run time whereas ITK images are templated over the pixel type and the dimension, thus in ITK both need to be specified at compile time. There two different situations, which are covered in the following sub-sections: \li Either you know the pixel type/dimension the ITK image should have at compile time for some reason (e.g., you always create MITK images of a specific pixel type/dimension) -\li or the pixel type/dimension of an MITK image is really unkown and the ITK image should - have the same (unkown) type. +\li or the pixel type/dimension of an MITK image is really unknown and the ITK image should + have the same (unknown) type. \subsection MitkToFixedItk Converting MITK images to ITK images with known type If you know the type (pixel type and dimension) of the MITK image you have two options: \li mitk::ImageToItk: is a process class that takes an mitk::Image as input and produces an itk::Image of the given type \a TOutputImage as output (to be accessed using \a GetOutput()). In case the MITK image does not have the same type as \a TOutputImage an exception will be thrown. \li mitk::CastToItkImage: this function has two parameters, the mitk::Image (input) and a smartpointer to an itk::Image (output, does not need to be initialized). In case the MITK image does not have the same type as the ITK image it will be casted (if possible; done via itk::CastImageFilter). Thus, mitk::CastToItkImage is the more powerful variant: here it is sufficient that you know what you want to have (the ITK data type), to which the MITK image will be casted, if needed. -\subsection MitkToUnkownItk Accessing an MITK image as an ITK image (type unkown) +\subsection MitkToUnkownItk Accessing an MITK image as an ITK image (type unknown) If you do not know the pixel type/dimension of an MITK image in advance and the ITK image should -have the same (unkown) type, e.g., to run a filter on the MITK image data, we cannot really convert -to one ITK image. This is simply, because we cannot instantiate an itk::Image object with unkown +have the same (unknown) type, e.g., to run a filter on the MITK image data, we cannot really convert +to one ITK image. This is simply, because we cannot instantiate an itk::Image object with unknown pixel type/dimension. -Nevertheless, MITK provides a way to access an MITK image as if it was an ITK image of unkown type. +Nevertheless, MITK provides a way to access an MITK image as if it was an ITK image of unknown type. To do so, first define an access method, which is templated as an ITK image is: \code template MyAccessMethod(itk::Image* itkImage) { ... } \endcode If you don't understand this template syntax, we need to refer you to an C++ text book. Understanding template syntax is crucial to successfully using ITK. To call this templated method with an (untemplated) mitk::Image, you can use the #AccessByItk macro (or one of its variants) from mitkImageAccessByItk.h. This macro checks for the actual image type of -the mitk::Image and does any neccessary conversions. This works for all configured pixel types (default +the mitk::Image and does any necessary conversions. This works for all configured pixel types (default is char, unsigned char, short, unsigned short, int, unsigned int, float, and double) and dimensions (default is 2 and 3). You can change the considered default pixel types and dimensions by modifying the CMake variables MITK_ACCESSBYITK_*. \code AccessByItk(mitkImage, MyAccessMethod) \endcode An example is given in \ref Step06Page. The AccessBy... macros create quite a lot of code: the user defined access method has to be compiled for all considered pixel types \em times the supported dimensions (default is 2 and 3). Therefore, depending on the complexity of the access method, some compilers may run into problems with memory. One workaround is to use explicit instantiation and distribute it on multiple files. The macro #InstantiateAccessFunction and its variants are for this purpose. An example is again given in \ref Step06Page. Another workaround is to reduce the created code by fixing either the type (#AccessFixedTypeByItk) or dimension (#AccessFixedDimensionByItk). There is one variant of AccessByItk... for passing additional parameters to the access-function, called #AccessFixedTypeByItk_n. \link mitkImage.h \endlink \link mitkImageCast.h \endlink \link mitkImageToItk.h \endlink \link mitkITKImageImport.h \endlink \section ItkToMitk ITK to MITK adaptors Converting ITK images to MITK is easier than the other way round. Basically, you have three options: \li mitk::ITKImageImport: is a process class that takes an itk::Image of the given type \a TOutputImage as input and produces an mitk::Image as output (to be accessed using \a GetOutput()). The image data contained in the itk::Image is referenced, not copied. \li mitk::ImportItkImage: this function takes the itk::Image as input and returns an mitk::Image. Internally, it uses the class just described. So again, the image data contained in the itk::Image is referenced, not copied. \li mitk::CastToMitkImage: this function has two parameters, the itk::Image (input) and a smartpointer to an mitk::Image (output, does not need to be initialized). In contrast to the other described methods, this function copies the image data! \section ItkToMitkCoordinateSystems ITK image vs MITK coordinate systems Converting coordinates from the ITK physical coordinate system (which does not support rotated images) to the MITK world coordinate system should be performed via the Geometry3D of the Image, see mitk::Geometry3D::WorldToItkPhysicalPoint. \section ModuleAdaptorLimitations Limitations The \ref MitkToItk for unspecified types have to do type multiplexing at compile time. This is done for a limited number of pixel types and dimensions, defined during the CMake configuration process. Especially, color image types are not multiplexed. This is because many algorithms do not support color images (e.g. with data type itk::RGBPixel) because they do not have a scalar data type. If your algorithm do support color and you want to multiplex over all scalar as well as the color data type, try the following: \code try { AccessFixedPixelTypeByItk(myMitkImageThatMaybeColor, // The MITK image which may be a color image myAlgorithmFunction, // The template method being able to handle color MITK_ACCESSBYITK_PIXEL_TYPES_SEQ // The default pixel type sequence (itk::RGBPixel) // The additional type sequence ) } catch(const mitk::AccessByItkException& e) { // add error handling here } \endcode */ diff --git a/Modules/Core/doc/Doxygen/ModuleGeometry.dox b/Modules/Core/doc/Doxygen/ModuleGeometry.dox index 14515b2d51..29f560cb86 100644 --- a/Modules/Core/doc/Doxygen/ModuleGeometry.dox +++ b/Modules/Core/doc/Doxygen/ModuleGeometry.dox @@ -1,43 +1,43 @@ /** \defgroup Geometry Geometry Classes \ingroup Core \brief This subcategory includes the geometry classes, which describe the geometry of the data in space and time. The Geometry3D class holds (see figure) \li a bounding box which is axes-parallel in intrinsic coordinates (often integer indices of pixels), to be accessed by Geometry3D::GetBoundingBox() \li a transform to convert intrinsic coordinates into a world-coordinate system with coordinates in millimeters and milliseconds (floating point values), to be accessed by Geometry3D::GetIndexToWorldTransform() \li a life span, i.e. a bounding box in time in ms (with start and end time), to be accessed by Geometry3D::GetTimeBounds(). The default is minus infinity to plus infinity. \imageMacro{ModuleGeometryFig1.png,"Geometry: Bounding box and transform",14.82} Geometry3D and its sub-classes allow converting between intrinsic coordinates (called index or unit coordinates) and word-coordinates (called world or mm coordinates), e.g. Geometry3D::WorldToIndex. Every data object (sub-)class of BaseData has a TimeGeometry which is accessed by BaseData::GetTimeGeometry(). This TimeGeometry holds one or more Geometry3D objects which describes the object at specific time points, e.g. provides conversion between world and index coordinates and contains bounding boxes covering the area in which the data are placed. There is the possibility of using different implementations of the abstract TimeGeometry class which may differ in how the time steps are saved and the times are calculated. -There are two ways to represent a time, either by a TimePointType or a TimeStepType. The first is similar to the continous index coordinates and defines a Time Point in milliseconds from timepoint zero. The second type is similar to index coordinates. These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps(). The conversion between a time point and a time step is done by calling the method TimeGeometry::TimeStepToTimePoint() or TimeGeometry::TimePointToTimeStep(). Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points. Also the distance of the time steps does not need to be equidistant over time, it depends on the used TimeGeometry implementation. +There are two ways to represent a time, either by a TimePointType or a TimeStepType. The first is similar to the continuous index coordinates and defines a Time Point in milliseconds from timepoint zero. The second type is similar to index coordinates. These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps(). The conversion between a time point and a time step is done by calling the method TimeGeometry::TimeStepToTimePoint() or TimeGeometry::TimePointToTimeStep(). Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points. Also the distance of the time steps does not need to be equidistant over time, it depends on the used TimeGeometry implementation. Each TimeGeometry has a bounding box covering the whole area in which the corresponding object is situated during all time steps. This bounding box may be accessed by calling TimeGeometry::GetBoundingBoxInWorld() and is always in world coordinates. The bounding box is calculated from all time steps, to manually start this calculation process call TimeGeometry::Update(). The bounding box is not updated if the getter is called. The TimeGeometry does not provide a transformation of world coordinates into image coordinates since each time step may has a different transformation. If a conversion between image and world is needed, the Geometry3D for a specific time step or time point must be fetched either by TimeGeometry::GetGeometryForTimeStep() or TimeGeometry::GetGeometryForTimePoint() and then the conversion is calculated by using this geometry. The TimeGeometry class is an abstract class therefore it is not possible to instantiate it. instead a derived class must be used. Currently the only class that can be chosen is ProportionalTimeGeometry() which assumes that the time steps are ordered equidistant. To initialize an object with given geometries call ProportionalTimeGeometry::Initialize() with an existing Geometry3D and the number of time steps. The given geometries will be copied and not referenced! For each time step of a given object a geometry-object needs to be specified. This are Geometry3D objects of objects of classes which are derived from Geometry3D. For example, images uses the sub-class SlicedGeometry, which contains several Geometry2D objects. -Geometry instances referring to images need a slightly different definition of corners, see Geometry3D::SetImageGeometry. This is usualy automatically called by Image. +Geometry instances referring to images need a slightly different definition of corners, see Geometry3D::SetImageGeometry. This is usually automatically called by Image. The class SlicedGeometry3D contains a list of Geometry2D objects describing the slices in the data object. It has spatial steps from 0 to GetSlices(). SlicedGeometry3D::InitializeEvenlySpaced (Geometry2D *geometry2D, unsigned int slices) initializes a stack of slices with the same thickness, one starting at the position where the previous one ends. Geometry2D provides methods for working with 2D manifolds (i.e., simply spoken, an object that can be described using a 2D coordinate-system) in 3D space. For example it allows mapping a 3D point on the 2D manifold using Geometry2D::Map. The most important sub-class is PlaneGeometry2D, which describes a planar rectangle. \section ExampleForImage Putting it together for Image Image has a TimeGeometry, which contains one or more SlicedGeometry3D instances (one for each time step), all of which contain one or more instances of (sub-classes of) Geometry2D (usually PlaneGeometry2D). \deprecated For ITK rev. 3.8 and earlier: Converting coordinates from the ITK physical coordinate system (which did not support rotated images for ITK v3.8 and earlier) to the MITK world coordinate system should be performed via the Geometry3D of the Image, see Geometry3D::WorldToItkPhysicalPoint. -As a reminder: Geometry instances referring to images need a slightly different definition of corners, see Geometry3D::SetImageGeometry. This is usualy automatically called by Image. +As a reminder: Geometry instances referring to images need a slightly different definition of corners, see Geometry3D::SetImageGeometry. This is usually automatically called by Image. */ diff --git a/Modules/Core/doc/Doxygen/ModuleMicroServicesInterfaces.dox b/Modules/Core/doc/Doxygen/ModuleMicroServicesInterfaces.dox index 7d273765c7..7c263a9820 100644 --- a/Modules/Core/doc/Doxygen/ModuleMicroServicesInterfaces.dox +++ b/Modules/Core/doc/Doxygen/ModuleMicroServicesInterfaces.dox @@ -1,15 +1,15 @@ /** \defgroup MicroServices_Interfaces Micro Services Interfaces \ingroup Core -\brief Classes (interfaces) in this category are explicitely designed for usage with the MITK Micro Services. +\brief Classes (interfaces) in this category are explicitly designed for usage with the MITK Micro Services. All classes in this category are structs with no non-static members and only pure virtual functions (except for an inlined virtual destructor). Such a class will be called a \e interface. These interfaces are meant to be implemented by MITK module developers to enhance or overwrite certain aspects of the MITK toolkit. All implementations must be registered in the implementing module's \e activator (see mitk::ModuleActivator) using the module's mitk::ModuleContext instance. */ diff --git a/Modules/Core/include/mitkAbstractFileReader.h b/Modules/Core/include/mitkAbstractFileReader.h index 7b61ca5b32..310ad06fda 100644 --- a/Modules/Core/include/mitkAbstractFileReader.h +++ b/Modules/Core/include/mitkAbstractFileReader.h @@ -1,243 +1,243 @@ /*============================================================================ 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 AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 #define AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 // Macro #include // MITK #include #include #include // Microservices #include #include #include namespace us { struct PrototypeServiceFactory; } namespace mitk { class CustomMimeType; /** * @brief Base class for creating mitk::BaseData objects from files or streams. * @ingroup IO */ class MITKCORE_EXPORT AbstractFileReader : public mitk::IFileReader { public: void SetInput(const std::string &location) override; void SetInput(const std::string &location, std::istream *is) override; std::string GetInputLocation() const override; std::istream *GetInputStream() const override; MimeType GetRegisteredMimeType() const; /** * @brief Reads a path or stream and creates a list of BaseData objects. * * The default implementation of this method (1) calls DoRead() - * (Implement the specific reader operation there) and (2) it addes general + * (Implement the specific reader operation there) and (2) it adds general * meta information about the loading process. */ std::vector> Read() override; DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage &ds) override; ConfidenceLevel GetConfidenceLevel() const override; Options GetOptions() const override; us::Any GetOption(const std::string &name) const override; void SetOptions(const Options &options) override; void SetOption(const std::string &name, const us::Any &value) override; void AddProgressCallback(const ProgressCallback &callback) override; void RemoveProgressCallback(const ProgressCallback &callback) override; /** * Associate this reader with the MIME type returned by the current IMimeTypeProvider * service for the provided extension if the MIME type exists, otherwise registers * a new MIME type when RegisterService() is called. * * If no MIME type for \c extension is already registered, a call to RegisterService() * will register a new MIME type and associate this reader instance with it. The MIME * type id can be set via SetMimeType() or it will be auto-generated using \c extension, * having the form "application/vnd.mitk.". * * @param context */ us::ServiceRegistration RegisterService(us::ModuleContext *context = us::GetModuleContext()); void UnregisterService(); /** * @return A list of files that were loaded during the last call of Read. Has to be filled by the actual reader class. */ std::vector< std::string > GetReadFiles() override; void SetProperties(const PropertyList* properties) override; protected: /** * @brief An input stream wrapper. * * If a reader can only work with input streams, use an instance * of this class to either wrap the specified input stream or * create a new input stream based on the input location in the * file system. */ class MITKCORE_EXPORT InputStream : public std::istream { public: InputStream(IFileReader *writer, std::ios_base::openmode mode = std::ios_base::in); ~InputStream() override; private: std::istream *m_Stream; }; AbstractFileReader(); ~AbstractFileReader() override; AbstractFileReader(const AbstractFileReader &other); /** * Associate this reader instance with the given MIME type. * * If \c mimeType does not provide an extension list, an already * registered mime-type object is used. Otherwise, the first entry in * the extensions list is used to construct a mime-type name and * register it as a new CustomMimeType service object in the default * implementation of RegisterMimeType(). * * @param mimeType The mime type this reader can read. * @param description A human readable description of this reader. * * @throws std::invalid_argument if \c mimeType is empty. * * @see RegisterService */ explicit AbstractFileReader(const CustomMimeType &mimeType, const std::string &description); /** Method that should be implemented by derived classes and does the real loading. * This method is called by Read(). * This method must be implemented for each specific reader. Call * GetInputStream() first and check for a non-null stream to read from. * If the input stream is \c nullptr, use GetInputLocation() to read from a local * file-system path. * * If the reader cannot use streams directly, use GetLocalFileName() instead. * * @return The created BaseData objects. * @throws mitk::Exception * * @see GetLocalFileName() * @see IFileReader::Read() */ virtual std::vector> DoRead() = 0; virtual us::ServiceProperties GetServiceProperties() const; /** * Registers a new CustomMimeType service object. * * This method is called from RegisterService and the default implementation * registers a new mime-type service object if all of the following conditions * are true: * * - TODO * * @param context * @return * @throws std::invalid_argument if \c context is nullptr. */ virtual us::ServiceRegistration RegisterMimeType(us::ModuleContext *context); void SetMimeType(const CustomMimeType &mimeType); /** * @return The mime-type this reader can handle. */ const CustomMimeType *GetMimeType() const; void SetMimeTypePrefix(const std::string &prefix); std::string GetMimeTypePrefix() const; void SetDescription(const std::string &description); std::string GetDescription() const; void SetDefaultOptions(const Options &defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file reader. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which reader to use if several * equivalent readers have been found. * It may be used to replace a default reader from MITK in your own project. * E.g. if you want to use your own reader for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; /** * @brief Get a local file name for reading. * * This is a convenience method for readers which cannot work natively * with input streams. If no input stream has been been set, * this method just returns the result of GetLocation(). However, if * SetLocation(std::string, std::istream*) has been called with a non-null * input stream, this method writes the contents of the stream to a temporary * file and returns the name of the temporary file. * * The temporary file is deleted when either SetLocation(std::string, std::istream*) * is called again with a different input stream or the destructor of this * class is called. * * This method does not validate file names set via SetInput(std::string). * * @return A file path in the local file-system for reading. */ std::string GetLocalFileName() const; virtual void SetDefaultDataNodeProperties(DataNode *node, const std::string &filePath); const PropertyList* GetProperties() const override; std::vector< std::string > m_ReadFiles; private: AbstractFileReader &operator=(const AbstractFileReader &other); virtual mitk::IFileReader *Clone() const = 0; class Impl; std::unique_ptr d; }; } // namespace mitk #endif /* AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Modules/Core/include/mitkAbstractFileWriter.h b/Modules/Core/include/mitkAbstractFileWriter.h index e8a64a2572..4bd26b552a 100644 --- a/Modules/Core/include/mitkAbstractFileWriter.h +++ b/Modules/Core/include/mitkAbstractFileWriter.h @@ -1,226 +1,226 @@ /*============================================================================ 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 AbstractFileWriter_H_HEADER_INCLUDED_C1E7E521 #define AbstractFileWriter_H_HEADER_INCLUDED_C1E7E521 // Macro #include // MITK #include #include // Microservices #include #include #include #include namespace us { struct PrototypeServiceFactory; } namespace mitk { class CustomMimeType; /** * @brief Base class for writing mitk::BaseData objects to files or streams. * * In general, all file writers should derive from this class, this way it is - * made sure that the new implementation is - * exposed to the Microservice-Framework and that is automatically available troughout MITK. + * made sure that the new implementation is exposed to the + * Microservice-Framework and that is automatically available throughout MITK. * The default implementation only requires one Write() * method and the Clone() method to be implemented. * * @ingroup IO */ class MITKCORE_EXPORT AbstractFileWriter : public mitk::IFileWriter { public: void SetInput(const BaseData *data) override; const BaseData *GetInput() const override; void SetOutputLocation(const std::string &location) override; std::string GetOutputLocation() const override; void SetOutputStream(const std::string &location, std::ostream *os) override; std::ostream *GetOutputStream() const override; /** * \brief Write the base data to the specified location or output stream. * * This method must be implemented for each specific writer. Call * GetOutputStream() first and check for a non-null stream to write to. * If the output stream is \c nullptr, use GetOutputLocation() to write * to a local file-system path. * * If the reader cannot use streams directly, use GetLocalFile() to retrieve * a temporary local file name instead. * * \throws mitk::Exception * * \see GetLocalFile() * \see IFileWriter::Write() */ void Write() override = 0; ConfidenceLevel GetConfidenceLevel() const override; MimeType GetRegisteredMimeType() const; Options GetOptions() const override; us::Any GetOption(const std::string &name) const override; void SetOptions(const Options &options) override; void SetOption(const std::string &name, const us::Any &value) override; void AddProgressCallback(const ProgressCallback &callback) override; void RemoveProgressCallback(const ProgressCallback &callback) override; us::ServiceRegistration RegisterService(us::ModuleContext *context = us::GetModuleContext()); void UnregisterService(); protected: /** * @brief A local file representation for streams. * * If a writer can only work with local files, use an instance * of this class to get either a temporary file name for writing * to the specified output stream or the original output location * if no output stream was set. */ class MITKCORE_EXPORT LocalFile { public: LocalFile(IFileWriter *writer); // Writes to the ostream and removes the temporary file ~LocalFile(); // Creates a temporary file for output operations. std::string GetFileName(); private: // disabled LocalFile(); LocalFile(const LocalFile &); LocalFile &operator=(const LocalFile &other); struct Impl; std::unique_ptr d; }; /** * @brief An output stream wrapper. * * If a writer can only work with output streams, use an instance * of this class to either wrap the specified output stream or * create a new output stream based on the output location in the * file system. */ class MITKCORE_EXPORT OutputStream : public std::ostream { public: OutputStream(IFileWriter *writer, std::ios_base::openmode mode = std::ios_base::trunc | std::ios_base::out); ~OutputStream() override; private: std::ostream *m_Stream; }; ~AbstractFileWriter() override; AbstractFileWriter(const AbstractFileWriter &other); AbstractFileWriter(const std::string &baseDataType); AbstractFileWriter(const std::string &baseDataType, const CustomMimeType &mimeType, const std::string &description); virtual us::ServiceProperties GetServiceProperties() const; /** * Registers a new CustomMimeType service object. * * This method is called from RegisterService and the default implementation * registers a new mime-type service object if all of the following conditions * are true: * * - TODO * * @param context * @return * @throws std::invalid_argument if \c context is nullptr. */ virtual us::ServiceRegistration RegisterMimeType(us::ModuleContext *context); void SetMimeType(const CustomMimeType &mimeType); /** * @return Get the mime-type this writer can handle. */ const CustomMimeType *GetMimeType() const; void SetMimeTypePrefix(const std::string &prefix); std::string GetMimeTypePrefix() const; /** * \brief Sets a human readable description of this writer. * * This will be used in file dialogs for example. */ void SetDescription(const std::string &description); std::string GetDescription() const; void SetDefaultOptions(const Options &defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file writer. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which writer to use if several * equivalent writers have been found. * It may be used to replace a default writer from MITK in your own project. * E.g. if you want to use your own writer for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; /** * \brief Sets the name of the mitk::Basedata that this writer is able to handle. * * The correct value is the one given as the first parameter in the mitkNewMacro of that BaseData derivate. * You can also retrieve it by calling GetNameOfClass() on an instance of said data. */ void SetBaseDataType(const std::string &baseDataType); virtual std::string GetBaseDataType() const; void ValidateOutputLocation() const; private: AbstractFileWriter &operator=(const AbstractFileWriter &other); virtual mitk::IFileWriter *Clone() const = 0; class Impl; std::unique_ptr d; }; } // namespace mitk #endif /* AbstractFileWriter_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Modules/Core/include/mitkAnnotation.h b/Modules/Core/include/mitkAnnotation.h index 91b86cf65f..5b74b463da 100644 --- a/Modules/Core/include/mitkAnnotation.h +++ b/Modules/Core/include/mitkAnnotation.h @@ -1,458 +1,458 @@ /*============================================================================ 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 Annotation_H #define Annotation_H #include "mitkServiceInterface.h" #include "usServiceRegistration.h" #include #include #include namespace mitk { /** \brief Base class for all Annotation * This class is to be implemented in order to create Annotation which are managed by a AbstractAnnotationRenderer. * This class contains an internal Propertylist for configuring the appearance of the implemented Overlay. */ class MITKCORE_EXPORT Annotation : public itk::Object { public: /** \brief Container for position and size on the display.*/ struct Bounds { itk::Point Position; itk::Point Size; }; - /** \brief Base class for mapper specific rendering ressources. + /** \brief Base class for mapper specific rendering resources. */ class MITKCORE_EXPORT BaseLocalStorage { public: bool IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Annotation *Annotation); inline void UpdateGenerateDataTime() { m_LastGenerateDataTime.Modified(); } inline itk::TimeStamp &GetLastGenerateDataTime() { return m_LastGenerateDataTime; } protected: /** \brief timestamp of last update of stored data */ itk::TimeStamp m_LastGenerateDataTime; }; /** * @brief Set the property (instance of BaseProperty) with key @a propertyKey in the PropertyList * of the @a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value. * * @warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). * * @sa GetProperty * @sa m_PropertyList * @sa m_MapOfPropertyLists */ void SetProperty(const std::string &propertyKey, const BaseProperty::Pointer &property); /** * @brief Replace the property (instance of BaseProperty) with key @a propertyKey in the PropertyList * of the @a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference. * * If @a renderer is @a nullptr the property is set in the BaseRenderer-independent * PropertyList of this Annotation. * @sa GetProperty * @sa m_PropertyList * @sa m_MapOfPropertyLists */ void ReplaceProperty(const std::string &propertyKey, const BaseProperty::Pointer &property); /** * @brief Add the property (instance of BaseProperty) if it does * not exist (or always if \a overwrite is \a true) * with key @a propertyKey in the PropertyList * of the @a renderer (if nullptr, use BaseRenderer-independent * PropertyList). This is set-by-value. * * For \a overwrite == \a false the property is \em not changed * if it already exists. For \a overwrite == \a true the method * is identical to SetProperty. * * @sa SetProperty * @sa GetProperty * @sa m_PropertyList * @sa m_MapOfPropertyLists */ void AddProperty(const std::string &propertyKey, const BaseProperty::Pointer &property, bool overwrite = false); /** * @brief Add values from another PropertyList. * * Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). * If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, * set \c replace. * * @param pList * @param replace true: if \c pList contains a property "visible" of type ColorProperty and our m_PropertyList * also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects * behind the pointer. * * @sa SetProperty * @sa ReplaceProperty * @sa m_PropertyList */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); /** * @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList * of the @a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If @a renderer is @a nullptr or the @a propertyKey cannot be found * in the PropertyList specific to @a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this Annotation is queried. * @sa GetPropertyList * @sa m_PropertyList * @sa m_MapOfPropertyLists */ mitk::BaseProperty *GetProperty(const std::string &propertyKey) const; /** * @brief Get the property of type T with key @a propertyKey from the PropertyList * of the @a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If @a renderer is @a nullptr or the @a propertyKey cannot be found * in the PropertyList specific to @a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this Annotation is queried. * @sa GetPropertyList * @sa m_PropertyList * @sa m_MapOfPropertyLists */ template bool GetProperty(itk::SmartPointer &property, const std::string &propertyKey) const { property = dynamic_cast(GetProperty(propertyKey)); return property.IsNotNull(); } /** * @brief Get the property of type T with key @a propertyKey from the PropertyList * of the @a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If @a renderer is @a nullptr or the @a propertyKey cannot be found * in the PropertyList specific to @a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this Annotation is queried. * @sa GetPropertyList * @sa m_PropertyList * @sa m_MapOfPropertyLists */ template bool GetProperty(T *&property, const std::string &propertyKey) const { property = dynamic_cast(GetProperty(propertyKey)); return property != nullptr; } /** * @brief Convenience access method for GenericProperty properties * (T being the type of the second parameter) * @return @a true property was found */ template bool GetPropertyValue(const std::string &propertyKey, T &value) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /** * @brief Convenience access method for bool properties (instances of * BoolProperty) * @return @a true property was found */ bool GetBoolProperty(const std::string &propertyKey, bool &boolValue) const; /** * @brief Convenience access method for int properties (instances of * IntProperty) * @return @a true property was found */ bool GetIntProperty(const std::string &propertyKey, int &intValue) const; /** * @brief Convenience access method for float properties (instances of * FloatProperty) * @return @a true property was found */ bool GetFloatProperty(const std::string &propertyKey, float &floatValue) const; /** * @brief Convenience access method for double properties (instances of * DoubleProperty) * @return @a true property was found */ bool GetDoubleProperty(const std::string &propertyKey, double &doubleValue) const; /** * @brief Convenience access method for string properties (instances of * StringProperty) * @return @a true property was found */ bool GetStringProperty(const std::string &propertyKey, std::string &string) const; /** * @brief Convenience method for setting int properties (instances of * IntProperty) */ void SetIntProperty(const std::string &propertyKey, int intValue); /** * @brief Convenience method for setting int properties (instances of * IntProperty) */ void SetBoolProperty(const std::string &propertyKey, bool boolValue); /** * @brief Convenience method for setting int properties (instances of * IntProperty) */ void SetFloatProperty(const std::string &propertyKey, float floatValue); /** * @brief Convenience method for setting int properties (instances of * IntProperty) */ void SetDoubleProperty(const std::string &propertyKey, double doubleValue); /** * @brief Convenience method for setting int properties (instances of * IntProperty) */ void SetStringProperty(const std::string &propertyKey, const std::string &string); /** * @brief Convenience access method for boolean properties (instances * of BoolProperty). Return value is the value of the property. If the property is * not found, the value of @a defaultIsOn is returned. * * Thus, the return value has a different meaning than in the * GetBoolProperty method! * @sa GetBoolProperty */ bool IsOn(const std::string &propertyKey, bool defaultIsOn = true) const { GetBoolProperty(propertyKey, defaultIsOn); return defaultIsOn; } /** * @brief Convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name") * @return @a true property was found */ bool GetName(std::string &nodeName, const std::string &propertyKey = "name") const; /** * @brief Extra convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name"). * * This method does not take the renderer specific * propertylists into account, because the name of an object should never be renderer specific. * @returns a std::string with the name of the object (content of "name" Property). * If there is no "name" Property, an empty string will be returned. */ virtual std::string GetName() const; /** * @brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const std::string &name); /** * @brief Convenience access method for color properties (instances of * ColorProperty) * @return @a true property was found */ bool GetColor(float rgb[], const std::string &propertyKey = "color") const; /** * @brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const mitk::Color &color, const std::string &propertyKey = "color"); /** * @brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(float red, float green, float blue, const std::string &propertyKey = "color"); /** * @brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const float rgb[], const std::string &propertyKey = "color"); /** * @brief Convenience access method for opacity properties (instances of * FloatProperty) * @return @a true property was found */ bool GetOpacity(float &opacity, const std::string &propertyKey = "opacity") const; /** * @brief Convenience method for setting opacity properties (instances of * FloatProperty) */ void SetOpacity(float opacity, const std::string &propertyKey = "opacity"); void SetText(std::string text); std::string GetText() const; void SetFontSize(int fontSize); int GetFontSize() const; /** * @brief Convenience access method for visibility properties (instances * of BoolProperty with property-key "visible") * @return @a true property was found * @sa IsVisible */ bool GetVisibility(bool &visible, const std::string &propertyKey = "visible") const; /** * @brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (@a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * @sa GetVisibility * @sa IsOn */ bool IsVisible(const std::string &propertyKey = "visible", bool defaultIsOn = true) const; /** * @brief Convenience method for setting visibility properties (instances * of BoolProperty) * @param visible If set to true, the data will be rendered. If false, the render will skip this data. - * @param propertyKey Can be used to specify a user defined name of the visibility propery. + * @param propertyKey Can be used to specify a user defined name of the visibility property. */ void SetVisibility(bool visible, const std::string &propertyKey = "visible"); /** \brief Adds the Annotation to the specified renderer. Update Annotation should be called soon in order to apply * all * properties*/ virtual void AddToBaseRenderer(BaseRenderer *renderer) = 0; /** \brief Adds the Annotation to the specified renderer. Update Annotation should be called soon in order to apply * all * properties*/ virtual void AddToRenderer(BaseRenderer *renderer, vtkRenderer *vtkrenderer) = 0; /** \brief Removes the Annotation from the specified renderer. It is not visible anymore then.*/ virtual void RemoveFromBaseRenderer(BaseRenderer *renderer) = 0; /** \brief Removes the Annotation from the specified renderer. It is not visible anymore then.*/ virtual void RemoveFromRenderer(BaseRenderer *renderer, vtkRenderer *vtkrenderer) = 0; /** \brief Applies all properties and should be called before the rendering procedure.*/ virtual void Update(BaseRenderer *renderer) = 0; /** \brief Returns position and size of the Annotation on the display.*/ virtual Bounds GetBoundsOnDisplay(BaseRenderer *renderer) const; /** \brief Sets position and size of the Annotation on the display.*/ virtual void SetBoundsOnDisplay(BaseRenderer *renderer, const Bounds &); void SetForceInForeground(bool forceForeground); bool IsForceInForeground() const; PropertyList *GetPropertyList() const; /** *\brief Returns the id that this device is registered with. The id will only be valid, if the * Annotation has been registered using RegisterAsMicroservice(). */ std::string GetMicroserviceID(); /** *\brief These Constants are used in conjunction with Microservices */ static const std::string US_INTERFACE_NAME; static const std::string US_PROPKEY_AnnotationNAME; static const std::string US_PROPKEY_ID; static const std::string US_PROPKEY_MODIFIED; static const std::string US_PROPKEY_RENDERER_ID; static const std::string US_PROPKEY_AR_ID; /** *\brief Registers this object as a Microservice, making it available to every module and/or plugin. * To unregister, call UnregisterMicroservice(). */ virtual void RegisterAsMicroservice(us::ServiceProperties props); /** *\brief Registers this object as a Microservice, making it available to every module and/or plugin. */ virtual void UnRegisterMicroservice(); void AnnotationModified(); mitkClassMacroItkParent(Annotation, itk::Object); protected: /** \brief explicit constructor which disallows implicit conversions */ Annotation(); /** \brief virtual destructor in order to derive from this class */ ~Annotation() override; /** * @brief BaseRenderer-independent PropertyList * * Properties herein can be overwritten specifically for each BaseRenderer * by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. */ PropertyList::Pointer m_PropertyList; /** * @brief Timestamp of the last change of m_Data */ itk::TimeStamp m_DataReferenceChangedTime; void SetUSProperty(const std::string &propertyKey, us::Any value); private: /** \brief render this Annotation on a foreground renderer */ bool m_ForceInForeground; /** \brief copy constructor */ Annotation(const Annotation &); /** \brief assignment operator */ Annotation &operator=(const Annotation &); private: us::ServiceRegistration m_ServiceRegistration; unsigned long m_PropertyListModifiedObserverTag; void PropertyListModified(const itk::Object *, const itk::EventObject &); }; } // namespace mitk MITK_DECLARE_SERVICE_INTERFACE(mitk::Annotation, "org.mitk.services.Annotation") #endif // Annotation_H diff --git a/Modules/Core/include/mitkApplicationCursor.h b/Modules/Core/include/mitkApplicationCursor.h index 349c24cce2..da717dde90 100644 --- a/Modules/Core/include/mitkApplicationCursor.h +++ b/Modules/Core/include/mitkApplicationCursor.h @@ -1,96 +1,96 @@ /*============================================================================ 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 MITK_APPLICATION_CURSOR_H_DEFINED_AND_ALL_IS_GOOD #define MITK_APPLICATION_CURSOR_H_DEFINED_AND_ALL_IS_GOOD #include "mitkNumericTypes.h" #include namespace mitk { /*! \brief Toolkit specific implementation of mitk::ApplicationCursor For any toolkit, this class has to be sub-classed. One instance of that sub-class has to - be registered with mitk::ApplicationCursor. See the (very simple) implmentation of + be registered with mitk::ApplicationCursor. See the (very simple) implementation of QmitkApplicationCursor for an example. */ class MITKCORE_EXPORT ApplicationCursorImplementation { public: /// Change the current application cursor virtual void PushCursor(const char *XPM[], int hotspotX, int hotspotY) = 0; /// Change the current application cursor virtual void PushCursor(std::istream &, int hotspotX, int hotspotY) = 0; /// Restore the previous cursor virtual void PopCursor() = 0; /// Get absolute mouse position on screen virtual const Point2I GetCursorPosition() = 0; /// Set absolute mouse position on screen virtual void SetCursorPosition(const Point2I &) = 0; virtual ~ApplicationCursorImplementation() {} protected: private: }; /*! \brief Allows to override the application's cursor. Base class for classes that allow to override the applications cursor with context dependent cursors. Accepts cursors in the XPM format. The behaviour is stack-like. You can push your cursor on top of the stack and later pop it to reset the cursor to its former state. This is mimicking Qt's Application::setOverrideCuror() behaviour, but should be ok for most cases where you want to switch a cursor. */ class MITKCORE_EXPORT ApplicationCursor { public: /// This class is a singleton. static ApplicationCursor *GetInstance(); /// To be called by a toolkit specific ApplicationCursorImplementation. static void RegisterImplementation(ApplicationCursorImplementation *implementation); /// Change the current application cursor void PushCursor(const char *XPM[], int hotspotX = -1, int hotspotY = -1); /// Change the current application cursor void PushCursor(std::istream &, int hotspotX = -1, int hotspotY = -1); /// Restore the previous cursor void PopCursor(); /// Get absolute mouse position on screen /// \return (-1, -1) if querying mouse position is not possible const Point2I GetCursorPosition(); /// Set absolute mouse position on screen void SetCursorPosition(const Point2I &); protected: /// Purposely hidden - singleton ApplicationCursor(); private: static ApplicationCursorImplementation *m_Implementation; }; } // namespace #endif diff --git a/Modules/Core/include/mitkArbitraryTimeGeometry.h b/Modules/Core/include/mitkArbitraryTimeGeometry.h index 9b82658e8a..0ad9797125 100644 --- a/Modules/Core/include/mitkArbitraryTimeGeometry.h +++ b/Modules/Core/include/mitkArbitraryTimeGeometry.h @@ -1,252 +1,252 @@ /*============================================================================ 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 ArbitraryTimeGeometry_h #define ArbitraryTimeGeometry_h //MITK #include #include #include namespace mitk { /** * \brief Organizes geometries over arbitrary defined time steps * * For this TimeGeometry implementation it is assumed that * the durations of the time steps are arbitrary and may differ. * The geometries of the time steps are independent, * and not linked to each other. Since the timeBounds of the * geometries are different for each time step it is not possible * to set the same geometry to different time steps. Instead * copies should be used. * @remark The lower time bound of a succeeding time step may not be smaller * than the upper time bound of its predecessor. Thus the list of time points is * always sorted by its lower time bounds. * @remark For the conversion between time step and time point the following assumption * is used.:\n * time step -> time point: time point is the lower time bound of the geometry indicated by step.\n * time point -> time step: associated time step is last step which lower time bound is smaller or equal then the time * point. * * \addtogroup geometry */ class MITKCORE_EXPORT ArbitraryTimeGeometry : public TimeGeometry { public: mitkClassMacro(ArbitraryTimeGeometry, TimeGeometry); ArbitraryTimeGeometry(); typedef ArbitraryTimeGeometry self; itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ TimeStepType CountTimeSteps() const override; /** * \brief Returns the first time point for which the time geometry instance is valid. * * Returns the first valid time point for this geometry. It is the lower time bound of * the first step. The time point is given in ms. */ TimePointType GetMinimumTimePoint() const override; /** * \brief Returns the last time point for which the time geometry instance is valid * * Gives the last time point for which a valid geometry is saved in * this time geometry. It is the upper time bound of the last step. * The time point is given in ms. */ TimePointType GetMaximumTimePoint() const override; /** * \brief Returns the first time point for which the time geometry instance is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ TimePointType GetMinimumTimePoint(TimeStepType step) const override; /** * \brief Returns the last time point for which the time geometry instance is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ TimePointType GetMaximumTimePoint(TimeStepType step) const override; /** * \brief Get the time bounds (in ms) * it returns GetMinimumTimePoint() and GetMaximumTimePoint() results as bounds. */ TimeBounds GetTimeBounds() const override; /** * \brief Get the time bounds for the given TimeStep (in ms) */ TimeBounds GetTimeBounds(TimeStepType step) const override; /** * \brief Tests if a given time point is covered by this time geometry instance * * Returns true if a geometry can be returned for the given time * point (so it is within GetTimeBounds() and fails if not. * The time point must be given in ms. */ bool IsValidTimePoint(TimePointType timePoint) const override; /** - * \brief Test for the given time step if a geometry is availible + * \brief Test for the given time step if a geometry is available * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. - * The time step is defined as positiv number. + * The time step is defined as positive number. */ bool IsValidTimeStep(TimeStepType timeStep) const override; /** * \brief Converts a time step to a time point * * Converts a time step to a time point by using the time steps lower * time bound. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ TimePointType TimeStepToTimePoint(TimeStepType timeStep) const override; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. * The associated time step is the last step which lower time bound * is smaller or equal then the time point. * If a negative invalid time point is given always time step 0 is * returned. If a positive invalid time point is given the last time * step will be returned. This is also true for time points that are * exactly on the upper time bound (the only exception is the final * time step in case that HasCollapsedFinalTimeStep() is true). */ TimeStepType TimePointToTimeStep(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns a clone of the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. */ BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const override; /** * \brief Returns the geometry which corresponds to the given time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const override; /** - * \brief Tests if all necessary informations are set and the object is valid + * \brief Tests if all necessary information are set and the object is valid */ bool IsValid() const override; /** * \brief Initializes a new object with one time steps which contains an empty geometry. */ void Initialize() override; /** * \brief Expands the time geometry to the given number of time steps. * * Initializes the new time steps with empty geometries. This default geometries will behave like * ProportionalTimeGeometry. * Shrinking is not supported. The new steps will have the same duration like the last step before extension. */ void Expand(TimeStepType size) override; /** * \brief Replaces the geometry instances with clones of the passed geometry. * * Replaces the geometries of all time steps with clones of the passed * geometry. Replacement strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ void ReplaceTimeStepGeometries(const BaseGeometry *geometry) override; /** * \brief Sets the geometry for the given time step * * If passed time step is not valid. Nothing will be changed. * @pre geometry must point to a valid instance. */ void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) override; /** * \brief Makes a deep copy of the current object */ itk::LightObject::Pointer InternalClone() const override; void ClearAllGeometries(); /** Append the passed geometry to the time geometry. * @pre The passed geometry pointer must be valid. * @pre The minimumTimePoint must not be smaller than the maximum time point of the currently last time step. * Therefore time steps must not be overlapping in time. * @pre minimumTimePoint must not be larger then maximumTimePoint.*/ void AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint); /** Same than AppendNewTimeStep. But clones geometry before adding it.*/ void AppendNewTimeStepClone(const BaseGeometry* geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint ); void ReserveSpaceForGeometries( TimeStepType numberOfGeometries ); void PrintSelf(std::ostream &os, itk::Indent indent) const override; - /** This is a helper that indicates problematic corner cases that often occure e.g. when loading + /** This is a helper that indicates problematic corner cases that often occur e.g. when loading dynamic DICOM data. There the final time step is collapsed as min time bound and max time bound have the same value. For a more detailed explanation why it happens please see: https://phabricator.mitk.org/T24766#131411 and https://phabricator.mitk.org/T27259#203524 */ bool HasCollapsedFinalTimeStep() const; protected: ~ArbitraryTimeGeometry() override; std::vector m_GeometryVector; std::vector m_MinimumTimePoints; std::vector m_MaximumTimePoints; }; // end class ArbitraryTimeGeometry } // end namespace MITK #endif // ArbitraryTimeGeometry_h diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index 040e5e8134..687c184515 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,752 +1,752 @@ /*============================================================================ 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 BaseGeometry_H_HEADER_INCLUDED #define BaseGeometry_H_HEADER_INCLUDED #include "mitkOperationActor.h" #include #include #include "itkScalableAffineTransform.h" #include "mitkNumericTypes.h" #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; //##Documentation //## @brief BaseGeometry Describes the geometry of a data object //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li an origin and spacing to define the geometry //## //## BaseGeometry and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## - //## An important sub-class is SlicedGeometry3D, which descibes + //## An important sub-class is SlicedGeometry3D, which describes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This - //## is usualy automatically called by Image. + //## is usually automatically called by Image. //## //## BaseGeometry have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation - //## is the sublass Geometry3D. + //## is the subclass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITKCORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacroItkParent(BaseGeometry, itk::Object); itkCloneMacro(Self); // ********************************** TypeDef ********************************** typedef GeometryTransformHolder::TransformType TransformType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D &origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## const mitk::Vector3D GetSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const override; friend class ModifiedLock; //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self *newGeometry) const; // ********************************** Transformations Set/Get ********************************** //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates mitk::AffineTransform3D *GetIndexToWorldTransform(); //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates const mitk::AffineTransform3D *GetIndexToWorldTransform() const; //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix); //## @brief Set the transformation used to convert from index //## to world coordinates.This function keeps the original spacing. void SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform *GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## void SetIdentity(); // ********************************** Transformations ********************************** //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. //## This method also changes m_spacing. void Compose(const TransformType *other, bool pre = false); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. void Compose(const vtkMatrix4x4 *vtkmatrix, bool pre = false); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D &vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) void ExecuteOperation(Operation *operation) override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template void WorldToIndex(const mitk::Point3D &pt_mm, itk::Index &index) const { typedef itk::Index IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim = index.GetIndexDimension(); if (dim > 3) { index.Fill(0); dim = 3; } for (i = 0; i < dim; ++i) { index[i] = itk::Math::RoundHalfIntegerUp(pt_units[i]); } } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const; //##Documentation //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template void IndexToWorld(const itk::Index &index, mitk::Point3D &pt_mm) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim = index.GetIndexDimension(); if (dim > 3) { dim = 3; } for (i = 0; i < dim; ++i) { pt_units[i] = index[i]; } IndexToWorld(pt_units, pt_mm); } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point3D) is not used. If possible, please use void IndexToWorld(const // mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &atPt3d_units, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point3D) is not used. If possible, please use void WorldToIndex(const // mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert ITK physical coordinates of a \em point (in mm, //## but without a rotation) into MITK world coordinates (in mm) //## //## For more information, see WorldToItkPhysicalPoint. template void ItkPhysicalPointToWorld(const itk::Point &itkPhysicalPoint, mitk::Point3D &pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the BaseGeometry is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template void WorldToItkPhysicalPoint(const mitk::Point3D &pt_mm, itk::Point &itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const; #endif const BoundsArrayType GetBounds() const; //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. void SetBounds(const BoundsArrayType &bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D *transform) const; //##Documentation //## @brief Set the time bounds (in ms) // void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const; //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront = true, bool yFront = true, bool zFront = true) const; //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D &p) const; //##Documentation - //## @brief Test whether the point \a p ((continous!)index coordinates in units) is + //## @brief Test whether the point \a p ((continuous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D &index) const; //##Documentation //## @brief Convenience method for working with ITK indices template bool IsIndexInside(const itk::Index &index) const { int i, dim = index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for (i = 0; i < dim; ++i) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } // ********************************* Image Geometry ******************************** //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to //change // the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and // changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool) //##Documentation - //## @brief Define that this BaseGeometry is refering to an Image + //## @brief Define that this BaseGeometry is referring to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); const GeometryTransformHolder *GetGeometryTransformHolder() const; protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry &other); ~BaseGeometry() override; itk::LightObject::Pointer InternalClone() const override = 0; void PrintSelf(std::ostream &os, itk::Indent indent) const override; static const std::string GetTransformAsString(TransformType *transformType); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; void SetVtkMatrixDeepCopy(vtkTransform *vtktransform); void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; virtual void PreSetSpacing(const mitk::Vector3D & /*aSpacing*/){}; //##Documentation //## @brief CheckBounds //## //## This function is called in SetBounds. Assertions can be implemented in this function (see PlaneGeometry.cpp). //## If you implement this function in a subclass, make sure, that all classes were your class inherits from //## have an implementation of CheckBounds //## (e.g. inheritance BaseGeometry <- A <- B. Implementation of CheckBounds in class B needs implementation in A as // well!) virtual void CheckBounds(const BoundsArrayType & /*bounds*/){}; //##Documentation //## @brief CheckIndexToWorldTransform //## //## This function is called in SetIndexToWorldTransform. Assertions can be implemented in this function (see // PlaneGeometry.cpp). //## In Subclasses of BaseGeometry, implement own conditions or call Superclass::CheckBounds(bounds);. virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D * /*transform*/){}; private: GeometryTransformHolder *m_GeometryTransform; void InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry); //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; unsigned int m_FrameOfReferenceID; // mitk::TimeBounds m_TimeBounds; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; bool m_ImageGeometry; //##Documentation //## @brief ModifiedLockFlag is used to prohibit the call of Modified() //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the ModifiedLock class! bool m_ModifiedLockFlag; //##Documentation //## @brief ModifiedcalledFlag is used to collect calls of Modified(). //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the Modified() function! mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** - * @brief Equal A function comparing two geometries for beeing identical. + * @brief Equal A function comparing two geometries for being identical. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * The parameter eps is a tolarence value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose = false); /** - * @brief Equal A function comparing two geometries for beeing identical. + * @brief Equal A function comparing two geometries for being identical. * * @ingroup MITKTestingAPI * * This is an overloaded version that uses a single tolerance for spatial and directional aspects. For more details, * see the other overloaded version. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType eps = mitk::eps, bool verbose = false); /** - * @brief Equal A function comparing two transforms (TransformType) for beeing identical. + * @brief Equal A function comparing two transforms (TransformType) for being identical. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * The parameter eps is a tolarence value for all methods which are internally used for comparison. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose); /** - * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. + * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for being identical. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * The parameter eps is a tolarence value for all methods which are internally used for comparison. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, - * orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal + * origin is on voxel grid), but the bounding box of the checked geometry is contained or equal * to the bounding box of the reference geometry.\n * By this definition equal geometries are always sub geometries of each other. * * The function checks the spacing, origin, axis vectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparisons are true. False otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose = false); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * This is a overloaded version that uses a single tolerance for spatial and directional aspects. For more details, * see the other overloaded version. * * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. * @param eps Tolarence for comparison (both spatial and directional). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType eps = mitk::eps, bool verbose = false); } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkBaseProperty.h b/Modules/Core/include/mitkBaseProperty.h index fcad73663b..5bd652197f 100644 --- a/Modules/Core/include/mitkBaseProperty.h +++ b/Modules/Core/include/mitkBaseProperty.h @@ -1,98 +1,98 @@ /*============================================================================ 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 BASEPROPERTY_H_HEADER_INCLUDED_C1F4DF54 #define BASEPROPERTY_H_HEADER_INCLUDED_C1F4DF54 #include #include #include #include namespace mitk { /*! \brief Abstract base class for properties \ingroup DataManagement Base class for properties. Properties are arbitrary additional information (to define a new type of information you have to define a subclass of BaseProperty) that can be added to a PropertyList. Concrete subclasses of BaseProperty should define Set-/Get-methods to assess the property value, which should be stored by value (not by reference). Subclasses must implement an operator==(const BaseProperty& property), which is used by PropertyList to check whether a property has been changed. */ class MITKCORE_EXPORT BaseProperty : public itk::Object { public: mitkClassMacroItkParent(BaseProperty, itk::Object); itkCloneMacro(Self); /*! @brief Subclasses must implement IsEqual(const BaseProperty&) to support comparison. operator== which is used by PropertyList to check whether a property has been changed. */ bool operator==(const BaseProperty &property) const; /*! @brief Assigns property to this BaseProperty instance. Subclasses must implement Assign(const BaseProperty&) and call the superclass Assign method for proper handling of polymorphic assignments. The assignment operator of the subclass should be disabled and the baseclass operator should be made visible using "using" statements. */ BaseProperty &operator=(const BaseProperty &property); /*! @brief Assigns property to this BaseProperty instance. This method is identical to the assignment operator, except for the return type. - It allows to directly check if the assignemnt was successfull. + It allows to directly check if the assignment was successful. */ bool AssignProperty(const BaseProperty &property); virtual std::string GetValueAsString() const; /** * @brief Default return value if a property which can not be returned as string */ static const std::string VALUE_CANNOT_BE_CONVERTED_TO_STRING; protected: BaseProperty(); BaseProperty(const BaseProperty &other); ~BaseProperty() override; private: /*! Override this method in subclasses to implement a meaningful comparison. The property argument is guaranteed to be castable to the type of the implementing subclass. */ virtual bool IsEqual(const BaseProperty &property) const = 0; /*! Override this method in subclasses to implement a meaningful assignment. The property argument is guaranteed to be castable to the type of the implementing subclass. @warning This is not yet exception aware/safe and if this method returns false, this property's state might be undefined. @return True if the argument could be assigned to this property. */ virtual bool Assign(const BaseProperty &) = 0; }; } // namespace mitk #endif /* BASEPROPERTY_H_HEADER_INCLUDED_C1F4DF54 */ diff --git a/Modules/Core/include/mitkCallbackFromGUIThread.h b/Modules/Core/include/mitkCallbackFromGUIThread.h index fe0d5c6d0b..de52351c9a 100644 --- a/Modules/Core/include/mitkCallbackFromGUIThread.h +++ b/Modules/Core/include/mitkCallbackFromGUIThread.h @@ -1,176 +1,176 @@ /*============================================================================ 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 MITK_CALLBACK_WITHIN_GUI_TREAD_H_INCLUDGEWQ #define MITK_CALLBACK_WITHIN_GUI_TREAD_H_INCLUDGEWQ #include #include #include namespace mitk { /*! \brief Used by CallbackFromGUIThread to pass parameters. */ template class CallbackEventOneParameter : public itk::AnyEvent { public: typedef CallbackEventOneParameter Self; typedef itk::AnyEvent Superclass; CallbackEventOneParameter(const T t) : m_Data(t) {} ~CallbackEventOneParameter() override {} const char *GetEventName() const override { return "CallbackEventOneParameter"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_Data); } const T GetData() const { return m_Data; } CallbackEventOneParameter(const Self &s) : itk::AnyEvent(s), m_Data(s.m_Data){}; protected: const T m_Data; private: void operator=(const Self &); }; /*! \brief Toolkit specific implementation of mitk::CallbackFromGUIThread For any toolkit, this class has to be sub-classed. One instance of that sub-class has to - be registered with mitk::CallbackFromGUIThread. See the (very simple) implmentation of + be registered with mitk::CallbackFromGUIThread. See the (very simple) implementation of QmitkCallbackFromGUIThread for an example. */ class MITKCORE_EXPORT CallbackFromGUIThreadImplementation { public: /// Change the current application cursor virtual void CallThisFromGUIThread(itk::Command *, itk::EventObject *) = 0; virtual ~CallbackFromGUIThreadImplementation(){}; protected: private: }; /*! \brief Allows threads to call some method from within the GUI thread. This class is useful for use with GUI toolkits that are not thread-safe, e.g. Qt. Any thread that needs to work with the GUI at some time during its execution (e.g. at the end, to display some results) can use this class to ask for a call to a member function from the GUI thread. Usage example We assume that you have a class ThreadedClass, that basically lives in a thread that is different from the GUI thread. Now this class has to change some element of the GUI to indicate its status. This could be dangerous (with Qt it is for sure). The solution is, that ThreadedClass asks mitk::CallbackFromGUIThread to call a method from the GUI thread (main thread). Here is part of the header of ThreadedClass: \code class ThreadedClass : public ParentClass { public: ... // All you need // This function runs in its own thread ! void ThreadedFunction(); // This should be called from the GUI thread void ChangeGUIElementsToIndicateProgress(const itk::EventObject&); ... }; \endcode \code #include "mitkCallbackFromGUIThread.h" #include // This method runs in a thread of its own! So it can't manipulate GUI elements directly without causing trouble void ThreadedClass::ThreadedFunction() { ... // Create a command object (passing parameters comes later) itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &ThreadedClass::ChangeGUIElementsToIndicateProgress); // Ask to execute that command from the GUI thread mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); ... } // Do dangerous GUI changing stuff here void ThreadedClass::ChangeGUIElementsToIndicateProgress(const itk::EventObject& e) { Application::GetButtonGrid()->AddButton("Stop"); // this is pseudo code } \endcode This obviously won't allow you to pass parameters to ChangeGUIElementsToIndicateProgress. If you need to do that, you have to create a kind of itk::EventObject that can be asked for a parameter (this solution is not nice, if you see a better solution, please mail to mitk-users@lists.sourceforge.net). The itk::EventObject has to be created with "new" (which can also be done by calling MakeObject on an existing EventObject). \code const mitk::OneParameterEvent* event = new mitk::OneParameterEvent(1); // this class is not yet defined but will be itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &ThreadedClass::ChangeGUIElementsToIndicateProgress); mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command, event); // DO NOT delete event now. This will be done by CallThisFromGUIThread after the command will executed. \endcode \todo Create a set of "normal" parameter-event-objects that people might want to use. */ class MITKCORE_EXPORT CallbackFromGUIThread { public: /// This class is a singleton. static CallbackFromGUIThread *GetInstance(); /// To be called by a toolkit specific CallbackFromGUIThreadImplementation. static void RegisterImplementation(CallbackFromGUIThreadImplementation *implementation); /// Change the current application cursor void CallThisFromGUIThread(itk::Command *, itk::EventObject *e = nullptr); protected: /// Purposely hidden - singleton CallbackFromGUIThread(); private: static CallbackFromGUIThreadImplementation *m_Implementation; static CallbackFromGUIThread *m_Instance; }; } // namespace #endif diff --git a/Modules/Core/include/mitkCommon.h b/Modules/Core/include/mitkCommon.h index 1fd400dfaf..d483493f69 100644 --- a/Modules/Core/include/mitkCommon.h +++ b/Modules/Core/include/mitkCommon.h @@ -1,196 +1,196 @@ /*============================================================================ 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 MITK_COMMON_H_DEFINED #define MITK_COMMON_H_DEFINED #ifdef _MSC_VER // This warns about truncation to 255 characters in debug/browse info #pragma warning(disable : 4786) #pragma warning(disable : 4068) /* disable unknown pragma warnings */ #endif // add only those headers here that are really necessary for all classes! #include "itkObject.h" #include "mitkConfig.h" #include "mitkExceptionMacro.h" #include "mitkGetClassHierarchy.h" #include "mitkLogMacros.h" typedef unsigned int MapperSlotId; /** From ITK 4.7 version, the TypeMacro overrides (by using the explicit attribute) the GetNameOfClass * hence the SuperClass must provide one. * * If not, use the mitkClassMacroNoParent version */ #define mitkClassMacro(className, SuperClassName) \ typedef className Self; \ typedef SuperClassName Superclass; \ typedef itk::SmartPointer Pointer; \ typedef itk::SmartPointer ConstPointer; \ static const char *GetStaticNameOfClass() { return #className; } \ virtual std::vector GetClassHierarchy() const override { return mitk::GetClassHierarchy(); } \ itkTypeMacro(className, SuperClassName); #define mitkClassMacroItkParent(className, SuperClassName) \ typedef className Self; \ typedef SuperClassName Superclass; \ typedef itk::SmartPointer Pointer; \ typedef itk::SmartPointer ConstPointer; \ static const char *GetStaticNameOfClass() { return #className; } \ virtual std::vector GetClassHierarchy() const { return mitk::GetClassHierarchy(); } \ itkTypeMacro(className, SuperClassName); /** At version 4.7 provides two type macros, the normal one expects the Superclass to provide the - * GetNameOfClass explicitely, the NoParent deos not expect anything. + * GetNameOfClass explicitly, the NoParent deos not expect anything. */ #define mitkClassMacroNoParent(className) \ typedef className Self; \ typedef itk::SmartPointer Pointer; \ typedef itk::SmartPointer ConstPointer; \ static const char *GetStaticNameOfClass() { return #className; } \ virtual std::vector GetClassHierarchy() const { return mitk::GetClassHierarchy(); } \ itkTypeMacroNoParent(className) /** * Macro for Constructors with one parameter for classes derived from itk::Lightobject **/ #define mitkNewMacro1Param(classname, type) \ \ static Pointer New(type _arg) \ \ { \ Pointer smartPtr = new classname(_arg); \ smartPtr->UnRegister(); \ return smartPtr; \ } /** * Macro for Constructors with two parameters for classes derived from itk::Lightobject **/ #define mitkNewMacro2Param(classname, typea, typeb) \ \ static Pointer New(typea _arga, typeb _argb) \ \ { \ Pointer smartPtr = new classname(_arga, _argb); \ smartPtr->UnRegister(); \ return smartPtr; \ } /** * Macro for Constructors with three parameters for classes derived from itk::Lightobject **/ #define mitkNewMacro3Param(classname, typea, typeb, typec) \ \ static Pointer New(typea _arga, typeb _argb, typec _argc) \ \ { \ Pointer smartPtr = new classname(_arga, _argb, _argc); \ smartPtr->UnRegister(); \ return smartPtr; \ } /** * Macro for Constructors with four parameters for classes derived from itk::Lightobject **/ #define mitkNewMacro4Param(classname, typea, typeb, typec, typed) \ \ static Pointer New(typea _arga, typeb _argb, typec _argc, typed _argd) \ \ { \ Pointer smartPtr = new classname(_arga, _argb, _argc, _argd); \ smartPtr->UnRegister(); \ return smartPtr; \ } /** * Macro for Constructors with five parameters for classes derived from itk::Lightobject **/ #define mitkNewMacro5Param(classname, typea, typeb, typec, typed, typee) \ \ static Pointer New(typea _arga, typeb _argb, typec _argc, typed _argd, typee _arge) \ \ { \ Pointer smartPtr = new classname(_arga, _argb, _argc, _argd, _arge); \ smartPtr->UnRegister(); \ return smartPtr; \ } /** * Macro for Constructors with six parameters for classes derived from itk::Lightobject **/ #define mitkNewMacro6Param(classname, typea, typeb, typec, typed, typee, typef) \ \ static Pointer New(typea _arga, typeb _argb, typec _argc, typed _argd, typee _arge, typef _argf) \ \ { \ Pointer smartPtr = new classname(_arga, _argb, _argc, _argd, _arge, _argf); \ smartPtr->UnRegister(); \ return smartPtr; \ } /** Get a smart const pointer to an object. Creates the member * Get"name"() (e.g., GetPoints()). */ #define mitkGetObjectMacroConst(name, type) \ virtual type *Get##name() const \ { \ itkDebugMacro("returning " #name " address " << this->m_##name); \ return this->m_##name.GetPointer(); \ } /** Creates a Clone() method for "Classname". Returns a smartPtr of a clone of the calling object*/ #define mitkCloneMacro(classname) \ virtual itk::LightObject::Pointer InternalClone() const override \ \ { \ Pointer smartPtr = new classname(*this); \ smartPtr->UnRegister(); \ return smartPtr.GetPointer(); \ } /** cross-platform deprecation macro \todo maybe there is something in external toolkits (ITK, VTK,...) that we could reulse -- would be much preferable */ #ifdef MITK_NO_DEPRECATED_WARNINGS #define DEPRECATED(func) func #elif defined(__GNUC__) #define DEPRECATED(...) __VA_ARGS__ __attribute__((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED(...) __declspec(deprecated)##__VA_ARGS__ #else #pragma message("WARNING: You need to implement DEPRECATED for your compiler!") #define DEPRECATED(func) func #endif /** * Mark templates as exported to generate public RTTI symbols which are * needed for GCC and Clang to support e.g. dynamic_cast between DSOs. */ #if defined(__clang__) || defined(__GNUC__) #define MITK_EXPORT __attribute__((visibility("default"))) #define MITK_IMPORT __attribute__((visibility("default"))) #define MITK_LOCAL __attribute__((visibility("hidden"))) #elif defined(WIN32) #define MITK_EXPORT __declspec(dllexport) #define MITK_IMPORT __declspec(dllimport) #define MITK_LOCAL #else #define MITK_EXPORT #define MITK_IMPORT #define MITK_LOCAL #endif #endif // MITK_COMMON_H_DEFINED diff --git a/Modules/Core/include/mitkCompareImageDataFilter.h b/Modules/Core/include/mitkCompareImageDataFilter.h index ba19b64e8a..4acbb3fc5e 100644 --- a/Modules/Core/include/mitkCompareImageDataFilter.h +++ b/Modules/Core/include/mitkCompareImageDataFilter.h @@ -1,108 +1,108 @@ /*============================================================================ 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 MITKCOMPAREIMAGEDATAFILTER_H #define MITKCOMPAREIMAGEDATAFILTER_H // MITK #include "mitkImage.h" #include "mitkImageToImageFilter.h" // ITK #include namespace mitk { /** * @brief A simple struct to hold the result of the comparison filter. */ struct CompareFilterResults { void PrintSelf() { if (!m_FilterCompleted) { MITK_INFO << "Comparison filter terminated due to an exception: \n " << m_ExceptionMessage; return; } MITK_INFO << "Min. difference: " << m_MinimumDifference << "\n" << "Max. difference: " << m_MaximumDifference << "\n" << "Total difference: " << m_TotalDifference << "\n" << "Mean difference: " << m_MeanDifference << "\n" << "Number of pixels with differences: " << m_PixelsWithDifference; } double m_MinimumDifference; double m_MaximumDifference; double m_TotalDifference; double m_MeanDifference; size_t m_PixelsWithDifference; bool m_FilterCompleted; std::string m_ExceptionMessage; }; /** * @brief Filter for comparing two mitk::Image objects by pixel values * * The comparison is pixel-wise, the filter uses the itk::Testing::ComparisonImageFilter * to find differences. The filter expects two images as input, provide them by using the SetInput( int, mitk::Image) * method. */ class MITKCORE_EXPORT CompareImageDataFilter : public ImageToImageFilter { public: mitkClassMacro(CompareImageDataFilter, ImageToImageFilter); itkSimpleNewMacro(Self); /** * @brief Get the result of the comparison * * The method compares only the number of pixels with differences. It returns true if the amount * is under the specified threshold. To get the complete results, use the GetCompareResults method. * - * Returns false also if the itk ComparisionImageFilter raises an exception during update. + * Returns false also if the itk ComparisonImageFilter raises an exception during update. * * @param threshold Allowed amount of pixels with differences */ bool GetResult(size_t threshold = 0); /** - * @brief Get the detailed results of the comparision run + * @brief Get the detailed results of the comparison run * * @sa CompareFilterResults */ CompareFilterResults GetCompareResults() { return m_CompareDetails; } void SetTolerance(double eps) { m_Tolerance = eps; } protected: CompareImageDataFilter(); ~CompareImageDataFilter() override {} void GenerateData() override; - /*! \brief Method resets the compare detail memeber struct to its initial state */ + /*! \brief Method resets the compare detail member struct to its initial state */ void ResetCompareResultsToInitial(); - /** ITK-like method which calls the ComparisionFilter on the two inputs of the filter */ + /** ITK-like method which calls the ComparisonFilter on the two inputs of the filter */ template void EstimateValueDifference(const itk::Image *itkImage1, const mitk::Image *referenceImage); bool m_CompareResult; CompareFilterResults m_CompareDetails; double m_Tolerance; }; } // end namespace mitk #endif // MITKCompareImageDataFilter_H diff --git a/Modules/Core/include/mitkCoreObjectFactory.h b/Modules/Core/include/mitkCoreObjectFactory.h index f1bdca80dd..6bcc7442ad 100644 --- a/Modules/Core/include/mitkCoreObjectFactory.h +++ b/Modules/Core/include/mitkCoreObjectFactory.h @@ -1,140 +1,140 @@ /*============================================================================ 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 COREOBJECTFACTORY_H_INCLUDED #define COREOBJECTFACTORY_H_INCLUDED #include #include "mitkCoreObjectFactoryBase.h" #include "mitkFileWriterWithInformation.h" #include namespace mitk { class Event; class LegacyFileReaderService; class LegacyFileWriterService; class LegacyImageWriterService; class MITKCORE_EXPORT CoreObjectFactory : public CoreObjectFactoryBase { public: mitkClassMacro(CoreObjectFactory, CoreObjectFactoryBase); itkFactorylessNewMacro(CoreObjectFactory); Mapper::Pointer CreateMapper(mitk::DataNode *node, MapperSlotId slotId) override; void SetDefaultProperties(mitk::DataNode *node) override; virtual void MapEvent(const mitk::Event *event, const int eventID); virtual void RegisterExtraFactory(CoreObjectFactoryBase *factory); virtual void UnRegisterExtraFactory(CoreObjectFactoryBase *factory); static Pointer GetInstance(); ~CoreObjectFactory() override; /** * @brief This method gets the supported (open) file extensions as string. * * This string can then used by the Qt QFileDialog widget. * * @return The c-string that contains the file extensions * @deprecatedSince{2014_10} See mitk::FileReaderRegistry and QmitkIOUtil */ DEPRECATED(virtual std::string GetFileExtensions() override); /** * @brief get the defined (open) file extension map * * @return the defined (open) file extension map * @deprecatedSince{2014_10} See mitk::FileReaderRegistry and QmitkIOUtil */ DEPRECATED(virtual MultimapType GetFileExtensionsMap() override); /** * @brief This method gets the supported (save) file extensions as string. * * This string can then used by the Qt QFileDialog widget. * * @return The c-string that contains the (save) file extensions * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(virtual std::string GetSaveFileExtensions() override); /** * @brief get the defined (save) file extension map * * @return the defined (save) file extension map * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ MultimapType GetSaveFileExtensionsMap() override; /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry */ DEPRECATED(virtual FileWriterList GetFileWriters()); /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(std::string GetDescriptionForExtension(const std::string &extension)); protected: CoreObjectFactory(); /** * @brief Merge the input map into the fileExtensionsMap. Duplicate entries are removed * * @param fileExtensionsMap the existing map, it contains value pairs like * ("*.dcm", "DICOM files"),("*.dc3", "DICOM files"). - * This map is extented/merged with the values from the input map. + * This map is extended/merged with the values from the input map. * @param inputMap the input map, it contains value pairs like ("*.dcm", * "DICOM files"),("*.dc3", "DICOM files") returned by the extra factories. * @deprecatedSince{2014_10} */ void MergeFileExtensions(MultimapType &fileExtensionsMap, MultimapType inputMap); /** * @brief initialize the file extension entries for open and save * @deprecatedSince{2014_10} */ void CreateFileExtensionsMap(); /** * @deprecatedSince{2014_10} */ DEPRECATED(void CreateSaveFileExtensions()); typedef std::set ExtraFactoriesContainer; ExtraFactoriesContainer m_ExtraFactories; FileWriterList m_FileWriters; std::string m_FileExtensions; MultimapType m_FileExtensionsMap; std::string m_SaveFileExtensions; MultimapType m_SaveFileExtensionsMap; private: void RegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory); void RegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory); void UnRegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory); void UnRegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory); std::map> m_LegacyReaders; std::map> m_LegacyWriters; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkCustomMimeType.h b/Modules/Core/include/mitkCustomMimeType.h index 5e609a81aa..1a550cff2f 100644 --- a/Modules/Core/include/mitkCustomMimeType.h +++ b/Modules/Core/include/mitkCustomMimeType.h @@ -1,131 +1,131 @@ /*============================================================================ 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 MITKCUSTOMMIMETYPE_H #define MITKCUSTOMMIMETYPE_H #include #include #include #include namespace mitk { class MimeType; /** * @ingroup IO * @ingroup MicroServices_Interfaces * * @brief The CustomMimeType class represents a custom mime-type which * may be registered as a service object. It should only be used for mime-type registration, * see also mitk::MimeType. * * Instances of this class are usually created and registered as a service. * They act as meta data information to allow the linking of files to reader and writer. * They write files to specific IFileReader instances and provide data format * meta-data for selecting compatible IFileWriter instances. - * mirk::CustomMimetype should only be used to register mime-types. All other interaction should happen trough + * mirk::CustomMimetype should only be used to register mime-types. All other interaction should happen through * mitk::MimeTypeProvider, from which registered mimetypes can be pulled. mitk::MimeType provides a safe and * memory-managed * way of interacting with Mimetypes. */ class MITKCORE_EXPORT CustomMimeType { public: CustomMimeType(); CustomMimeType(const std::string &name); CustomMimeType(const CustomMimeType &other); explicit CustomMimeType(const MimeType &other); virtual ~CustomMimeType(); CustomMimeType &operator=(const CustomMimeType &other); CustomMimeType &operator=(const MimeType &other); /** * \brief Returns the unique name for the MimeType. */ std::string GetName() const; /** * \brief Returns the human-readable Category of the mime-type. Allows grouping of similar mime-types (like Surfaces) */ std::string GetCategory() const; /** * \brief Returns all extensions that this MimeType can handle. */ std::vector GetExtensions() const; /** * \brief Returns the Human readable comment of the MimeType, a string that describes its unique role. */ std::string GetComment() const; /** * \brief Checks if the MimeType can handle file at the given location. * * In its base implementation, this function exclusively looks a the given string. * However, child classes can override this behaviour and peek into the file. */ virtual bool AppliesTo(const std::string &path) const; /** - * \brief Checks if the MimeType can handle the etension of the given path + * \brief Checks if the MimeType can handle the extension of the given path * * This function exclusively looks a the given string */ bool MatchesExtension(const std::string &path) const; /** * \brief Provides the first matching extension * * Checks whether any of its extensions are present at the end of the provided path. * Returns the first found one. */ std::string GetExtension(const std::string &path) const; /** * \brief Provides the filename minus the extension * * Checks whether any of its extensions are present at the end of the provided path. * Returns the filename without that extension and without the directory. */ std::string GetFilenameWithoutExtension(const std::string &path) const; void SetName(const std::string &name); void SetCategory(const std::string &category); void SetExtension(const std::string &extension); void AddExtension(const std::string &extension); void SetComment(const std::string &comment); void Swap(CustomMimeType &r); virtual CustomMimeType *Clone() const; private: // returns true if an extension was found bool ParsePathForExtension(const std::string &path, std::string &extension, std::string &filename) const; struct Impl; Impl *d; }; void swap(CustomMimeType &l, CustomMimeType &r); } MITK_DECLARE_SERVICE_INTERFACE(mitk::CustomMimeType, "org.mitk.CustomMimeType") #endif // MITKCUSTOMMIMETYPE_H diff --git a/Modules/Core/include/mitkDataNode.h b/Modules/Core/include/mitkDataNode.h index b90c2e03e8..64a1da0297 100644 --- a/Modules/Core/include/mitkDataNode.h +++ b/Modules/Core/include/mitkDataNode.h @@ -1,618 +1,618 @@ /*============================================================================ 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 DATATREENODE_H_HEADER_INCLUDED_C1E14338 #define DATATREENODE_H_HEADER_INCLUDED_C1E14338 #include "mitkBaseData.h" //#include "mitkMapper.h" #include "mitkDataInteractor.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include #include #include "mitkColorProperty.h" #include "mitkPropertyList.h" #include "mitkStringProperty.h" //#include "mitkMapper.h" #include "mitkGeometry3D.h" #include "mitkLevelWindow.h" #include #include class vtkLinearTransform; namespace mitk { class BaseRenderer; class Mapper; /** * \brief Definition of an itk::Event that is invoked when * a DataInteractor is set on this DataNode. */ itkEventMacroDeclaration(InteractorChangedEvent, itk::AnyEvent); /** * \brief Class for nodes of the DataTree * * Contains the data (instance of BaseData), a list of mappers, which can * draw the data, a transform (vtkTransform) and a list of properties * (PropertyList). * \ingroup DataManagement * * \todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced * to * bool GetProperty(type&) * * \warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). */ class MITKCORE_EXPORT DataNode : public itk::DataObject, public IPropertyOwner { public: typedef mitk::Geometry3D::Pointer Geometry3DPointer; typedef std::vector> MapperVector; typedef std::map MapOfPropertyLists; typedef std::vector PropertyListKeyNames; typedef std::set GroupTagList; mitkClassMacroItkParent(DataNode, itk::DataObject); itkFactorylessNewMacro(Self); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; mitk::Mapper *GetMapper(MapperSlotId id) const; /** * \brief Get the data object (instance of BaseData, e.g., an Image) * managed by this DataNode */ BaseData *GetData() const; /** * \brief Get the transformation applied prior to displaying the data as * a vtkTransform * \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead */ vtkLinearTransform *GetVtkTransform(int t = 0) const; /** * \brief Set the data object (instance of BaseData, e.g., an Image) * managed by this DataNode * * Prior set properties are kept if previous data of the node already exists and has the same * type as the new data to be set. Otherwise, the default properties are used. * In case that previous data already exists, the property list of the data node is cleared * before setting new default properties. * * \warning the actor-mode of the vtkInteractor does not work any more, if the transform of the * data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput. */ virtual void SetData(mitk::BaseData *baseData); /** * \brief Set the Interactor. */ virtual void SetDataInteractor(const DataInteractor::Pointer interactor); virtual DataInteractor::Pointer GetDataInteractor() const; mitk::DataNode &operator=(const DataNode &right); mitk::DataNode &operator=(BaseData *right); virtual void SetMapper(MapperSlotId id, mitk::Mapper *mapper); void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; void CopyInformation(const itk::DataObject *data) override; /** * \brief The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string). * * All possible values for the "renderer" parameters of * the diverse GetProperty/List() methods. */ PropertyListKeyNames GetPropertyListNames() const; /** * \brief Set the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value. * * \warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). * * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void SetProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Replace the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference. * * If \a renderer is \a nullptr the property is set in the BaseRenderer-independent * PropertyList of this DataNode. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void ReplaceProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Add the property (instance of BaseProperty) if it does * not exist (or always if\a overwrite is\a true) * with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent * PropertyList). This is set-by-value. * * For\a overwrite ==\a false the property is\em not changed * if it already exists. For\a overwrite ==\a true the method * is identical to SetProperty. * * \sa SetProperty * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void AddProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** * \brief Get the PropertyList of the \a renderer. If \a renderer is \a * nullptr, the BaseRenderer-independent PropertyList of this DataNode * is returned. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::PropertyList *GetPropertyList(const mitk::BaseRenderer *renderer = nullptr) const; mitk::PropertyList *GetPropertyList(const std::string &rendererName) const; /** * \brief Add values from another PropertyList. * * Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). * If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, * set \c replace . * * \param replace true: if \param pList contains a property "visible" of type ColorProperty and our m_PropertyList * also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects * behind the pointer. * * \sa SetProperty * \sa ReplaceProperty * \sa m_PropertyList */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); /** * \brief Get the property (instance of BaseProperty) with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * * If \a fallBackOnDataProperties is true, the data property list is queried as a last resort. * * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::BaseProperty *GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr, bool fallBackOnDataProperties = true) const; /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(itk::SmartPointer &property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property.IsNotNull(); } /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(T *&property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property != nullptr; } /** * \brief Convenience access method for GenericProperty properties * (T being the type of the second parameter) * \return \a true property was found */ template bool GetPropertyValue(const char *propertyKey, T &value, const mitk::BaseRenderer *renderer = nullptr) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey, renderer)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /// \brief Get a set of all group tags from this node's property list GroupTagList GetGroupTags() const; /** * \brief Convenience access method for bool properties (instances of * BoolProperty) * \return \a true property was found */ bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for int properties (instances of * IntProperty) * \return \a true property was found */ bool GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for float properties (instances of * FloatProperty) * \return \a true property was found */ bool GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for double properties (instances of * DoubleProperty) * * If there is no DoubleProperty for the given\c propertyKey argument, the method * looks for a corresponding FloatProperty instance. * * \return \a true property was found */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for string properties (instances of * StringProperty) * \return \a true property was found */ bool GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found */ bool GetColor(float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color") const; /** * \brief Convenience access method for level-window properties (instances of * LevelWindowProperty) * \return \a true property was found */ bool GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow") const; /** * \brief set the node as selected */ void SetSelected(bool selected, const mitk::BaseRenderer *renderer = nullptr); /** * \brief set the node as selected * \return \a true node is selected */ bool IsSelected(const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name") * \return \a true property was found */ bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "name") const { return GetStringProperty(propertyKey, nodeName, renderer); } /** * \brief Extra convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name"). * * This method does not take the renderer specific * propertylists into account, because the name of an object should never be renderer specific. * \returns a std::string with the name of the object (content of "name" Property). * If there is no "name" Property, an empty string will be returned. */ virtual std::string GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } /** Value constant that is used indicate that node names are not set so far.*/ static std::string NO_NAME_VALUE() { return "No Name!"; } /** * \brief Extra convenience access method to set the name of an object. * * If the data has already a "name" property, the name will be stored in it. Otherwise, the name will be stored in * the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const char *name) { if (name == nullptr) return; auto* data = this->GetData(); if (nullptr != data) { auto property = data->GetProperty("name"); if (property.IsNotNull()) { auto* stringProperty = dynamic_cast(property.GetPointer()); if (nullptr != stringProperty) { stringProperty->SetValue(name); return; } } } this->SetStringProperty("name", name); } /** * \brief Extra convenience access method to set the name of an object. * * \sa SetName(const char*) */ virtual void SetName(const std::string& name) { this->SetName(name.c_str()); } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty with property-key "visible") * \return \a true property was found * \sa IsVisible */ bool GetVisibility(bool &visible, const mitk::BaseRenderer *renderer, const char *propertyKey = "visible") const { return GetBoolProperty(propertyKey, visible, renderer); } /** * \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found */ bool GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey = "opacity") const; /** * \brief Convenience access method for boolean properties (instances * of BoolProperty). Return value is the value of the property. If the property is * not found, the value of \a defaultIsOn is returned. * * Thus, the return value has a different meaning than in the * GetBoolProperty method! * \sa GetBoolProperty */ bool IsOn(const char *propertyKey, const mitk::BaseRenderer *renderer, bool defaultIsOn = true) const { if (propertyKey == nullptr) return defaultIsOn; GetBoolProperty(propertyKey, defaultIsOn, renderer); return defaultIsOn; } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \sa IsOn */ bool IsVisible(const mitk::BaseRenderer *renderer, const char *propertyKey = "visible", bool defaultIsOn = true) const { return IsOn(propertyKey, renderer, defaultIsOn); } /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(float red, float green, float blue, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting visibility properties (instances * of BoolProperty) * \param visible If set to true, the data will be rendered. If false, the render will skip this data. * \param renderer Specify a renderer if the visibility shall be specific to a renderer - * \param propertyKey Can be used to specify a user defined name of the visibility propery. + * \param propertyKey Can be used to specify a user defined name of the visibility property. */ void SetVisibility(bool visible, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "visible"); /** * \brief Convenience method for setting opacity properties (instances of * FloatProperty) */ void SetOpacity(float opacity, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "opacity"); /** * \brief Convenience method for setting level-window properties * (instances of LevelWindowProperty) */ void SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow"); /** * \brief Convenience method for setting int properties (instances of * IntProperty) */ void SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting boolean properties (instances of * BoolProperty) */ void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting float properties (instances of * FloatProperty) */ void SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting double properties (instances of * DoubleProperty) */ void SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting string properties (instances of * StringProperty) */ void SetStringProperty(const char *propertyKey, const char *string, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Get the timestamp of the last change of the contents of this node or * the referenced BaseData. */ itk::ModifiedTimeType GetMTime() const override; /** * \brief Get the timestamp of the last change of the reference to the * BaseData. */ unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); } protected: DataNode(); ~DataNode() override; /// Invoked when the property list was modified. Calls Modified() of the DataNode virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event); /// \brief Mapper-slots mutable MapperVector m_Mappers; /** * \brief The data object (instance of BaseData, e.g., an Image) managed * by this DataNode */ BaseData::Pointer m_Data; /** * \brief BaseRenderer-independent PropertyList * * Properties herein can be overwritten specifically for each BaseRenderer * by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. */ PropertyList::Pointer m_PropertyList; /// \brief Map associating each BaseRenderer with its own PropertyList mutable MapOfPropertyLists m_MapOfPropertyLists; DataInteractor::Pointer m_DataInteractor; /// \brief Timestamp of the last change of m_Data itk::TimeStamp m_DataReferenceChangedTime; unsigned long m_PropertyListModifiedObserverTag; }; MITKCORE_EXPORT std::istream &operator>>(std::istream &i, DataNode::Pointer &dtn); MITKCORE_EXPORT std::ostream &operator<<(std::ostream &o, DataNode::Pointer &dtn); } // namespace mitk #endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */ diff --git a/Modules/Core/include/mitkDataNodeSource.h b/Modules/Core/include/mitkDataNodeSource.h index 5f55e1be01..0823971f56 100644 --- a/Modules/Core/include/mitkDataNodeSource.h +++ b/Modules/Core/include/mitkDataNodeSource.h @@ -1,72 +1,72 @@ /*============================================================================ 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 _MITK_DATA_TREE_NODE_SOURCE_H #define _MITK_DATA_TREE_NODE_SOURCE_H #include "itkProcessObject.h" #include "mitkDataNode.h" namespace mitk { /** * @brief Superclass of all classes generating data tree nodes (instances of class * mitk::DataNode) as output. * * In itk and vtk the generated result of a ProcessObject is only guaranteed * to be up-to-date, when Update() of the ProcessObject or the generated * DataObject is called immediately before access of the data stored in the * DataObject. This is also true for subclasses of mitk::BaseProcess and thus * for mitk::DataNodeSource. * @ingroup Process */ class MITKCORE_EXPORT DataNodeSource : public itk::ProcessObject { public: mitkClassMacroItkParent(DataNodeSource, itk::ProcessObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef mitk::DataNode OutputType; typedef OutputType::Pointer OutputTypePointer; /** * Allocates a new output object and returns it. Currently the * index idx is not evaluated. * @param idx the index of the output for which an object should be created * @returns the new object */ DataObjectPointer MakeOutput(DataObjectPointerArraySizeType idx) override; /** * This is a default implementation to make sure we have something. - * Once all the subclasses of ProcessObject provide an appopriate + * Once all the subclasses of ProcessObject provide an appropriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ DataObjectPointer MakeOutput(const DataObjectIdentifierType &name) override; OutputType *GetOutput(); const OutputType *GetOutput() const; OutputType *GetOutput(DataObjectPointerArraySizeType idx); const OutputType *GetOutput(DataObjectPointerArraySizeType idx) const; protected: DataNodeSource(); ~DataNodeSource() override; }; } #endif // #define _MITK_BASE_DATA_SOURCE_H diff --git a/Modules/Core/include/mitkDataStorage.h b/Modules/Core/include/mitkDataStorage.h index b884a7b12d..a0d1c42fc9 100644 --- a/Modules/Core/include/mitkDataStorage.h +++ b/Modules/Core/include/mitkDataStorage.h @@ -1,442 +1,442 @@ /*============================================================================ 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 MITKDATASTORAGE_H #define MITKDATASTORAGE_H #include "itkObject.h" #include "itkVectorContainer.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkMessage.h" #include #include #include namespace mitk { class NodePredicateBase; class DataNode; class BaseRenderer; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The DataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects, that this object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## If a new node is added to the DataStorage, AddNodeEvent is emitted. //## If a node is removed, RemoveNodeEvent is emitted. //## //## //## \ingroup DataStorage class MITKCORE_EXPORT DataStorage : public itk::Object { public: mitkClassMacroItkParent(DataStorage, itk::Object); //##Documentation //## @brief A Container of objects that is used as a result set of GetSubset() query operations (Set of //SmartPointers // to DataNodes). typedef itk::VectorContainer SetOfObjects; //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the DataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. virtual void Add(DataNode *node, const DataStorage::SetOfObjects *parents = nullptr) = 0; //##Documentation //## @brief Convenience method to add a node that has one parent //## void Add(DataNode *node, DataNode *parent); //##Documentation //## @brief Removes node from the DataStorage //## virtual void Remove(const DataNode *node) = 0; //##Documentation //## @brief Checks if a node exists in the DataStorage //## virtual bool Exists(const DataNode *node) const = 0; //##Documentation //## @brief Removes a set of nodes from the DataStorage //## void Remove(const DataStorage::SetOfObjects *nodes); //##Documentation //## @brief returns a set of data objects that meet the given condition(s) //## //## GetSubset returns a set of objects with a specific data type that meet the condition(s) //## specified in the condition parameter. Conditions can be //## - data type of the data object //## - is source object of specific object (e.g. all source objects of node x) //## - has property with specific value (e.g. OrganType is Liver) //## - negation of any condition //## - conjunction of a set of conditions //## - disjunction of a set of conditions //## Conditions are implemented as predicates using the Composite Design Pattern //## (see definition of NodePredicateBase for details). //## The method returns a set of SmartPointers to the DataNodes that fulfill the //## conditions. A set of all objects can be retrieved with the GetAll() method; SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const; //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## virtual SetOfObjects::ConstPointer GetSources(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const = 0; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. virtual SetOfObjects::ConstPointer GetDerivations(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const = 0; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## virtual SetOfObjects::ConstPointer GetAll() const = 0; //##Documentation //## @brief Convenience method to get the first node that matches the predicate condition //## DataNode *GetNode(const NodePredicateBase *condition = nullptr) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const char *name) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); } //##Documentation //## @brief Convenience method to get the first node with a given name that is derived from sourceNode //## DataNode *GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const; //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const char *name) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedNode(name); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const std::string name) const { return this->GetNamedObject(name.c_str()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name that is derived // from a specific node //## template DataType *GetNamedDerivedObject(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Returns a list of used grouptags //## const DataNode::GroupTagList GetGroupTags() const; /*ITK Mutex */ mutable std::mutex m_MutexOne; /* Public Events */ typedef Message1 DataStorageEvent; //##Documentation //## @brief AddEvent is emitted whenever a new node has been added to the DataStorage. //## //## Observers should register to this event by calling myDataStorage->AddNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->AddNodeEvent.RemoveListener(myObject, //MyObject::MyMethod). //## Note: AddEvents are _not_ emitted if a node is added to DataStorage by adding it to the the underlying //DataTree! // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent AddNodeEvent; //##Documentation //## @brief RemoveEvent is emitted directly before a node is removed from the DataStorage. //## //## Observers should register to this event by calling myDataStorage->RemoveNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->RemoveNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Note: RemoveEvents are also emitted if a node was removed from the DataStorage by deleting it from the //underlying // DataTree // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent RemoveNodeEvent; //##Documentation //## @brief ChangedEvent is emitted directly after a node was changed. //## //## Observers should register to this event by calling myDataStorage->ChangedNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been changed. //## Observers should unregister by calling myDataStorage->ChangedNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::ModifiedEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent ChangedNodeEvent; //##Documentation //## @brief DeleteNodeEvent is emitted directly before a node is deleted. //## //## Observers should register to this event by calling myDataStorage->DeleteNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called when a node is deleted. //## Observers should unregister by calling myDataStorage->DeleteNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::DeleteEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent DeleteNodeEvent; DataStorageEvent InteractorChangedNodeEvent; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the input objects //## //## Throws std::invalid_argument exception if input is nullptr //## @param input set of objects of the DataStorage to be included in the bounding geometry //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the data tree //## (bounding box, minimal spacing of the considered nodes, live-span) //## //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of all visible parts of the //## data tree bounding box, minimal spacing of the considered nodes, live-span) //## //## Simply calls ComputeBoundingGeometry3D(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr); //##Documentation //## @brief Compute the bounding box of data tree structure //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr); //##Documentation //## \brief Compute the bounding box of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## Simply calls ComputeBoundingBox(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. BoundingBox::Pointer ComputeVisibleBoundingBox(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr) { return ComputeBoundingBox("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Compute the time-bounds of the contents of a data tree structure //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeBounds ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2); //##Documentation //## @brief Compute the time-bounds of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## Simply calls ComputeTimeBounds(it, "visible", renderer, boolPropertyKey). //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey TimeBounds ComputeTimeBounds(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeTimeBounds("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Defines whether or not NodeChangedEvent is invoked . //## //## This method can be used to set m_BlockNodeModifiedEvents. //## //## If this flag is true, NodeChangedEvent is not invoked when a //## DataNode is modified. This might be undesired when setting //## many properties on a datanode and you do not want anyone to //## react. void BlockNodeModifiedEvents(bool block); protected: //##Documentation //## @brief EmitAddNodeEvent emits the AddNodeEvent //## //## This method should be called by subclasses to emit the AddNodeEvent void EmitAddNodeEvent(const DataNode *node); //##Documentation //## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent //## //## This method should be called by subclasses to emit the RemoveNodeEvent void EmitRemoveNodeEvent(const DataNode *node); void OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief OnNodeModified listens to modified events of DataNodes. //## //## The node is hidden behind the caller parameter, which has to be casted first. //## If the cast succeeds the ChangedNodeEvent is emitted with this node. void OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief Adds a Modified-Listener to the given Node. void AddListeners(const DataNode *_Node); //##Documentation //## @brief Removes a Modified-Listener from the given Node. void RemoveListeners(const DataNode *_Node); //##Documentation //## @brief Saves Modified-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeModifiedObserverTags; std::map m_NodeInteractorChangedObserverTags; //##Documentation //## @brief Saves Delete-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeDeleteObserverTags; //##Documentation //## @brief If this class changes nodes itself, set this to TRUE in order //## to suppress NodeChangedEvent to be emitted. bool m_BlockNodeModifiedEvents; DataStorage(); ~DataStorage() override; //##Documentation //## @brief Filters a SetOfObjects by the condition. If no condition is provided, the original set is returned SetOfObjects::ConstPointer FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const; //##Documentation //## @brief Prints the contents of the DataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; //##Documentation //## @brief returns the topmost visible node of a given list of nodes. //## The function returns a node that is visible and has the highest layer of a set of given nodes. - //## The property list, which is used to find the visibility- and layer-property is is specified by the + //## The property list, which is used to find the visibility- and layer-property is specified by the //## given base renderer. //## MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer* baseRender); } // namespace mitk #endif // MITKDATASTORAGE_H diff --git a/Modules/Core/include/mitkDisplayCoordinateOperation.h b/Modules/Core/include/mitkDisplayCoordinateOperation.h index b37a864f4a..7e97e38f99 100644 --- a/Modules/Core/include/mitkDisplayCoordinateOperation.h +++ b/Modules/Core/include/mitkDisplayCoordinateOperation.h @@ -1,71 +1,71 @@ /*============================================================================ 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 MITKDISPLAYCOORDINATEOPERATION_H_HEADER_INCLUDED_C10E33D0 #define MITKDISPLAYCOORDINATEOPERATION_H_HEADER_INCLUDED_C10E33D0 #include "mitkBaseRenderer.h" #include "mitkNumericTypes.h" #include "mitkOperation.h" #include #include #define mitkGetMacro(name, type) \ virtual type Get##name() { return this->m_##name; } namespace mitk { // TODO Legacy , no longer necessary when after migrating all DisplayInteractions to new Interactions. // Coordinate supplier can probably also be removed then. //##Documentation - //## @brief Operation with informations necessary for operations of DisplayVectorInteractor + //## @brief Operation with information necessary for operations of DisplayVectorInteractor //## @ingroup Undo class MITKCORE_EXPORT DisplayCoordinateOperation : public Operation { public: DisplayCoordinateOperation(mitk::OperationType operationType, mitk::BaseRenderer *renderer, const mitk::Point2D &startDisplayCoordinate, const mitk::Point2D &lastDisplayCoordinate, const mitk::Point2D ¤tDisplayCoordinate); DisplayCoordinateOperation(mitk::OperationType operationType, mitk::BaseRenderer *renderer, const mitk::Point2D &startDisplayCoordinate, const mitk::Point2D &lastDisplayCoordinate, const mitk::Point2D ¤tDisplayCoordinate, const mitk::Point2D &startCoordinateInMM); ~DisplayCoordinateOperation() override; mitk::BaseRenderer *GetRenderer(); mitkGetMacro(StartDisplayCoordinate, mitk::Point2D); mitkGetMacro(LastDisplayCoordinate, mitk::Point2D); mitkGetMacro(CurrentDisplayCoordinate, mitk::Point2D); mitkGetMacro(StartCoordinateInMM, mitk::Point2D); mitk::Vector2D GetLastToCurrentDisplayVector(); mitk::Vector2D GetStartToCurrentDisplayVector(); mitk::Vector2D GetStartToLastDisplayVector(); private: mitk::WeakPointer m_Renderer; const mitk::Point2D m_StartDisplayCoordinate; const mitk::Point2D m_LastDisplayCoordinate; const mitk::Point2D m_CurrentDisplayCoordinate; const mitk::Point2D m_StartCoordinateInMM; }; } #endif /* MITKDISPLAYCOORDINATEOPERATION_H_HEADER_INCLUDED_C10E33D0 */ diff --git a/Modules/Core/include/mitkEventConfig.h b/Modules/Core/include/mitkEventConfig.h index afcbe5524c..1c4c35d8c2 100755 --- a/Modules/Core/include/mitkEventConfig.h +++ b/Modules/Core/include/mitkEventConfig.h @@ -1,189 +1,189 @@ /*============================================================================ 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 mitkStateMachineConfig_h #define mitkStateMachineConfig_h #include #include "mitkPropertyList.h" #include "usSharedData.h" #include "itkSmartPointer.h" namespace us { class Module; } namespace mitk { class InteractionEvent; struct EventConfigPrivate; /** * \class EventConfig * \brief Configuration Object for Statemachines. * * Reads given config file, which translates specific user inputs (InteractionEvents) into EventVariants that can be *processed * by the StateMachine. * Refer to \ref ConfigFileDescriptionSection . * * @ingroup Interaction **/ class MITKCORE_EXPORT EventConfig { public: typedef itk::SmartPointer EventType; /** * @brief Constructs an invalid EventConfig object. * * Call LoadConfig to create a valid configuration object. */ EventConfig(); EventConfig(const EventConfig &other); /** * @brief Construct an EventConfig object based on a XML configuration file. * * Uses the specified resource file containing an XML event configuration to * construct an EventConfig object. If the resource is invalid, the created * EventConfig object will also be invalid. * * @param filename The resource name relative to the Interactions resource folder. * @param module */ EventConfig(const std::string &filename, const us::Module *module = nullptr); /** * @brief Construct an EventConfig object based on a XML configuration file. * - * Uses the specified istream refering to a file containing an XML event configuration to + * Uses the specified istream referring to a file containing an XML event configuration to * construct an EventConfig object. If the resource is invalid, the created * EventConfig object will also be invalid. * * @param inputStream std::ifstream to XML configuration file */ EventConfig(std::istream &inputStream); /** * @brief Construct an EventConfig object based on a vector of mitk::PropertyLists * * Constructs the EventObject based on a description provided by vector of property values, where each mitk::PropertyList describes * one Event. * Example \code #include "mitkEventConfig.h" #include "mitkInteractionEventConst.h" #include "mitkPropertyList.h" // First event mitk::PropertyList::Pointer propertyList1 = mitk::PropertyList::New(); // Setting the EventClass property to 'MousePressEvent' propertyList1->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass.c_str(), "MousePressEvent"); // Setting the Event variant value to 'MousePressEventVariantÄ propertyList1->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant.c_str(), "MousePressEventVariant"); // set control and alt buttons as modifiers propertyList1->SetStringProperty("Modifiers","CTRL,ALT"); // Second event mitk::PropertyList::Pointer propertyList2 = mitk::PropertyList::New(); propertyList2->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass.c_str(), "MouseReleaseEvent"); propertyList2->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant.c_str(), "MouseReleaseEventVariant"); propertyList2->SetStringProperty("Modifiers","SHIFT"); // putting both descriptions in a vector std::vector* configDescription = new std::vector(); configDescription->push_back(propertyList1); configDescription->push_back(propertyList2); // create the config object mitk::EventConfig newConfig(configDescription); \endcode */ EventConfig(const std::vector &configDescription); EventConfig &operator=(const EventConfig &other); ~EventConfig(); /** - * @brief Checks wether this EventConfig object is valid. + * @brief Checks whether this EventConfig object is valid. * @return Returns \c true if a configuration was successfully loaded, \c false otherwise. */ bool IsValid() const; /** * @brief This method \e extends this configuration. * * The configuration from the resource provided is loaded and only the ones conflicting are replaced by the new one. * This way several configuration files can be combined. * * @see AddConfig(const EventConfig&) * @see InteractionEventHandler::AddEventConfig(const std::string&, const Module*) * * @param filename The resource name relative to the Interactions resource folder. * @param module The module containing the resource. Defaults to the Mitk module. * @return \c true if the configuration was successfully added, \c false otherwise. */ bool AddConfig(const std::string &filename, const us::Module *module = nullptr); /** * @brief This method \e extends this configuration. * The configuration from the EventConfig object is loaded and only the ones conflicting are replaced by the new * one. * This way several configurations can be combined. * * @see AddConfig(const std::string&, const Module*) * @see InteractionEventHandler::AddEventConfig(const EventConfig&) * * @param config The EventConfig object whose configuration should be added. * @return \c true if the configuration was successfully added, \c false otherwise. */ bool AddConfig(const EventConfig &config); /** * @brief Reset this EventConfig object, rendering it invalid. */ void ClearConfig(); /** * Returns a PropertyList that contains the properties set in the configuration file. * All properties are stored as strings. */ PropertyList::Pointer GetAttributes() const; /** * Checks if the config object has a definition for the given event. If it has, the corresponding variant name is * returned, else * an empty string is returned. * \note mitk::InternalEvent is handled differently. Their signal name is returned as event variant. So there is no * need * to configure them in a config file. * \note mitk::InteractionKeyEvent may have a defined event variant, if this is the case, this function returns it. * If * no * such definition is found key events are mapped to Std + Key , so an 'A' will be return as 'StdA' . */ std::string GetMappedEvent(const EventType &interactionEvent) const; private: us::SharedDataPointer d; }; } // namespace mitk #endif /* mitkStateMachineConfig_h */ diff --git a/Modules/Core/include/mitkEventRecorder.h b/Modules/Core/include/mitkEventRecorder.h index a99f54fb56..4cc12578a3 100644 --- a/Modules/Core/include/mitkEventRecorder.h +++ b/Modules/Core/include/mitkEventRecorder.h @@ -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. ============================================================================*/ #ifndef mitkEventRecorder_h #define mitkEventRecorder_h #include "iostream" #include "mitkInteractionEventObserver.h" #include namespace mitk { /** *\class EventRecorder *@brief Observer that enables recoding of all user interaction with the render windows and storing it in an XML *file. * * @ingroup Interaction * * XML file will look like: * * \code{.unparsed} * * * * * * * * * * * * * * * * \endcode **/ class MITKCORE_EXPORT EventRecorder : public InteractionEventObserver { public: EventRecorder(); ~EventRecorder() override; /** * By this function the Observer gets notified about new events. */ void Notify(InteractionEvent *interactionEvent, bool) override; /** * @brief SetEventIgnoreList Optional. Provide a list of strings that describe which events are to be ignored */ void SetEventIgnoreList(std::vector list); void StartRecording(); void StopRecording(); bool IsActive() { return m_Active; } void SetOutputFile(std::string filename) { m_FileName = filename; } private: /** * @brief m_IgnoreList lists the names of events that are dropped */ std::vector m_IgnoreList; /** - * @brief m_Active determindes if events are caught and written to file + * @brief m_Active determines if events are caught and written to file */ bool m_Active; std::string m_FileName; std::ofstream m_FileStream; }; } #endif diff --git a/Modules/Core/include/mitkException.h b/Modules/Core/include/mitkException.h index c632970caf..f0d84497b8 100644 --- a/Modules/Core/include/mitkException.h +++ b/Modules/Core/include/mitkException.h @@ -1,115 +1,115 @@ /*============================================================================ 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 MITKEXCEPTION_H_INCLUDED #define MITKEXCEPTION_H_INCLUDED #include #include #include namespace mitk { /**Documentation * \brief An object of this class represents an exception of MITK. * Please don't instantiate exceptions manually, but use the * exception macros (file mitkExceptionMacro.h) instead. * Simple use in your code is: * * mitkThrow() << "optional exception message"; * * You can also define specialized exceptions which must inherit * from this class. Please always use the mitkExceptionClassMacro * when implementing specialized exceptions. A simple implementation * can look like: * * class MyException : public mitk::Exception * { * public: * mitkExceptionClassMacro(MyException,mitk::Exception); * }; * * You can then throw your specialized exceptions by using the macro * * mitkThrowException(MyException) << "optional exception message"; */ class MITKCORE_EXPORT Exception : public itk::ExceptionObject { public: Exception(const char *file, unsigned int lineNumber = 0, const char *desc = "None", const char *loc = "Unknown") : itk::ExceptionObject(file, lineNumber, desc, loc) { } ~Exception() throw() override {} itkTypeMacro(ClassName, SuperClassName); /** \brief Adds rethrow data to this exception. */ void AddRethrowData(const char *file, unsigned int lineNumber, const char *message); /** \return Returns how often the exception was rethrown. */ int GetNumberOfRethrows(); /** @return Returns the rethrow data of the specified rethrow number. Returns empty data, if the rethrowNumber * doesn't * exist. * @param rethrowNumber The internal number of the rethrow. - * @param file (returnvalue) This varaiable will be filled with the file of the specified rethrow. - * @param line (returnvalue) This varaiable will be filled with the line of the specified rethrow. - * @param message (returnvalue) This varaiable will be filled with the message of the specified rethrow. + * @param file (returnvalue) This variable will be filled with the file of the specified rethrow. + * @param line (returnvalue) This variable will be filled with the line of the specified rethrow. + * @param message (returnvalue) This variable will be filled with the message of the specified rethrow. */ void GetRethrowData(int rethrowNumber, std::string &file, int &line, std::string &message); /** \brief Definition of the bit shift operator for this class.*/ template inline Exception &operator<<(const T &data) { std::stringstream ss; ss << this->GetDescription() << data; this->SetDescription(ss.str()); return *this; } /** \brief Definition of the bit shift operator for this class (for non const data).*/ template inline Exception &operator<<(T &data) { std::stringstream ss; ss << this->GetDescription() << data; this->SetDescription(ss.str()); return *this; } /** \brief Definition of the bit shift operator for this class (for functions).*/ inline Exception &operator<<(std::ostream &(*func)(std::ostream &)) { std::stringstream ss; ss << this->GetDescription() << func; this->SetDescription(ss.str()); return *this; } protected: struct ReThrowData { std::string RethrowClassname; unsigned int RethrowLine; std::string RethrowMessage; }; std::vector m_RethrowData; }; MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const mitk::Exception &e); } // namespace mitk #endif diff --git a/Modules/Core/include/mitkExtractSliceFilter.h b/Modules/Core/include/mitkExtractSliceFilter.h index e4b1ae2e3d..c7e2182e3e 100644 --- a/Modules/Core/include/mitkExtractSliceFilter.h +++ b/Modules/Core/include/mitkExtractSliceFilter.h @@ -1,208 +1,208 @@ /*============================================================================ 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 mitkExtractSliceFilter_h_Included #define mitkExtractSliceFilter_h_Included #include "MitkCoreExports.h" #include "mitkImageToImageFilter.h" #include #include #include #include #include #include #include namespace mitk { /** - \brief ExtractSliceFilter extracts a 2D abitrary oriented slice from a 3D volume. + \brief ExtractSliceFilter extracts a 2D arbitrary oriented slice from a 3D volume. The filter can reslice in all orthogonal planes such as sagittal, coronal and axial, and is also able to reslice an arbitrary oriented oblique plane. Curved planes are specified via an AbstractTransformGeometry as the input worldgeometry. Additionally the filter extracts the specified component of a multi-component input image. This is done only if the caller requests an mitk::Image output ('m_VtkOutputRequested' set to false). The default component to be extracted is '0'. The convenient workflow is: 1. Set an image as input. 2. Set the worldPlaneGeometry. This defines a grid where the slice is being extracted 3. And then start the pipeline. There are a few more properties that can be set to modify the behavior of the slicing. The properties are: - interpolation mode either Nearestneighbor, Linear or Cubic. - a transform this is a convenient way to adapt the reslice axis for the case that the image is transformed e.g. rotated. - time step the time step in a times volume. - the component to extract from a multi-component input image - vtkoutputrequested, to define whether an mitk::image should be initialized - resample by geometry whether the resampling grid corresponds to the specs of the worldgeometry or is directly derived from the input image By default the properties are set to: - interpolation mode Nearestneighbor. - a transform nullptr (No transform is set). - time step 0. - component 0. - resample by geometry false (Corresponds to input image). */ class MITKCORE_EXPORT ExtractSliceFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractSliceFilter, ImageToImageFilter); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(Self, vtkImageReslice *); /** \brief Set the axis where to reslice at.*/ void SetWorldGeometry(const PlaneGeometry *geometry) { if (geometry != m_WorldGeometry) { this->m_WorldGeometry = geometry; this->Modified(); } } /** \brief Set the time step in the 4D volume */ void SetTimeStep(unsigned int timestep) { m_TimeStep = timestep; } unsigned int GetTimeStep() { return m_TimeStep; } /** \brief Set the component of an image to be extracted */ void SetComponent(unsigned int component) { m_Component = component; } /** \brief Set a transform for the reslice axes. * This transform is needed if the image volume itself is transformed. (Effects the reslice axis) */ void SetResliceTransformByGeometry(const BaseGeometry *transform) { this->m_ResliceTransform = transform; } /** \brief Resampling grid corresponds to: false->image true->worldgeometry*/ void SetInPlaneResampleExtentByGeometry(bool inPlaneResampleExtentByGeometry) { this->m_InPlaneResampleExtentByGeometry = inPlaneResampleExtentByGeometry; } /** \brief Sets the output dimension of the slice*/ void SetOutputDimensionality(unsigned int dimension) { this->m_OutputDimension = dimension; } /** \brief Set the spacing in z direction manually. * Required if the outputDimension is > 2. */ void SetOutputSpacingZDirection(double zSpacing) { this->m_ZSpacing = zSpacing; } - /** \brief Set the extent in pixel for direction z manualy. + /** \brief Set the extent in pixel for direction z manually. Required if the output dimension is > 2. */ void SetOutputExtentZDirection(int zMin, int zMax) { this->m_ZMin = zMin; this->m_ZMax = zMax; } /** \brief Get the bounding box of the slice [xMin, xMax, yMin, yMax, zMin, zMax] * The method uses the input of the filter to calculate the bounds. * It is recommended to use * GetClippedPlaneBounds(const BaseGeometry*, const PlaneGeometry*, double*) * if you are not sure about the input. */ bool GetClippedPlaneBounds(double bounds[6]); /** \brief Get the bounding box of the slice [xMin, xMax, yMin, yMax, zMin, zMax]*/ bool GetClippedPlaneBounds(const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds); /** \brief Get the spacing of the slice. returns mitk::ScalarType[2] */ mitk::ScalarType *GetOutputSpacing(); /** \brief Get Output as vtkImageData. * Note: * SetVtkOutputRequest(true) has to be called at least once before * GetVtkOutput(). Otherwise the output is empty for the first update step. */ vtkImageData *GetVtkOutput() { m_VtkOutputRequested = true; return m_Reslicer->GetOutput(); } - /** Set VtkOutPutRequest to suppress the convertion of the image. + /** Set VtkOutPutRequest to suppress the conversion of the image. * It is suggested to use this with GetVtkOutput(). * Note: * SetVtkOutputRequest(true) has to be called at least once before * GetVtkOutput(). Otherwise the output is empty for the first update step. */ void SetVtkOutputRequest(bool isRequested) { m_VtkOutputRequested = isRequested; } /** \brief Get the reslices axis matrix. * Note: the axis are recalculated when calling SetResliceTransformByGeometry. */ vtkMatrix4x4 *GetResliceAxes() { return this->m_Reslicer->GetResliceAxes(); } void SetBackgroundLevel(double backgroundLevel) { m_BackgroundLevel = backgroundLevel; } enum ResliceInterpolation { RESLICE_NEAREST = 0, RESLICE_LINEAR = 1, RESLICE_CUBIC = 3 }; void SetInterpolationMode(ExtractSliceFilter::ResliceInterpolation interpolation) { this->m_InterpolationMode = interpolation; } protected: ExtractSliceFilter(vtkImageReslice *reslicer = nullptr); ~ExtractSliceFilter() override; void GenerateData() override; void GenerateOutputInformation() override; void GenerateInputRequestedRegion() override; PlaneGeometry::ConstPointer m_WorldGeometry; vtkSmartPointer m_Reslicer; unsigned int m_TimeStep; unsigned int m_OutputDimension; double m_ZSpacing; int m_ZMin; int m_ZMax; ResliceInterpolation m_InterpolationMode; bool m_InPlaneResampleExtentByGeometry; // Resampling grid corresponds to: false->image true->worldgeometry mitk::ScalarType *m_OutPutSpacing; bool m_VtkOutputRequested; double m_BackgroundLevel; unsigned int m_Component; private: BaseGeometry::ConstPointer m_ResliceTransform; /* Axis vectors of the relevant geometry. Set in GenerateOutputInformation() and also used in GenerateData().*/ Vector3D m_Right, m_Bottom; /* Bounds of the relevant plane. Set in GenerateOutputInformation() and also used in GenerateData().*/ int m_XMin, m_XMax, m_YMin, m_YMax; }; } #endif // mitkExtractSliceFilter_h_Included diff --git a/Modules/Core/include/mitkGenericIDRelationRule.h b/Modules/Core/include/mitkGenericIDRelationRule.h index f6e2c8fd96..de22d3649d 100644 --- a/Modules/Core/include/mitkGenericIDRelationRule.h +++ b/Modules/Core/include/mitkGenericIDRelationRule.h @@ -1,98 +1,98 @@ /*============================================================================ 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 mitkGenericIDRelationRule_h #define mitkGenericIDRelationRule_h #include "mitkPropertyRelationRuleBase.h" namespace mitk { /**This rule class can be used for relations that are only defined on the ID-layer and where no connection on the Data-layer can be defined or deduced. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In order to be able to use this class for different relation types based on ID, the ruleIDTag is used. It must be specified when creating a rule instance. The ruleIDTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. - One may also specify the display name and the role names of the instance. If not speficied the default values + One may also specify the display name and the role names of the instance. If not specified, the default values are used (display name: " relation", source role name: "source of relation", destination role name: "destination of relation") */ class MITKCORE_EXPORT GenericIDRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(GenericIDRelationRule, PropertyRelationRuleBase); itkCloneMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; /** Pass through to base implementation of PropertyRelationRuleBase. See PropertyRelationRuleBase::connect documentation for more information. */ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; protected: GenericIDRelationRule(const RuleIDType &ruleIDTag); GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName); GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~GenericIDRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; private: RuleIDType m_RuleIDTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkGeometryDataReaderService.h b/Modules/Core/include/mitkGeometryDataReaderService.h index aa95947ed4..a06f33e20b 100644 --- a/Modules/Core/include/mitkGeometryDataReaderService.h +++ b/Modules/Core/include/mitkGeometryDataReaderService.h @@ -1,59 +1,59 @@ /*============================================================================ 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 mitkGeometryDataReaderService_h #define mitkGeometryDataReaderService_h // MITK #include #include namespace mitk { /** * @internal * * @brief reads XML representations of mitk::GeometryData from a file/stream. * * To be used via IOUtil. * - * Reader for XML files containing one or multiple XML represenations of + * Reader for XML files containing one or multiple XML representations of * mitk::GeometryData. If multiple mitk::GeometryData objects are stored in one file, * these are assigned to multiple BaseData objects. * * @sa Geometry3DToXML * * @ingroup IO */ class GeometryDataReaderService : public AbstractFileReader { public: GeometryDataReaderService(); ~GeometryDataReaderService() override; using AbstractFileReader::Read; /** * @brief Provides the MIME type for reader and writer. */ static CustomMimeType GEOMETRY_DATA_MIMETYPE(); protected: std::vector> DoRead() override; private: GeometryDataReaderService(const GeometryDataReaderService &other); GeometryDataReaderService *Clone() const override; }; } #endif diff --git a/Modules/Core/include/mitkGradientBackground.h b/Modules/Core/include/mitkGradientBackground.h index 6ec83c793b..a98a7c0816 100644 --- a/Modules/Core/include/mitkGradientBackground.h +++ b/Modules/Core/include/mitkGradientBackground.h @@ -1,103 +1,103 @@ /*============================================================================ 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 mitkGradientBackground_h #define mitkGradientBackground_h #include #include #include #include class vtkRenderer; class vtkRenderWindow; namespace mitk { class RenderWindow; /** * Displays a color gradient in the background * of a vtkRenderWindow. - * The gradient ist faked by displaying a non-interactable + * The gradient is faked by displaying a non-interactable * smoothly shaded plane in a separate layer behind the * scene. After setting the renderwindow, the gradient may be * activated by calling Enable() * @deprecatedSince{2015_05} not used in renderwindows */ class MITKCORE_EXPORT GradientBackground : public itk::Object { public: mitkClassMacroItkParent(GradientBackground, itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Sets the renderwindow, in which the gradient background * will be shown. Make sure, you have called this function * before calling Enable() */ virtual void SetRenderWindow(vtkSmartPointer renderWindow); /** * Returns the vtkRenderWindow, which is used * for displaying the gradient background */ virtual vtkSmartPointer GetRenderWindow(); /** * Returns the renderer responsible for * rendering the color gradient into the * vtkRenderWindow */ virtual vtkSmartPointer GetVtkRenderer(); /** * Sets the gradient colors. The gradient * will smoothly fade from color1 to color2 */ virtual void SetGradientColors(double r1, double g1, double b1, double r2, double g2, double b2); virtual void SetGradientColors(Color upper, Color lower); virtual void SetUpperColor(double r, double g, double b); virtual void SetLowerColor(double r, double g, double b); virtual void SetUpperColor(Color upper); virtual void SetLowerColor(Color lower); /** * Enables drawing of the color gradient background. * If you want to disable it, call the Disable() function. */ virtual void Enable(); /** * Disables drawing of the color gradient background. * If you want to enable it, call the Enable() function. */ virtual void Disable(); /** * Checks, if the gradient background is currently * enabled (visible) */ virtual bool IsEnabled(); protected: GradientBackground(); ~GradientBackground() override; vtkSmartPointer m_RenderWindow; vtkSmartPointer m_Renderer; }; } // end of namespace mitk #endif // mitkGradientBackground_h diff --git a/Modules/Core/include/mitkIFileReader.h b/Modules/Core/include/mitkIFileReader.h index 615cb27f80..04b7062762 100644 --- a/Modules/Core/include/mitkIFileReader.h +++ b/Modules/Core/include/mitkIFileReader.h @@ -1,156 +1,156 @@ /*============================================================================ 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 IFileReader_H_HEADER_INCLUDED_C1E7E521 #define IFileReader_H_HEADER_INCLUDED_C1E7E521 #include #include #include namespace mitk { class BaseData; class PropertyList; } namespace itk { template class SmartPointer; } namespace mitk { /** * \ingroup IO * \ingroup MicroServices_Interfaces * * \brief The common interface for all MITK file readers. * * Implementations of this interface must be registered as a service - * to make themselve available via the service registry. If the + * to make themselves available via the service registry. If the * implementation is state-full, the service should be registered using * a PrototypeServiceFactory. * * The file reader implementation is associated with a mime-type, specified * in the service property PROP_MIMETYPE(). The specified mime-type should * have a corresponding CustomMimeType service object, registered by the reader * or some other party. * * It is recommended to derive new implementations from AbstractFileReader or * from AbstractFileIO (if both reader and writer is implemented), * which provide correct service registration semantics. * * \sa AbstractFileReader * \sa AbstractFileIO * \sa CustomMimeType * \sa FileReaderRegistry * \sa IFileWriter */ struct MITKCORE_EXPORT IFileReader : public IFileIO { ~IFileReader() override; /** * \brief Set the input location. * \param location The file name to read from. */ virtual void SetInput(const std::string &location) = 0; /** * @brief Set an input stream to read from. * @param location A custom label for the input stream. * @param is The input stream. * * If \c is is \c nullptr, this clears the current input stream and \c location * is interpreted as a file-system path. Otherwise, \c location is a custom * label describing the input stream \c is. */ virtual void SetInput(const std::string &location, std::istream *is) = 0; /** * @brief Get the current input location. * @return The input location. */ virtual std::string GetInputLocation() const = 0; /** * @brief Get the input stream. * @return The currently set input stream. */ virtual std::istream *GetInputStream() const = 0; /** * \brief Reads the specified file or input stream and returns its contents. * * \return A list of created BaseData objects. * * If GetInputStream() returns a non-null value, this method must use * the returned stream object to read the data from. If no input stream * was set, the data must be read from the path returned by GetInputLocation(). * * \throws mitk::Exception */ virtual std::vector> Read() = 0; /** * \brief Reads the specified file or input stream, loading its * contents into the provided DataStorage. * * \param ds The DataStorage to which the data is added. * \return The set of added DataNodes to \c ds. * * This method may be overridden by implementations to create or * reconstructed a hierarchy of mitk::DataNode instances in the * provided mitk::DataStorage. * * \throws mitk::Exception */ virtual DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage &ds) = 0; /** * @return A list of files that were loaded during the last call of Read. */ virtual std::vector< std::string > GetReadFiles() = 0; /** * \brief Optionally provide base data properties as a source of meta data. * * The purpose of this method is not to preset the base data property list * of the read data but to provide access to meta data of previous read * operations that may be beneficial for the new read operation. * * A typical usecase may occur when reading files from an MITK scene file * in which case base data properties are already provided in addition to * the actual data file. If such a data file references other files * relative to its original location on the filesystem, the original * location can be retrieved from the base data properties. In this * scenario, GetInputLocation() would return a path within a temporary * directory in which the scene file has been extracted. This is not the * intended base path for searching referenced external data files. */ virtual void SetProperties(const PropertyList* properties) = 0; protected: /** \sa SetProperties(). */ virtual const PropertyList* GetProperties() const = 0; }; } // namespace mitk MITK_DECLARE_SERVICE_INTERFACE(mitk::IFileReader, "org.mitk.IFileReader") #endif /* IFileReader_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Modules/Core/include/mitkIFileWriter.h b/Modules/Core/include/mitkIFileWriter.h index 20cd64970b..53cebeb1ea 100644 --- a/Modules/Core/include/mitkIFileWriter.h +++ b/Modules/Core/include/mitkIFileWriter.h @@ -1,134 +1,134 @@ /*============================================================================ 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 IFileWriter_H_HEADER_INCLUDED_C1E7E521 #define IFileWriter_H_HEADER_INCLUDED_C1E7E521 #include #include namespace mitk { class BaseData; } namespace mitk { /** * \ingroup IO * \ingroup MicroServices_Interfaces * * \brief The common interface of all MITK file writers. * * Implementations of this interface must be registered as a service - * to make themselve available via the service registry. If the + * to make themselves available via the service registry. If the * implementation is stateful, the service should be registered using * a PrototypeServiceFactory. * * The file writer implementation is associated with a mime-type, specified * in the service property PROP_MIMETYPE() and a mitk::BaseData sub-class * as specified in the PROP_BASEDATA_TYPE() service property. * The specified mime-type should have a corresponding CustomMimeType service * object, registered by the reader or some other party. * * It is recommended to derive new implementations from AbstractFileWriter or * AbstractFileIO (if both reader and writer is implemented), * which provide correct service registration semantics. * * A more in-depth explanation is available on the concepts pages in the developer manual. * * \sa AbstractFileWriter * \sa AbstractFileIO * \sa CustomMimeType * \sa FileWriterRegistry * \sa IFileReader */ struct MITKCORE_EXPORT IFileWriter : public IFileIO { ~IFileWriter() override; /** * @brief Set the input data for writing. * @param data The input data. */ virtual void SetInput(const BaseData *data) = 0; /** * @brief Get the input data set via SetInput(). * @return The input data. */ virtual const BaseData *GetInput() const = 0; /** * @brief Set the output location. * @param location A file-system path. * * The location argument specifies a file-system path where the input * data must be written. This method must remove any previously set * output stream. */ virtual void SetOutputLocation(const std::string &location) = 0; /** * @brief Get the current output location. * @return The output location. * * If no stream is set (i.e. GetOutputStream returns \c nullptr), the * returned location string is required to represent a file-system path. */ virtual std::string GetOutputLocation() const = 0; /** * @brief Set an output stream for writing. * @param location A custom label for the output stream. * @param os The output stream. * * If \c os is \c nullptr, this clears the current output stream and \c location * is interpreted as a file-system path. Otherwise, \c location is a custom * label describing the output stream \c os. */ virtual void SetOutputStream(const std::string &location, std::ostream *os) = 0; /** * @brief Get the output stream. * @return The currently set output stream. */ virtual std::ostream *GetOutputStream() const = 0; /** * @brief Write the input data. * * If GetOutputStream() returns a non-null value, this method must use * the returned stream object to write the data set via SetInput(). If no * output stream was set, the data must be written to the path returned * by GetOutputLocation(). * * @throws mitk::Exception */ virtual void Write() = 0; /** * @brief Service property name for the supported mitk::BaseData sub-class. * * The property value must be of type \c std::string. * * @return The property name. */ static std::string PROP_BASEDATA_TYPE(); }; } // namespace mitk MITK_DECLARE_SERVICE_INTERFACE(mitk::IFileWriter, "org.mitk.IFileWriter") #endif /* IFileWriter_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Modules/Core/include/mitkIMimeTypeProvider.h b/Modules/Core/include/mitkIMimeTypeProvider.h index a891013803..ccaa88b827 100644 --- a/Modules/Core/include/mitkIMimeTypeProvider.h +++ b/Modules/Core/include/mitkIMimeTypeProvider.h @@ -1,68 +1,68 @@ /*============================================================================ 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 MITKIMIMETYPEPROVIDER_H #define MITKIMIMETYPEPROVIDER_H #include #include #include #include #include namespace mitk { /** * @ingroup IO * @ingroup MicroServices_Interfaces * * @brief The IMimeTypeProvider service interface allows to query all registered * mime types. * * Mime types are added to the system by registering a service object of type * CustomMimeType and the registered mime types can be queried bei either using direct * look-ups in the service registry or calling the methods of this service interface. * * This service interface also allows to infer the mime type of a file on the file - * system. The heuristics for infering the actual mime type is implementation specific. + * system. The heuristics for inferring the actual mime type is implementation specific. * * @note This is a core service * * @sa CustomMimeType * @sa CoreServices::GetMimeTypeProvider() */ struct MITKCORE_EXPORT IMimeTypeProvider { virtual ~IMimeTypeProvider(); virtual std::vector GetMimeTypes() const = 0; virtual std::vector GetMimeTypesForFile(const std::string &filePath) const = 0; virtual std::vector GetMimeTypesForCategory(const std::string &category) const = 0; virtual MimeType GetMimeTypeForName(const std::string &name) const = 0; /** * @brief Get a sorted and unique list of mime-type categories. * @return A sorted, unique list of mime-type categories. */ virtual std::vector GetCategories() const = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IMimeTypeProvider, "org.mitk.IMimeTypeProvider") #endif // MITKIMIMETYPEPROVIDER_H diff --git a/Modules/Core/include/mitkIOUtil.h b/Modules/Core/include/mitkIOUtil.h index c774f9b929..50425bdc57 100644 --- a/Modules/Core/include/mitkIOUtil.h +++ b/Modules/Core/include/mitkIOUtil.h @@ -1,444 +1,444 @@ /*============================================================================ 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 MITKIOUTIL_H #define MITKIOUTIL_H #include #include #include #include #include #include #include #include #include #include #if !defined(MITK_WINDOWS_NO_UNDEF) && defined(GetTempPath) #undef GetTempPath #endif namespace us { class ModuleResource; } namespace mitk { class PropertyList; /** * \ingroup IO * * \brief A utility class to load and save data from/to the local file system. * * \see QmitkIOUtil */ class MITKCORE_EXPORT IOUtil { public: - /**Struct that containes information regarding the current loading process. (e.g. Path that should be loaded, + /**Struct that contains information regarding the current loading process. (e.g. Path that should be loaded, all found readers for the load path,...). It is set be IOUtil and used to pass information via the option callback in load operations. */ struct MITKCORE_EXPORT LoadInfo { LoadInfo(const std::string &path); std::string m_Path; std::vector m_Output; FileReaderSelector m_ReaderSelector; bool m_Cancel; const PropertyList* m_Properties; }; /**Struct that is the base class for option callbacks used in load operations. The callback is used by IOUtil, if - more than one suitable reader was found or the a reader containes options that can be set. The callback allows to + more than one suitable reader was found or the a reader contains options that can be set. The callback allows to change option settings and select the reader that should be used (via loadInfo). */ struct MITKCORE_EXPORT ReaderOptionsFunctorBase { virtual bool operator()(LoadInfo &loadInfo) const = 0; }; struct MITKCORE_EXPORT SaveInfo { SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path); bool operator<(const SaveInfo &other) const; /// The BaseData object to save. const BaseData *m_BaseData; /// Contains a set of IFileWriter objects. FileWriterSelector m_WriterSelector; /// The selected mime-type, used to restrict results from FileWriterSelector. MimeType m_MimeType; /// The path to write the BaseData object to. std::string m_Path; /// Flag indicating if sub-sequent save operations are to be canceled. bool m_Cancel; }; /**Struct that is the base class for option callbacks used in save operations. The callback is used by IOUtil, if - more than one suitable writer was found or the a writer containes options that can be set. The callback allows to + more than one suitable writer was found or the a writer contains options that can be set. The callback allows to change option settings and select the writer that should be used (via saveInfo). */ struct MITKCORE_EXPORT WriterOptionsFunctorBase { virtual bool operator()(SaveInfo &saveInfo) const = 0; }; /** * Get the file system path where the running executable is located. * * @return The location of the currently running executable, without the filename. */ static std::string GetProgramPath(); /** * Get the default temporary path. * * @return The default path for temporary data. */ static std::string GetTempPath(); /** * Returns the Directory Seperator for the current OS. * * @return the Directory Seperator for the current OS, i.e. "\\" for Windows and "/" otherwise. */ static char GetDirectorySeparator(); /** * Create and open a temporary file. * * This method generates a unique temporary filename from \c templateName, creates * and opens the file using the output stream \c tmpStream and returns the name of * the newly create file. * - * The \c templateName argument must contain six consective 'X' characters ("XXXXXX") + * The \c templateName argument must contain six consecutive 'X' characters ("XXXXXX") * and these are replaced with a string that makes the filename unique. * * The file is created with read and write permissions for owner only. * * @param tmpStream The output stream for writing to the temporary file. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(std::ofstream &tmpStream, const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Create and open a temporary file. * * This method generates a unique temporary filename from \c templateName, creates * and opens the file using the output stream \c tmpStream and the specified open * mode \c mode and returns the name of the newly create file. The open mode is always * OR'd with std::ios_base::out | std::ios_base::trunc. * - * The \c templateName argument must contain six consective 'X' characters ("XXXXXX") + * The \c templateName argument must contain six consecutive 'X' characters ("XXXXXX") * and these are replaced with a string that makes the filename unique. * * The file is created with read and write permissions for owner only. * * @param tmpStream The output stream for writing to the temporary file. * @param mode The open mode for the temporary file stream. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(std::ofstream &tmpStream, std::ios_base::openmode mode, const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Creates an empty temporary file. * * This method generates a unique temporary filename from \c templateName and creates * this file. * * The file is created with read and write permissions for owner only. * * --- * This version is potentially unsafe because the created temporary file is not kept open * and could be used by another process between calling this method and opening the returned * file path for reading or writing. * --- * * @return The filename of the created temporary file. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Create a temporary directory. * * This method generates a uniquely named temporary directory from \c templateName. * The last set of six consecutive 'X' characters in \c templateName is replaced * with a string that makes the directory name unique. * * The directory is created with read, write and executable permissions for owner only. * * @param templateName An optional template for the directory name. * @param path An optional path where the temporary directory should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary directory could not be created. */ static std::string CreateTemporaryDirectory(const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * @brief Load a file into the given DataStorage. * * This method calls Load(const std::vector&, DataStorage&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param storage A DataStorage object to which the loaded data will be added. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static DataStorage::SetOfObjects::Pointer Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback = nullptr); /** * @brief Load a file into the given DataStorage given user defined IFileReader::Options. * * This method calls Load(const std::vector&, DataStorage&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param options IFileReader option instance that should be used if selected reader * has options. * @param storage A DataStorage object to which the loaded data will be added. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static DataStorage::SetOfObjects::Pointer Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage); /** * @brief Load a file and return the loaded data. * * This method calls Load(const std::vector&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static std::vector Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback = nullptr); template static typename T::Pointer Load(const std::string& path, const ReaderOptionsFunctorBase *optionsCallback = nullptr) { return dynamic_cast(Load(path, optionsCallback).at(0).GetPointer()); } /** * @brief Load a file and return the loaded data. * * This method calls Load(const std::vector&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param options IFileReader option instance that should be used if selected reader * has options. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static std::vector Load(const std::string &path, const IFileReader::Options &options); template static typename T::Pointer Load(const std::string& path, const IFileReader::Options &options) { return dynamic_cast(Load(path, options).at(0).GetPointer()); } /** * @brief Loads a list of file paths into the given DataStorage. * * If an entry in \c paths cannot be loaded, this method will continue to load * the remaining entries into \c storage and throw an exception afterwards. * * @param paths A list of absolute file names including the file extension. * @param storage A DataStorage object to which the loaded data will be added. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if an entry in \c paths could not be loaded. */ static DataStorage::SetOfObjects::Pointer Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback = nullptr); static std::vector Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback = nullptr); /** * @brief Loads the contents of a us::ModuleResource and returns the corresponding mitk::BaseData * @param usResource a ModuleResource, representing a BaseData object * @param mode Optional parameter to set the openmode of the stream * @return The set of loaded BaseData objects. \c Should contain either one or zero elements, since a resource * stream * respresents one object. * @throws mitk::Exception if no reader was found for the stream. */ static std::vector Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in); template static typename T::Pointer Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in) { return dynamic_cast(Load(usResource, mode).at(0).GetPointer()); } static BaseData::Pointer Load(const std::string& path, const PropertyList* properties); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param path The path to the image including file name and and optional file extension. * If no extension is set, the default extension and mime-type for the * BaseData type of \c data is used. * @param setPathProperty * @throws mitk::Exception if no writer for \c data is available or the writer * is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param path The path to the image including file name and an optional file extension. * If no extension is set, the default extension and mime-type for the * BaseData type of \c data is used. * @param options The IFileWriter options to use for the selected writer. * @param setPathProperty * @throws mitk::Exception if no writer for \c data is available or the writer * is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param mimeType The mime-type to use for writing \c data. * @param path The path to the image including file name and an optional file extension. * @param addExtension If \c true, an extension according to the given \c mimeType * is added to \c path if it does not contain one. If \c path already contains * a file name extension, it is not checked for compatibility with \c mimeType. * @param setPathProperty * * @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is * available or the writer is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension = true, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param mimeType The mime-type to use for writing \c data. * @param path The path to the image including file name and an optional file extension. * @param options Configuration data for the used IFileWriter instance. * @param addExtension If \c true, an extension according to the given \c mimeType * is added to \c path if it does not contain one. If \c path already contains * a file name extension, it is not checked for compatibility with \c mimeType. * @param setPathProperty * * @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is * available or the writer is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &mimeType, const std::string &path, const mitk::IFileWriter::Options &options, bool addExtension = true, bool setPathProperty = false); /** * @brief Use SaveInfo objects to save BaseData instances. * * This is a low-level method for directly working with SaveInfo objects. Usually, * the Save() methods taking a BaseData object as an argument are more appropriate. * * @param saveInfos A list of SaveInfo objects for saving contained BaseData objects. * @param setPathProperty * * @see Save(const mitk::BaseData*, const std::string&) */ static void Save(std::vector &saveInfos, bool setPathProperty = false); protected: static std::string Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback); static std::string Save(const BaseData *data, const std::string &mimeType, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension, bool setPathProperty); static std::string Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty); private: struct Impl; }; } #endif // MITKIOUTIL_H diff --git a/Modules/Core/include/mitkIPersistenceService.h b/Modules/Core/include/mitkIPersistenceService.h index 175777be24..22a390446e 100644 --- a/Modules/Core/include/mitkIPersistenceService.h +++ b/Modules/Core/include/mitkIPersistenceService.h @@ -1,370 +1,370 @@ /*============================================================================ 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 mitkIPersistenceService_h #define mitkIPersistenceService_h // mitk #include "mitkPropertyListReplacedObserver.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceReference.h" // for microservices #include "mitkDataStorage.h" #include namespace mitk { /** * \ingroup MicroServices_Interfaces * * The central service for the persistence module * Basic idea is to create PropertyLists with a unique id using AddPropertyList(). A consumer * of this interface can write arbitrary information into this propertylist * Calling Save() and Load() will cause the Service to save and load the current set of propertlists from * a file in the user directory. * Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application * start/stop. * Moreover, depending on the backend type, the service is connected to the SceneSerialization module, i.e. * the user will be asked whether to save/load the propertlists in/from the current ".mitk" file that is selected * by the user. */ class MITKCORE_EXPORT IPersistenceService { public: /** * If PropertyList with the given id exists, returns it. Otherwise creates a new one and returns it. * If id is empty a UUID will be created and set on the variable * If existed was passed, it is true if the PropertyList with that id existed, false otherwise * \return a valid PropertyList with a StringProperty "Id" containing the passed id */ virtual mitk::PropertyList::Pointer GetPropertyList(std::string &id, bool *existed = nullptr) = 0; /** * removes the PropertyList with the given id * \return true if PropertyList existed and could be removed, false otherwise */ virtual bool RemovePropertyList(std::string &id) = 0; /** * Get the default name of the PersistenceFile (the one that is loaded at startup) */ virtual std::string GetDefaultPersistenceFile() = 0; /** * \return The name of the Bool Property that specifies whether a DataNode is a Node carrying Persistence * PropertyLists */ virtual std::string GetPersistenceNodePropertyName() = 0; /** * Creates a vector of DataNodes that contain all PropertyLists. Additionally, the DataNodes * will have the property name set to the PropertyList's id and a BoolProperty equal to * GetPersistenceNodePropertyName() set to true. If ds is set the returned DataNodes will also be added to that DS. * \return vector of DataNodes with the described attributes */ virtual DataStorage::SetOfObjects::Pointer GetDataNodes(DataStorage *ds = nullptr) = 0; /** * Searches storage for persistent DataNodes, extracts and inserts the appended property lists to this service * \return true if at least one node was found from which a PropertyList could be restored */ virtual bool RestorePropertyListsFromPersistentDataNodes(const DataStorage *storage) = 0; /** * Save the current PropertyLists to fileName. If fileName is empty, a special file in the users home directory will * be * used. * if appendchanges is true, the file will not replaced but first loaded, then overwritten and then replaced - * \return false if an error occured (cannot write to file), true otherwise + * \return false if an error occurred (cannot write to file), true otherwise */ virtual bool Save(const std::string &fileName = "", bool appendChanges = false) = 0; /** * Load PropertyLists from fileName. If fileName is empty, a special file in the users home directory will be used. * If enforeReload is false, the service will take care of modified time flags, i.e. it will not load a file * that was loaded before and did not change in the meantime or that was modified by the service itself * *ATTENTION*: If there are PropertyLists with the same id contained in the file, existing PropertyLists will be * overwritten! * \see AddPropertyListReplacedObserver() - * \return false if an error occured (cannot load from file), true otherwise + * \return false if an error occurred (cannot load from file), true otherwise */ virtual bool Load(const std::string &fileName = "", bool enforeReload = true) = 0; /** * Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application * start/stop. */ virtual void SetAutoLoadAndSave(bool autoLoadAndSave) = 0; /** * \return whether AutoLoading is activated or not */ virtual bool GetAutoLoadAndSave() = 0; /** * adds a observer which is informed if a propertyList gets replaced during a Load() procedure */ virtual void AddPropertyListReplacedObserver(PropertyListReplacedObserver *observer) = 0; /** * removes a specific observer */ virtual void RemovePropertyListReplacedObserver(PropertyListReplacedObserver *observer) = 0; /** * nothing to do here */ virtual ~IPersistenceService(); }; } // MACROS FOR AUTOMATIC SAVE FUNCTION #define PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION us::GetModuleContext() #define PERSISTENCE_GET_SERVICE_MACRO \ mitk::IPersistenceService *persistenceService = 0; \ us::ModuleContext *context = PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION; \ if (context) \ { \ us::ServiceReference persistenceServiceRef = \ context->GetServiceReference(); \ if (persistenceServiceRef) \ { \ persistenceService = dynamic_cast( \ context->GetService(persistenceServiceRef)); \ } \ } #define PERSISTENCE_GET_SERVICE_METHOD_MACRO \ mitk::IPersistenceService *GetPersistenceService() const \ { \ PERSISTENCE_GET_SERVICE_MACRO \ return persistenceService; \ } #define PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ \ public: \ bool Save(const std::string &fileName = "", bool appendChanges = false) \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ bool noError = persistenceService != 0; \ if (noError) \ this->ToPropertyList(); \ if (noError) \ noError = persistenceService->Save(fileName, appendChanges); \ return noError; \ } \ bool Load(const std::string &fileName = "", bool enforeReload = true) \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ bool noError = persistenceService != 0 && persistenceService->Load(fileName, enforeReload); \ if (noError) \ { \ this->FromPropertyList(); \ } \ return noError; \ } \ void ToPropertyList() \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ this->InitializePropertyListReplacedObserver(persistenceService); \ if (!persistenceService) \ return; \ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME); #define PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ } \ void FromPropertyList() \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ this->InitializePropertyListReplacedObserver(persistenceService); \ if (!persistenceService) \ return; \ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME); #define PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) \ } \ \ std::string GetId() const { return ID_MEMBER_NAME; } \ private: \ PERSISTENCE_GET_SERVICE_METHOD_MACRO \ struct MyPropertyListReplacedObserver : public mitk::PropertyListReplacedObserver \ { \ MyPropertyListReplacedObserver() : m_##THE_CLASS_NAME(0), m_PersistenceService(0) {} \ ~MyPropertyListReplacedObserver() \ { \ if (m_PersistenceService) \ m_PersistenceService->RemovePropertyListReplacedObserver(this); \ } \ void AfterPropertyListReplaced(const std::string &id, mitk::PropertyList *) \ { \ if (m_##THE_CLASS_NAME && m_##THE_CLASS_NAME->GetId() == id) \ m_##THE_CLASS_NAME->FromPropertyList(); \ } \ void Initialize(THE_CLASS_NAME *_##THE_CLASS_NAME, mitk::IPersistenceService *persistenceService) \ { \ m_##THE_CLASS_NAME = _##THE_CLASS_NAME; \ m_PersistenceService = persistenceService; \ if (m_PersistenceService) \ m_PersistenceService->AddPropertyListReplacedObserver(this); \ } \ \ private: \ THE_CLASS_NAME *m_##THE_CLASS_NAME; \ mitk::IPersistenceService *m_PersistenceService; \ }; \ MyPropertyListReplacedObserver m_MyPropertyListReplacedObserver; \ void InitializePropertyListReplacedObserver(mitk::IPersistenceService *persistenceService) \ { \ static bool observerInitialized = false; \ if (observerInitialized == false && persistenceService) \ { \ m_MyPropertyListReplacedObserver.Initialize(this, persistenceService); \ observerInitialized = true; \ } \ } #define PERSISTENCE_CREATE(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE2(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE3(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE4( \ THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE5(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE6(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME, \ PARAM6_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Set(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Get(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE7(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME, \ PARAM6_MEMBER_NAME, \ PARAM7_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Set(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Set(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Get(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Get(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE8(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME, \ PARAM6_MEMBER_NAME, \ PARAM7_MEMBER_NAME, \ PARAM8_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Set(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Set(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ propList->Set(#PARAM8_MEMBER_NAME, PARAM8_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Get(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Get(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ propList->Get(#PARAM8_MEMBER_NAME, PARAM8_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) MITK_DECLARE_SERVICE_INTERFACE(mitk::IPersistenceService, "org.mitk.services.IPersistenceService") #endif diff --git a/Modules/Core/include/mitkIPropertyDescriptions.h b/Modules/Core/include/mitkIPropertyDescriptions.h index b9c8659ec7..9c31c66e58 100644 --- a/Modules/Core/include/mitkIPropertyDescriptions.h +++ b/Modules/Core/include/mitkIPropertyDescriptions.h @@ -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. ============================================================================*/ #ifndef mitkIPropertyDescriptions_h #define mitkIPropertyDescriptions_h #include #include #include namespace mitk { /** * \ingroup MicroServices_Interfaces * \brief Interface of property descriptions service. * * This service allows you to manage descriptions for properties. * The property view displays descriptions of selected properties (in rich text format) at its bottom. */ class MITKCORE_EXPORT IPropertyDescriptions { public: virtual ~IPropertyDescriptions(); /** \brief Add a description for a specific property. * * \param[in] propertyName Name of the property. * \param[in] description Description of the property. * \param[in] className Optional data node class name to which this description is restricted. * \param[in] overwrite Overwrite already existing description. * \return True if description was added successfully. */ virtual bool AddDescription(const std::string &propertyName, const std::string &description, const std::string &className = "", bool overwrite = false) = 0; - /** \brief Add a description for all properties matching the property regulary expression. + /** \brief Add a description for all properties matching the property regular expression. * - * \param[in] propertyRegEx String of the regular expression specifing all relevant property names. + * \param[in] propertyRegEx String of the regular expression specifying all relevant property names. * \param[in] description Description of the property. * \param[in] className Optional data node class name to which this description is restricted. * \param[in] overwrite Overwrite already existing description. * \return True if description was added successfully. */ virtual bool AddDescriptionRegEx(const std::string &propertyRegEx, const std::string &description, const std::string &className = "", bool overwrite = false) = 0; /** \brief Get the description for a specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the returned description is restricted. * \param[in] allowNameRegEx Indicates of also regular expressions should be regarded. * \return Property description or empty string if no description is available. */ virtual std::string GetDescription(const std::string &propertyName, const std::string &className = "", bool allowNameRegEx = true) const = 0; /** \brief Check if a specific property has a description. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which this description is restricted. * \param[in] allowNameRegEx Indicates of also regular expressions should be regarded. * \return True if the property has a description, false otherwise. */ virtual bool HasDescription(const std::string &propertyName, const std::string &className = "", bool allowNameRegEx = true) const = 0; /** \brief Remove all descriptions. * * \param[in] className Optional data node class name to which this description is restricted. */ virtual void RemoveAllDescriptions(const std::string &className = "") = 0; /** \brief Remove description of specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which this description is restricted. */ virtual void RemoveDescription(const std::string &propertyName, const std::string &className = "") = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyDescriptions, "org.mitk.IPropertyDescriptions") #endif diff --git a/Modules/Core/include/mitkIPropertyPersistence.h b/Modules/Core/include/mitkIPropertyPersistence.h index 50b17a941b..e0ff774a1e 100644 --- a/Modules/Core/include/mitkIPropertyPersistence.h +++ b/Modules/Core/include/mitkIPropertyPersistence.h @@ -1,115 +1,115 @@ /*============================================================================ 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 mitkIPropertyPersistence_h #define mitkIPropertyPersistence_h #include #include namespace mitk { /** \ingroup MicroServices_Interfaces * \brief Interface of property persistence service * * This service allows you to manage persistence info for base data properties. * Persistent properties will be saved if the file format supports custom key value pairs like the .nrrd file * format.\n * \remark The service supports the use of regular expressions (regex; ECMAscript syntax) - * for property names and persistance keys to specifiy persistence rules in a generic way. The getter of the + * for property names and persistance keys to specify persistence rules in a generic way. The getter of the * interface - * (GetInfo and GetInfoByKey) will always ensure that the return containes no regex, thus name and key in the results + * (GetInfo and GetInfoByKey) will always ensure that the return contains no regex, thus name and key in the results * can directly be used. */ class MITKCORE_EXPORT IPropertyPersistence { public: virtual ~IPropertyPersistence(); using InfoResultType = std::list; using MimeTypeNameType = PropertyPersistenceInfo::MimeTypeNameType; /** \brief Add persistence info for a specific base data property. * If there is already a property info instance for the passed * property name and the same info, it won't be added. Info instances * are regarded equal, if the mime types are equal. * You may enforce to overwrite the old equal info for a property name * by the overwrite parameter. * * \param[in] info Persistence info of the property. * \param[in] overwrite Overwrite already existing persistence info. * \return True if persistence info was added successfully. */ virtual bool AddInfo(const PropertyPersistenceInfo *info, bool overwrite = false) = 0; /** \brief Get the persistence info for a specific base data property. * \param[in] propertyName Name of the property. * \param[in] allowNameRegEx Indicates if also added info instances with regexs are being checked. * \return Property persistence info or null pointer if no persistence info is available. */ virtual InfoResultType GetInfo(const std::string &propertyName, bool allowNameRegEx = true) const = 0; /** \brief Get the persistence info for a specific base data property and mime type. * * \param[in] propertyName Name of the property. * \param[in] mime Name of the mime type the info is specified for. * \param[in] allowMimeWildCard Indicates if wildcard is allowed. If it is allowed, the method will first try to find * the specified info. * If no info was found but an info exists with the mime type name PropertyPersistenceInfo::ANY_MIMETYPE_NAME(), the * later info will be * returned as fall back option. * \param[in] allowNameRegEx Indicates if also added info instances with regexs are being checked. * \return Property persistence info or null pointer if no persistence info is available. */ virtual InfoResultType GetInfo(const std::string &propertyName, const MimeTypeNameType &mime, bool allowMimeWildCard = false, bool allowNameRegEx = true) const = 0; /** \brief Get the persistence info that will use the specified key. * * \param[in] persistenceKey Name of the property. * \param[in] allowKeyRegEx Indicates if also added info instances with regexs for the key are being checked. * \return Property persistence info or null pointer if no persistence info is available. */ virtual InfoResultType GetInfoByKey(const std::string &persistenceKey, bool allowKeyRegEx = true) const = 0; /** \brief Check if a specific base data property has persistence info. * * \param[in] propertyName Name of the property. * \param[in] allowNameRegEx Indicates if also added info instances with regexs are being checked. * \return True if the property has persistence info, false otherwise. */ virtual bool HasInfo(const std::string &propertyName, bool allowNameRegEx = true) const = 0; /** \brief Remove all persistence info. */ virtual void RemoveAllInfo() = 0; /** \brief Remove persistence info instances of a specific property name/regex. * * \param[in] propertyName Registered name or regex that should be removed. */ virtual void RemoveInfo(const std::string &propertyName) = 0; /** \brief Remove persistence info instances of a specific property name/regex and mime type. * * \param[in] propertyName Registered name or regex that should be removed. * \param[in] mime Name of the mime type. */ virtual void RemoveInfo(const std::string &propertyName, const MimeTypeNameType &mime) = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyPersistence, "org.mitk.IPropertyPersistence") #endif diff --git a/Modules/Core/include/mitkIPropertyRelations.h b/Modules/Core/include/mitkIPropertyRelations.h index 45476b48d0..c35bd8640d 100644 --- a/Modules/Core/include/mitkIPropertyRelations.h +++ b/Modules/Core/include/mitkIPropertyRelations.h @@ -1,95 +1,95 @@ /*============================================================================ 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 mitkIPropertyRelations_h #define mitkIPropertyRelations_h #include #include namespace mitk { /** \ingroup MicroServices_Interfaces * \brief Interface of property relations service * * This service allows you to manage relations between property provider based on properties. * Property relations are managed by rule classes derived from PropertyRelationRuleBase. * */ class MITKCORE_EXPORT IPropertyRelations { public: virtual ~IPropertyRelations(); using RuleResultVectorType = std::list; using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RuleIDVectorType = std::vector; /** \brief Add rule for a specific relation. * If there is already a property rule instance it won't be added. * Rule instances are regarded equal, if the rule ID is equal. * You may enforce to overwrite the old equal rule * by the overwrite parameter. * * \param[in] rule Relation rule of the property. * \param[in] overwrite Overwrite already existing relation rule. * \return True if relation rule was added successfully. */ virtual bool AddRule(const PropertyRelationRuleBase *rule, bool overwrite = false) = 0; /** \brief Get all relation rules the passed source is really a source of. (PropertyRelationRuleBase::IsSource() would return true) * \param[in] source Pointer to the instance that should be checked as source for the searched rules. * \return Property relation rules or empty list if no relation rules are available. */ virtual RuleResultVectorType GetRulesForSource(const IPropertyProvider* source) const = 0; /** \brief Get all relation rules that would accept the passed IPropertOwner as source. * \param[in] sourceCandidate Pointer to the instance that should be checked to be a suitable source. * \return Property relation rules or empty list if no relation rules are available. */ virtual RuleResultVectorType GetRulesForSourceCandidate(const IPropertyProvider* sourceCandidate) const = 0; /** \brief Get all relation rules that would accept the passed IPropertOwner as destination. * \param[in] destCandidate Pointer to the instance that should be checked to be a suitable destination. * \return Property relation rules or empty list if no relation rules are available. */ virtual RuleResultVectorType GetRulesForDestinationCandidate(const IPropertyProvider* destCandidate) const = 0; - /** \brief Get the relation rule that has the specfied ruleID. + /** \brief Get the relation rule that has the specified ruleID. * * \return Property relation rule or null pointer, if no relation rule is available. */ virtual PropertyRelationRuleBase::ConstPointer GetRule(const RuleIDType &ruleID) const = 0; /** \brief Get all IDs of registered rules */ virtual RuleIDVectorType GetRuleIDs() const = 0; /** \brief Check if a passed instance has defined relation covered by the registered rules. * * \param[in] source Pointer to the instance that should be checked as source for the searched rules. * \return True if the property has relations of registered rules, false otherwise. */ virtual bool HasRuleForSource(const IPropertyProvider* source) const = 0; /** \brief Remove all relation rules. */ virtual void RemoveAllRules() = 0; /** \brief Remove relation rule instance with the passed ID. If rule does not exist nothing happens. */ virtual void RemoveRule(const RuleIDType &ruleID) = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyRelations, "org.mitk.IPropertyRelations") #endif diff --git a/Modules/Core/include/mitkITKImageImport.txx b/Modules/Core/include/mitkITKImageImport.txx index 5e15366fdb..046082da2a 100644 --- a/Modules/Core/include/mitkITKImageImport.txx +++ b/Modules/Core/include/mitkITKImageImport.txx @@ -1,187 +1,187 @@ /*============================================================================ 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 __mitkITKImageImport_txx #define __mitkITKImageImport_txx #include "mitkITKImageImport.h" #include "mitkImageReadAccessor.h" template mitk::ITKImageImport::ITKImageImport() { } template mitk::ITKImageImport::~ITKImageImport() { } template typename mitk::ITKImageImport::InputImageType *mitk::ITKImageImport::GetInput(void) { return static_cast(this->ProcessObject::GetInput(0)); } template void mitk::ITKImageImport::SetInput(const InputImageType *input) { this->ProcessObject::SetNthInput(0, const_cast(input)); } template void mitk::ITKImageImport::SetGeometry(const BaseGeometry *geometry) { if (geometry != nullptr) { m_Geometry = static_cast(geometry->Clone().GetPointer()); } else { m_Geometry = nullptr; } Modified(); } template void mitk::ITKImageImport::GenerateOutputInformation() { InputImageConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); output->InitializeByItk(input.GetPointer()); if (m_Geometry.IsNotNull()) { output->SetGeometry(m_Geometry); } } template void mitk::ITKImageImport::GenerateData() { InputImageConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); output->SetImportChannel((void *)input->GetBufferPointer(), 0, mitk::Image::ReferenceMemory); } template void mitk::ITKImageImport::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); // Input is an image, cast away the constness so we can set // the requested region. InputImagePointer input = const_cast(this->GetInput()); // Use the function object RegionCopier to copy the output region // to the input. The default region copier has default implementations // to handle the cases where the input and output are the same // dimension, the input a higher dimension than the output, and the // input a lower dimension than the output. InputImageRegionType inputRegion; OutputToInputRegionCopierType regionCopier; regionCopier(inputRegion, this->GetOutput()->GetRequestedRegion()); input->SetRequestedRegion(inputRegion); } template void mitk::ITKImageImport::SetNthOutput(DataObjectPointerArraySizeType idx, itk::DataObject *output) { if ((output == nullptr) && (idx == 0)) { // we are disconnected from our output: // copy buffer of input to output, because we // cannot guarantee that the input (to which our - // output is refering) will stay alive. + // output is referring) will stay alive. InputImageConstPointer input = this->GetInput(); mitk::Image::Pointer currentOutput = this->GetOutput(); if (input.IsNotNull() && currentOutput.IsNotNull()) currentOutput->SetChannel(input->GetBufferPointer()); } Superclass::SetNthOutput(idx, output); } template mitk::Image::Pointer mitk::ImportItkImage(const itk::SmartPointer &itkimage, const BaseGeometry *geometry, bool update) { typename mitk::ITKImageImport::Pointer importer = mitk::ITKImageImport::New(); importer->SetInput(itkimage); importer->SetGeometry(geometry); if (update) importer->Update(); return importer->GetOutput(); } template mitk::Image::Pointer mitk::ImportItkImage(const ItkOutputImageType *itkimage, const BaseGeometry *geometry, bool update) { typename mitk::ITKImageImport::Pointer importer = mitk::ITKImageImport::New(); importer->SetInput(itkimage); importer->SetGeometry(geometry); if (update) importer->Update(); return importer->GetOutput(); } template mitk::Image::Pointer mitk::GrabItkImageMemory(itk::SmartPointer &itkimage, mitk::Image *mitkImage, const BaseGeometry *geometry, bool update) { return GrabItkImageMemory(itkimage.GetPointer(), mitkImage, geometry, update); } template mitk::Image::Pointer mitk::GrabItkImageMemory(ItkOutputImageType *itkimage, mitk::Image *mitkImage, const BaseGeometry *geometry, bool update) { if (update) itkimage->Update(); mitk::Image::Pointer resultImage; if (mitkImage != nullptr) { resultImage = mitkImage; - // test the pointer equality with read accessor only if mitk Image is intialized, otherwise an Exception is thrown + // test the pointer equality with read accessor only if mitk Image is initialized, otherwise an Exception is thrown // by the ReadAccessor if (mitkImage->IsInitialized()) { // check the data pointer, for that, we need to ignore the lock of the mitkImage mitk::ImageReadAccessor read_probe(mitk::Image::Pointer(mitkImage), nullptr, mitk::ImageAccessorBase::IgnoreLock); if (itkimage->GetBufferPointer() == read_probe.GetData()) return resultImage; } } else { resultImage = mitk::Image::New(); } resultImage->InitializeByItk(itkimage); resultImage->SetImportVolume(itkimage->GetBufferPointer(), 0, 0, Image::ManageMemory); itkimage->GetPixelContainer()->ContainerManageMemoryOff(); if (geometry != nullptr) resultImage->SetGeometry(static_cast(geometry->Clone().GetPointer())); return resultImage; } #endif //__mitkITKImageImport_txx diff --git a/Modules/Core/include/mitkIdentifiable.h b/Modules/Core/include/mitkIdentifiable.h index 9c2cef821f..f499053cf1 100644 --- a/Modules/Core/include/mitkIdentifiable.h +++ b/Modules/Core/include/mitkIdentifiable.h @@ -1,77 +1,77 @@ /*============================================================================ 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 mitkIdentifiable_h #define mitkIdentifiable_h #include #include namespace mitk { /** * \brief Base class of identifiable objects. * * Offers an interface to query a UID for the instance. Can be inherited * by other classes to provide this capability.\n\n * What does the UID stand for/what is its scope?\n - * - It is unique in its creation, but it is not a content specific unique ID. Therfore: + * - It is unique in its creation, but it is not a content specific unique ID. Therefore: * - A class instance, associated with a UID, may change its values over its lifetime, but still have the same UID. - If a class instance gets persisted and loaded multiple times, then their could be several instances with the same UID. - UIDs are therefore more simelar to git paths than to git hashes. They identify something, but the state of something can change (stream of commits). The difference of the UID compared to using e.g. plain instance pointers to identify an object is that UIDs allow the feature of serialization as they abstract from the memory location of the current runtime environment. - It is up to the application that builds upon that feature to ensure appropriate usage within the application scope. * @remark It is not a feature of mitk::Identifiable per se to be persistable. It depends on classes that derive * from mitk::Identifiable, if and how they implement a persistence mechanism for their MITK UID. * @remark If you want to change the unique ID after creation time, you can use * the mitk::UIDManipulator class. The reationale behind this pattern is * to ensure that you think twice before doing this. It is intended to be * used by data readers if necessary at all. */ class MITKCORE_EXPORT Identifiable { public: using UIDType = std::string; Identifiable(); explicit Identifiable(const UIDType &uid); Identifiable(const Identifiable &) = delete; Identifiable(Identifiable &&) noexcept; virtual ~Identifiable(); Identifiable & operator =(const Identifiable &) = delete; Identifiable & operator =(Identifiable &&other) noexcept; /** * \brief Get unique ID of an object. * * \return Empty string if an object has no unique ID. */ virtual UIDType GetUID() const; protected: virtual void SetUID(const UIDType& uid); private: friend class UIDManipulator; struct Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/Core/include/mitkImage.h b/Modules/Core/include/mitkImage.h index a568fb65b8..5ed23f8a55 100644 --- a/Modules/Core/include/mitkImage.h +++ b/Modules/Core/include/mitkImage.h @@ -1,643 +1,643 @@ /*============================================================================ 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 MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include "mitkBaseData.h" #include "mitkImageAccessorBase.h" #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageVtkAccessor.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedData.h" #include #include #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace itk { template class MutexLockHolder; } namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; /** * @brief Image class for storing images * * Can be asked for header information, the data vector, or vtkImageData objects. * If not the complete data is required, the appropriate SubImageSelector class * should be used for access. * Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n * x ND). Channels are for different kind of data, e.g., morphology in * channel 0, velocities in channel 1. All channels must have the same Geometry! In * particular, the dimensions of all channels are the same, only the pixel-type * may differ between channels. * * For importing ITK images use of mitk::ITKImageImport is recommended, see * \ref Adaptor. * * For ITK v3.8 and older: Converting coordinates from the ITK physical * coordinate system (which does not support rotated images) to the MITK world * coordinate system should be performed via the BaseGeometry of the Image, see * BaseGeometry::WorldToItkPhysicalPoint. * * For more information, see \ref MitkImagePage . * @ingroup Data */ class MITKCORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageVtkReadAccessor; friend class ImageVtkWriteAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder *StatisticsHolderPointer; /** This enum is evaluated when setting new data to an image. */ enum ImportMemoryManagementType { CopyMemory, /**< Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. */ ManageMemory, /**< Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. */ ReferenceMemory, /**< Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. */ DontManageMemory = ReferenceMemory }; /** * @brief Vector container of SmartPointers to ImageDataItems; * Class is only for internal usage to allow convenient access to all slices over iterators; * See documentation of ImageDataItem for details. */ typedef std::vector ImageDataItemPointerArray; public: /** * @brief Returns the PixelType of channel @a n. */ const mitk::PixelType GetPixelType(int n = 0) const; /** * @brief Get dimension of the image */ unsigned int GetDimension() const; /** * @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). * * @sa GetDimensions() */ unsigned int GetDimension(int i) const; public: /** * @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. */ virtual vtkImageData *GetVtkImageData(int t = 0, int n = 0); virtual const vtkImageData *GetVtkImageData(int t = 0, int n = 0) const; /** * @brief Check whether slice @a s at time @a t in channel @a n is set */ bool IsSliceSet(int s = 0, int t = 0, int n = 0) const override; /** * @brief Check whether volume at time @a t in channel @a n is set */ bool IsVolumeSet(int t = 0, int n = 0) const override; /** * @brief Check whether the channel @a n is set */ bool IsChannelSet(int n = 0) const override; /** * @brief Set @a data as slice @a s at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a slice (at least is not smaller than a slice), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportSlice with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicSlice, SetImportSlice, SetImportVolume */ virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); /** * @brief Set @a data as volume at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a volume (at least is not smaller than a volume), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportVolume with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicVolume, SetImportVolume */ virtual bool SetVolume(const void *data, int t = 0, int n = 0); /** * @brief Set @a data in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a channel (at least is not smaller than a channel), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportChannel with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicChannel, SetImportChannel */ virtual bool SetChannel(const void *data, int n = 0); /** * @brief Set @a data as slice @a s at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a slice (at least is not smaller than a slice), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicSlice */ virtual bool SetImportSlice( void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @brief Set @a data as volume at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a volume (at least is not smaller than a volume), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicVolume */ virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual bool SetImportVolume(const void *const_data, int t = 0, int n = 0); /** * @brief Set @a data in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a channel (at least is not smaller than a channel), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicChannel */ virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * initialize new (or re-initialize) image information * @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). */ virtual void Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); /** * initialize new (or re-initialize) image information by a BaseGeometry * * \param type * \param geometry * \param channels * @param tDim defines the number of time steps for which the Image should be initialized */ virtual void Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels = 1, int tDim = 1); /** * \brief Initialize new (or re-initialize) image information by a TimeGeometry * * \param type * \param geometry * \param channels * \param tDim override time dimension if the value is bigger than 0 (Default -1) */ virtual void Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels = 1, int tDim = -1); /** * initialize new (or re-initialize) image information by a PlaneGeometry and number of slices * * Initializes the bounding box according to the width/height of the * PlaneGeometry and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. * The spacing is calculated from the PlaneGeometry. * \sa SlicedGeometry3D::InitializeEvenlySpaced */ virtual void Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels = 1, int tDim = 1); /** * initialize new (or re-initialize) image information by another * mitk-image. * Only the header is used, not the data vector! */ virtual void Initialize(const mitk::Image *image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); /** * initialize new (or re-initialize) image information by @a vtkimagedata, * a vtk-image. * Only the header is used, not the data vector! Use * SetVolume(vtkimage->GetScalarPointer()) to set the data vector. * * @param vtkimagedata * @param channels * @param tDim override time dimension in @a vtkimagedata (if >0 and <) * @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) * @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) */ virtual void Initialize(vtkImageData *vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); /** * initialize new (or re-initialize) image information by @a itkimage, * a templated itk-image. * Only the header is used, not the data vector! Use * SetVolume(itkimage->GetBufferPointer()) to set the data vector. * * @param itkimage * @param channels * @param tDim override time dimension in @a itkimage (if >0 and <) * @param sDim override z-space dimension in @a itkimage (if >0 and <) */ template void InitializeByItk(const itkImageType *itkimage, int channels = 1, int tDim = -1, int sDim = -1) { if (itkimage == nullptr) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension = itkimage->GetImageDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = itkimage->GetLargestPossibleRegion().GetSize().GetSize()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } // overwrite number of slices if sDim is set if ((m_Dimension > 2) && (sDim >= 0)) tmpDimensions[2] = sDim; // overwrite number of time points if tDim is set if ((m_Dimension > 3) && (tDim >= 0)) tmpDimensions[3] = tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize( MakePixelType(itkimage->GetNumberOfComponentsPerPixel()), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType &itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = itkspacing[1]; if (m_Dimension >= 3) spacing[2] = itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType &itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = itkorigin[1]; if (m_Dimension >= 3) origin[2] = itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType &itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3 ? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for (j = 0; j < itkDimMax3; ++j) { columnSum = 0.0; for (i = 0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if (columnSum < mitk::eps) { itkdirectionOk = false; } if ((spacing[j] < -mitk::eps) // (normally sized) negative value && (j == 2) && (m_Dimensions[2] == 1)) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO - // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly + // In these cases spacing is not determined by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). - // I experienced that ITK produced something meaningful nonetheless because is is + // I experienced that ITK produced something meaningful nonetheless because it is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j << "]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j << "]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if (itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) if (i == j) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = itkdirection[i][j] * spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = static_cast(GetSlicedGeometry(0)->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); // clean-up delete[] tmpDimensions; this->Initialize(); } /** * @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; /** * @brief Check whether volume at time @a t in channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidVolume(int t = 0, int n = 0) const; /** * @brief Check whether the channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidChannel(int n = 0) const; /** * @brief Returns true if an image is rotated, i.e. its geometry's * transformation matrix has nonzero elements besides the diagonal. * Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. */ bool IsRotated() const; /** * @brief Get the sizes of all dimensions as an integer-array. * * @sa GetDimension(int i); */ unsigned int *GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor(int id = 0) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ void SetGeometry(BaseGeometry *aGeometry3D) override; /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: mitkCloneMacro(Self); typedef std::lock_guard MutexHolder; int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; void Expand(unsigned int timeSteps) override; virtual ImageDataItemPointer AllocateSliceData( int s = 0, int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; virtual ImageDataItemPointer AllocateVolumeData( int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; virtual ImageDataItemPointer AllocateChannelData( int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; Image(); Image(const Image &other); ~Image() override; void Clear() override; /** @warning Has to be called by every Initialize method! */ void Initialize() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; mutable std::mutex m_ImageDataArraysLock; unsigned int m_Dimension; unsigned int *m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: ImageDataItemPointer GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer GetVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer GetChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const; bool IsSliceSet_unlocked(int s, int t, int n) const; bool IsVolumeSet_unlocked(int t, int n) const; bool IsChannelSet_unlocked(int n) const; /** Stores all existing ImageReadAccessors */ mutable std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ mutable std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ mutable std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ mutable std::mutex m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ mutable std::mutex m_VtkReadersLock; }; /** - * @brief Equal A function comparing two images for beeing equal in meta- and imagedata + * @brief Equal A function comparing two images for being equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see * mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKCORE_EXPORT bool Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Modules/Core/include/mitkImageAccessByItk.h b/Modules/Core/include/mitkImageAccessByItk.h index 388e92c867..80a497bf82 100644 --- a/Modules/Core/include/mitkImageAccessByItk.h +++ b/Modules/Core/include/mitkImageAccessByItk.h @@ -1,706 +1,706 @@ /*============================================================================ 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 MITKIMAGEACCESSBYITK_H_HEADER_INCLUDED #define MITKIMAGEACCESSBYITK_H_HEADER_INCLUDED #include #include #include #include #include #include #include #include #include #include namespace mitk { /** * \brief Exception class thrown in #AccessByItk macros. * * This exception can be thrown by the invocation of the #AccessByItk macros, * if the MITK image is of non-expected dimension or pixel type. * * \ingroup Adaptor */ class AccessByItkException : public virtual std::runtime_error { public: AccessByItkException(const std::string &msg) : std::runtime_error(msg) {} ~AccessByItkException() throw() override {} }; } #ifndef DOXYGEN_SKIP #define _accessByItkPixelTypeException(pixelType, pixelTypeSeq) \ { \ std::string msg("Pixel type "); \ msg.append(pixelType.GetPixelTypeAsString()); \ msg.append(" is not in " BOOST_PP_STRINGIZE(pixelTypeSeq)); \ throw mitk::AccessByItkException(msg); \ } #define _accessByItkDimensionException(dim, validDims) \ { \ std::stringstream msg; \ msg << "Dimension " << (dim) << " is not in " << validDims; \ throw mitk::AccessByItkException(msg.str()); \ } #define _checkSpecificDimensionIter(r, mitkImage, dim) \ if (mitkImage->GetDimension() == dim) \ ; \ else #define _checkSpecificDimension(mitkImage, dimSeq) \ BOOST_PP_SEQ_FOR_EACH(_checkSpecificDimensionIter, mitkImage, dimSeq) \ _accessByItkDimensionException(mitkImage->GetDimension(), BOOST_PP_STRINGIZE(dimSeq)) #define _msvc_expand_bug(macro, arg) BOOST_PP_EXPAND(macro arg) //-------------------------------- 0-Arg Versions -------------------------------------- #define _accessByItk(itkImageTypeFunctionAndImageSeq, pixeltype, dimension) \ if (pixelType == mitk::MakePixelType(pixelType.GetNumberOfComponents()) && \ BOOST_PP_SEQ_TAIL(itkImageTypeFunctionAndImageSeq)->GetDimension() == dimension) \ { \ BOOST_PP_SEQ_HEAD(itkImageTypeFunctionAndImageSeq) \ (mitk::ImageToItkImage(BOOST_PP_SEQ_TAIL(itkImageTypeFunctionAndImageSeq)).GetPointer()); \ } \ else #define _accessByItkArgs(itkImageTypeFunction, type) (itkImageTypeFunction, BOOST_PP_TUPLE_REM(2) type) // product will be of the form ((itkImageTypeFunction)(mitkImage))(short)(2) for pixel type short and dimension 2 #ifdef _MSC_VER #define _accessByItkProductIter(r, product) \ _msvc_expand_bug( \ _accessByItk, \ _msvc_expand_bug(_accessByItkArgs, (BOOST_PP_SEQ_HEAD(product), BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TAIL(product))))) #else #define _accessByItkProductIter(r, product) \ BOOST_PP_EXPAND( \ _accessByItk _accessByItkArgs(BOOST_PP_SEQ_HEAD(product), BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TAIL(product)))) #endif #define _accessFixedTypeByItk(itkImageTypeFunction, mitkImage, pixelTypeSeq, dimSeq) \ BOOST_PP_SEQ_FOR_EACH_PRODUCT(_accessByItkProductIter, (((itkImageTypeFunction)(mitkImage)))(pixelTypeSeq)(dimSeq)) //-------------------------------- n-Arg Versions -------------------------------------- #define _accessByItk_n(itkImageTypeFunctionAndImageSeq, pixeltype, dimension, args) \ if (pixelType == mitk::MakePixelType(pixelType.GetNumberOfComponents()) && \ BOOST_PP_SEQ_TAIL(itkImageTypeFunctionAndImageSeq)->GetDimension() == dimension) \ { \ BOOST_PP_SEQ_HEAD(itkImageTypeFunctionAndImageSeq) \ (mitk::ImageToItkImage(BOOST_PP_SEQ_TAIL(itkImageTypeFunctionAndImageSeq)).GetPointer(), \ BOOST_PP_TUPLE_REM(BOOST_PP_SEQ_HEAD(args)) BOOST_PP_SEQ_TAIL(args)); \ } \ else #define _accessByItkArgs_n(itkImageTypeFunction, type, args) (itkImageTypeFunction, BOOST_PP_TUPLE_REM(2) type, args) // product will be of the form (((itkImageTypeFunction)(mitkImage))(3)(a,b,c))(short)(2) // for the variable argument list a,b,c and for pixel type short and dimension 2 #ifdef _MSC_VER #define _accessByItkProductIter_n(r, product) \ _msvc_expand_bug(_accessByItk_n, \ _msvc_expand_bug(_accessByItkArgs_n, \ (BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_HEAD(product)), \ BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TAIL(product)), \ BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_HEAD(product))))) #else #define _accessByItkProductIter_n(r, product) \ BOOST_PP_EXPAND(_accessByItk_n _accessByItkArgs_n(BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_HEAD(product)), \ BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TAIL(product)), \ BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_HEAD(product)))) #endif #define _accessFixedTypeByItk_n(itkImageTypeFunction, mitkImage, pixelTypeSeq, dimSeq, va_tuple) \ BOOST_PP_SEQ_FOR_EACH_PRODUCT( \ _accessByItkProductIter_n, \ ((((itkImageTypeFunction)(mitkImage))(MITK_PP_ARG_COUNT va_tuple)va_tuple))(pixelTypeSeq)(dimSeq)) #endif // DOXYGEN_SKIP /** * \brief Access a MITK image by an ITK image * * Define a templated function or method (\a itkImageTypeFunction) * within which the mitk-image (\a mitkImage) is accessed: * \code * template < typename TPixel, unsigned int VImageDimension > * void ExampleFunction( itk::Image* itkImage ); * \endcode * * The itk::Image passed to the function/method has the same * data-pointer as the mitk-image. Depending on the const-ness of the * \c mitkImage, your templated access function needs to take a const or * non-const itk::Image pointer and you will get read-only or full read/write * access to the data vector of the mitk-image using the itk-image. * * Example code using the access function above: * \code * mitk::Image* inputMitkImage = ... * try * { * AccessByItk(inputMitkImage, ExampleFunction); * } * catch (const mitk::AccessByItkException& e) * { * // mitk::Image is of wrong pixel type or dimension, * // insert error handling here * } * \endcode * * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \note If your inputMitkImage is an mitk::Image::Pointer, use * inputMitkImage.GetPointer() * \note If you need to pass additional parameters to your * access-function (\a itkImageTypeFunction), use #AccessByItk_n. * \note If you know the dimension of your input mitk-image, * it is better to use AccessFixedDimensionByItk (less code * is generated). * \sa AccessFixedDimensionByItk * \sa AccessFixedTypeByItk * \sa AccessFixedPixelTypeByItk * \sa AccessByItk_n * * \ingroup Adaptor */ #define AccessByItk(mitkImage, itkImageTypeFunction) \ AccessFixedTypeByItk( \ mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ, MITK_ACCESSBYITK_DIMENSIONS_SEQ) /** * \brief Access a mitk-image with known pixeltype (but unknown dimension) by an itk-image. * * For usage, see #AccessByItk. * * \param pixelTypeSeq A sequence of pixel types, like (short)(char)(int) * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * If the image has a different pixel type, a mitk::AccessByItkException exception is * thrown. If you do not know the pixel type for sure, use #AccessByItk. * * \sa AccessByItk * \sa AccessFixedDimensionByItk * \sa AccessFixedTypeByItk * \sa AccessFixedPixelTypeByItk_n * * \ingroup Adaptor */ #define AccessFixedPixelTypeByItk(mitkImage, itkImageTypeFunction, pixelTypeSeq) \ AccessFixedTypeByItk(mitkImage, itkImageTypeFunction, pixelTypeSeq, MITK_ACCESSBYITK_DIMENSIONS_SEQ) /** * \brief Access a mitk-image with an integral pixel type by an itk-image * * See #AccessByItk for details. * * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \sa AccessFixedPixelTypeByItk * \sa AccessByItk * \sa AccessIntegralPixelTypeByItk_n */ #define AccessIntegralPixelTypeByItk(mitkImage, itkImageTypeFunction) \ AccessFixedTypeByItk( \ mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES_SEQ, MITK_ACCESSBYITK_DIMENSIONS_SEQ) /** * \brief Access a mitk-image with a floating point pixel type by an ITK image * * See #AccessByItk for details. * * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \sa AccessFixedPixelTypeByItk * \sa AccessByItk * \sa AccessFloatingPixelTypeByItk_n */ #define AccessFloatingPixelTypeByItk(mitkImage, itkImageTypeFunction) \ AccessFixedTypeByItk( \ mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES_SEQ, MITK_ACCESSBYITK_DIMENSIONS_SEQ) #define AccessVectorPixelTypeByItk(mitkImage, itkImageTypeFunction) \ AccessFixedTypeByItk( \ mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES_SEQ, MITK_ACCESSBYITK_DIMENSIONS_SEQ) /** * \brief Access a mitk-image with known dimension by an itk-image * * For usage, see #AccessByItk. * * \param dimension Dimension of the mitk-image. If the image has a different dimension, * a mitk::AccessByItkException exception is thrown. * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \note If you do not know the dimension for sure, use #AccessByItk. * * \sa AccessByItk * \sa AccessFixedDimensionByItk_n * \sa AccessFixedTypeByItk * \sa AccessFixedPixelTypeByItk * * \ingroup Adaptor */ #define AccessFixedDimensionByItk(mitkImage, itkImageTypeFunction, dimension) \ AccessFixedTypeByItk(mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ, (dimension)) #define AccessVectorFixedDimensionByItk(mitkImage, itkImageTypeFunction, dimension) \ AccessFixedTypeByItk(mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES_SEQ, (dimension)) /** * \brief Access a mitk-image with known type (pixel type and dimension) by an itk-image. * * The provided mitk-image must be in the set of types created by taking the * cartesian product of the pixel type sequence and the dimension sequence. * For example, a call to * \code * AccessFixedTypeByItk(myMitkImage, MyAccessFunction, (short)(int), (2)(3)) * \endcode * asserts that the type of myMitkImage (pixeltype,dim) is in the set {(short,2),(short,3),(int,2),(int,3)}. * For more information, see #AccessByItk. * * \param pixelTypeSeq A sequence of pixel types, like (short)(char)(int). * \param dimSeq A sequence of dimensions, like (2)(3). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * If the image has a different dimension or pixel type, * a mitk::AccessByItkException exception is thrown. * * \note If you do not know the dimension for sure, use #AccessByItk. * * \sa AccessByItk * \sa AccessFixedDimensionByItk * \sa AccessFixedTypeByItk_n * \sa AccessFixedPixelTypeByItk * * \ingroup Adaptor */ #define AccessFixedTypeByItk(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq) \ \ { \ const mitk::PixelType &pixelType = mitkImage->GetPixelType(); \ _checkSpecificDimension(mitkImage, dimSeq); \ _accessFixedTypeByItk(itkImageTypeFunction, mitkImage, pixelTypeSeq, dimSeq) \ _accessByItkPixelTypeException(mitkImage->GetPixelType(), pixelTypeSeq) \ } //------------------------------ n-Arg Access Macros ----------------------------------- /** * \brief Access a MITK image by an ITK image with one or more parameters. * - * Define a templated function or method (\a itkImageTypeFunction) with one ore more + * Define a templated function or method (\a itkImageTypeFunction) with one or more * additional parameters, within which the mitk-image (\a mitkImage) is accessed: * \code * template < typename TPixel, unsigned int VImageDimension > * void ExampleFunction( itk::Image* itkImage, SomeType param); * \endcode * * The itk::Image passed to the function/method has the same * data-pointer as the mitk-image. So you have full read- and write- * access to the data vector of the mitk-image using the itk-image. * Call by: * \code * SomeType param = ... * mitk::Image* inputMitkImage = ... * try * { * AccessByItk_n(inputMitkImage, ExampleFunction, (param)); * } * catch (const mitk::AccessByItkException& e) * { * // mitk::Image is of wrong pixel type or dimension, * // insert error handling here * } * \endcode * * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \note If your inputMitkImage is an mitk::Image::Pointer, use * inputMitkImage.GetPointer() * \note If you know the dimension of your input mitk-image, * it is better to use AccessFixedDimensionByItk_n (less code * is generated). * \sa AccessFixedDimensionByItk_n * \sa AccessFixedTypeByItk_n * \sa AccessFixedPixelTypeByItk_n * \sa AccessByItk * * \ingroup Adaptor */ #define AccessByItk_n(mitkImage, itkImageTypeFunction, va_tuple) \ AccessFixedTypeByItk_n( \ mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ, MITK_ACCESSBYITK_DIMENSIONS_SEQ, va_tuple) /** * \brief Access a mitk-image with known pixeltype (but unknown dimension) by an itk-image * with one or more parameters. * * For usage, see #AccessByItk_n. * * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param pixelTypeSeq A sequence of pixel types, like (short)(char)(int). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * If the image has a different pixel type, a mitk::AccessByItkException exception is * thrown. If you do not know the pixel type for sure, use #AccessByItk_n. * * \sa AccessByItk_n * \sa AccessFixedDimensionByItk_n * \sa AccessFixedTypeByItk_n * \sa AccessFixedPixelTypeByItk * * \ingroup Adaptor */ #define AccessFixedPixelTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, va_tuple) \ AccessFixedTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, MITK_ACCESSBYITK_DIMENSIONS_SEQ, va_tuple) /** * \brief Access an mitk::Image with an integral pixel type by an ITK image with * one or more parameters. * * See #AccessByItk_n for details. * * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \sa AccessFixedPixelTypeByItk_n * \sa AccessByItk_n * \sa AccessIntegralPixelTypeByItk */ #define AccessIntegralPixelTypeByItk_n(mitkImage, itkImageTypeFunction, va_tuple) \ AccessFixedTypeByItk_n(mitkImage, \ itkImageTypeFunction, \ MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES_SEQ, \ MITK_ACCESSBYITK_DIMENSIONS_SEQ, \ va_tuple) /** * \brief Access an mitk::Image with a floating point pixel type by an ITK image * with one or more parameters. * * See #AccessByItk_n for details. * * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \sa AccessFixedPixelTypeByItk_n * \sa AccessByItk_n * \sa AccessFloatingPixelTypeByItk */ #define AccessFloatingPixelTypeByItk_n(mitkImage, itkImageTypeFunction, va_tuple) \ AccessFixedTypeByItk_n(mitkImage, \ itkImageTypeFunction, \ MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES_SEQ, \ MITK_ACCESSBYITK_DIMENSIONS_SEQ, \ va_tuple) /** * \brief Access a vector mitk::Image by an ITK vector image with one or more parameters. * * See #AccessByItk_n for details. * * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \sa AccessFixedPixelTypeByItk_n * \sa AccessByItk_n */ #define AccessVectorPixelTypeByItk_n(mitkImage, itkImageTypeFunction, va_tuple) \ AccessFixedTypeByItk_n(mitkImage, \ itkImageTypeFunction, \ MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES_SEQ, \ MITK_ACCESSBYITK_DIMENSIONS_SEQ, \ va_tuple) /** * \brief Access a mitk-image with known dimension by an itk-image with * one or more parameters. * * For usage, see #AccessByItk_n. * * \param dimension Dimension of the mitk-image. If the image has a different dimension, * a mitk::AccessByItkException exception is thrown. * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \note If you do not know the dimension for sure, use #AccessByItk_n. * * \sa AccessByItk_n * \sa AccessFixedDimensionByItk * \sa AccessFixedTypeByItk_n * \sa AccessFixedPixelTypeByItk_n * * \ingroup Adaptor */ #define AccessFixedDimensionByItk_n(mitkImage, itkImageTypeFunction, dimension, va_tuple) \ AccessFixedTypeByItk_n(mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ, (dimension), va_tuple) /** * \brief Access a vector mitk-image with known dimension by a ITK vector image with * one or more parameters. * * For usage, see #AccessByItk_n. * * \param dimension Dimension of the mitk-image. If the image has a different dimension, * a mitk::AccessByItkException exception is thrown. * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * \note If you do not know the dimension for sure, use #AccessVectorPixelTypeByItk_n. * * \sa AccessByItk_n * \sa AccessVectorPixelTypeByItk_n * \sa AccessFixedTypeByItk_n * * \ingroup Adaptor */ #define AccessVectorFixedDimensionByItk_n(mitkImage, itkImageTypeFunction, dimension, va_tuple) \ AccessFixedTypeByItk_n( \ mitkImage, itkImageTypeFunction, MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES_SEQ, (dimension), va_tuple) /** * \brief Access a mitk-image with known type (pixel type and dimension) by an itk-image * with one or more parameters. * * For usage, see AccessFixedTypeByItk. * * \param pixelTypeSeq A sequence of pixel types, like (short)(char)(int). * \param dimSeq A sequence of dimensions, like (2)(3). * \param va_tuple A variable length tuple containing the arguments to be passed * to the access function itkImageTypeFunction, e.g. ("first", 2, THIRD). * \param mitkImage The MITK input image. * \param itkImageTypeFunction The templated access-function to be called. * * \throws mitk::AccessByItkException If mitkImage is of unsupported pixel type or dimension. * * If the image has a different dimension or pixel type, * a mitk::AccessByItkException exception is thrown. * * \note If you do not know the dimension for sure, use #AccessByItk_n. * * \sa AccessByItk_n * \sa AccessFixedDimensionByItk_n * \sa AccessFixedTypeByItk * \sa AccessFixedPixelTypeByItk_n * * \ingroup Adaptor */ #define AccessFixedTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, va_tuple) \ \ { \ const mitk::PixelType &pixelType = mitkImage->GetPixelType(); \ _checkSpecificDimension(mitkImage, dimSeq); \ _accessFixedTypeByItk_n(itkImageTypeFunction, mitkImage, pixelTypeSeq, dimSeq, va_tuple) \ _accessByItkPixelTypeException(mitkImage->GetPixelType(), pixelTypeSeq) \ } //------------------------- For back-wards compatibility ------------------------------- #define AccessByItk_1(mitkImage, itkImageTypeFunction, arg1) AccessByItk_n(mitkImage, itkImageTypeFunction, (arg1)) #define AccessFixedPixelTypeByItk_1(mitkImage, itkImageTypeFunction, pixelTypeSeq, arg1) \ AccessFixedPixelTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, (arg1)) #define AccessFixedDimensionByItk_1(mitkImage, itkImageTypeFunction, dimension, arg1) \ AccessFixedDimensionByItk_n(mitkImage, itkImageTypeFunction, dimension, (arg1)) #define AccessFixedTypeByItk_1(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, arg1) \ AccessFixedTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, (arg1)) #define AccessByItk_2(mitkImage, itkImageTypeFunction, arg1, arg2) \ AccessByItk_n(mitkImage, itkImageTypeFunction, (arg1, arg2)) #define AccessFixedPixelTypeByItk_2(mitkImage, itkImageTypeFunction, pixelTypeSeq, arg1, arg2) \ AccessFixedPixelTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, (arg1, arg2)) #define AccessFixedDimensionByItk_2(mitkImage, itkImageTypeFunction, dimension, arg1, arg2) \ AccessFixedDimensionByItk_n(mitkImage, itkImageTypeFunction, dimension, (arg1, arg2)) #define AccessFixedTypeByItk_2(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, arg1, arg2) \ AccessFixedTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, (arg1, arg2)) #define AccessByItk_3(mitkImage, itkImageTypeFunction, arg1, arg2, arg3) \ AccessByItk_n(mitkImage, itkImageTypeFunction, (arg1, arg2, arg3)) #define AccessFixedPixelTypeByItk_3(mitkImage, itkImageTypeFunction, pixelTypeSeq, arg1, arg2, arg3) \ AccessFixedPixelTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, (arg1, arg2, arg3)) #define AccessFixedDimensionByItk_3(mitkImage, itkImageTypeFunction, dimension, arg1, arg2, arg3) \ AccessFixedDimensionByItk_n(mitkImage, itkImageTypeFunction, dimension, (arg1, arg2, arg3)) #define AccessFixedTypeByItk_3(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, arg1, arg2, arg3) \ AccessFixedTypeByItk_n(mitkImage, itkImageTypeFunction, pixelTypeSeq, dimSeq, (arg1, arg2, arg3)) //----------------------------- Access two MITK Images --------------------------------- #ifndef DOXYGEN_SKIP #define _accessTwoImagesByItk(itkImageTypeFunction, pixeltype1, dim1, pixeltype2, dim2) \ if (pixelType1 == mitk::MakePixelType>() && \ pixelType2 == mitk::MakePixelType>() && constImage1->GetDimension() == dim1 && \ constImage2->GetDimension() == dim2) \ { \ typedef itk::Image ImageType1; \ typedef itk::Image ImageType2; \ typedef mitk::ImageToItk ImageToItkType1; \ typedef mitk::ImageToItk ImageToItkType2; \ itk::SmartPointer imagetoitk1 = ImageToItkType1::New(); \ imagetoitk1->SetInput(nonConstImage1); \ imagetoitk1->Update(); \ itk::SmartPointer imagetoitk2 = ImageToItkType2::New(); \ imagetoitk2->SetInput(nonConstImage2); \ imagetoitk2->Update(); \ itkImageTypeFunction(imagetoitk1->GetOutput(), imagetoitk2->GetOutput()); \ } \ else #define _accessTwoImagesByItkArgs2(itkImageTypeFunction, type1, type2) \ (itkImageTypeFunction, BOOST_PP_TUPLE_REM(2) type1, BOOST_PP_TUPLE_REM(2) type2) #define _accessTwoImagesByItkArgs(product) \ BOOST_PP_EXPAND(_accessTwoImagesByItkArgs2 BOOST_PP_EXPAND( \ (BOOST_PP_SEQ_HEAD(product), BOOST_PP_TUPLE_REM(2) BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TAIL(product))))) // product is of the form (itkImageTypeFunction)((short,2))((char,2)) #ifdef _MSC_VER #define _accessTwoImagesByItkIter(r, product) \ BOOST_PP_EXPAND(_accessTwoImagesByItk _msvc_expand_bug( \ _accessTwoImagesByItkArgs2, \ (BOOST_PP_SEQ_HEAD(product), \ _msvc_expand_bug(BOOST_PP_TUPLE_REM(2), BOOST_PP_EXPAND(BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TAIL(product))))))) #else #define _accessTwoImagesByItkIter(r, product) BOOST_PP_EXPAND(_accessTwoImagesByItk _accessTwoImagesByItkArgs(product)) #endif #define _accessTwoImagesByItkForEach(itkImageTypeFunction, tseq1, tseq2) \ BOOST_PP_SEQ_FOR_EACH_PRODUCT(_accessTwoImagesByItkIter, ((itkImageTypeFunction))(tseq1)(tseq2)) #endif // DOXYGEN_SKIP /** * \brief Access two mitk-images with known dimension by itk-images * * Define a templated function or method (\a itkImageTypeFunction) * within which the mitk-images (\a mitkImage1 and \a mitkImage2) are accessed: * \code * template * void ExampleFunctionTwoImages(itk::Image* itkImage1, itk::Image* itkImage2); * \endcode * * The itk::Image passed to the function/method has the same * data-pointer as the mitk-image. So you have full read- and write- * access to the data vector of the mitk-image using the itk-image. * Call by: * \code * mitk::Image* inputMitkImage1 = ... * mitk::Image* inputMitkImage2 = ... * try * { * AccessTwoImagesFixedDimensionByItk(inputMitkImage1, inputMitkImage2, ExampleFunctionTwoImages, 3); * } * catch (const mitk::AccessByItkException& e) * { * // mitk::Image arguments are of wrong pixel type or dimension, * // insert error handling here * } * \endcode * * \note If your inputMitkImage1 or inputMitkImage2 is a mitk::Image::Pointer, use * inputMitkImage1.GetPointer(). * * \param mitkImage1 The first MITK input image. * \param mitkImage2 The second MITK input image. * \param itkImageTypeFunction The name of the template access-function to be called. * \param dimension Dimension of the two mitk-images. * * \throws mitk::AccessByItkException If mitkImage1 and mitkImage2 have different dimensions or * one of the images is of unsupported pixel type or dimension. * * \sa #AccessByItk * * \ingroup Adaptor */ #define AccessTwoImagesFixedDimensionByItk(mitkImage1, mitkImage2, itkImageTypeFunction, dimension) \ \ { \ const mitk::PixelType &pixelType1 = mitkImage1->GetPixelType(); \ const mitk::PixelType &pixelType2 = mitkImage2->GetPixelType(); \ const mitk::Image *constImage1 = mitkImage1; \ const mitk::Image *constImage2 = mitkImage2; \ mitk::Image *nonConstImage1 = const_cast(constImage1); \ mitk::Image *nonConstImage2 = const_cast(constImage2); \ nonConstImage1->Update(); \ nonConstImage2->Update(); \ _checkSpecificDimension(mitkImage1, (dimension)); \ _checkSpecificDimension(mitkImage2, (dimension)); \ _accessTwoImagesByItkForEach( \ itkImageTypeFunction, MITK_ACCESSBYITK_TYPES_DIMN_SEQ(dimension), MITK_ACCESSBYITK_TYPES_DIMN_SEQ(dimension)) \ { \ std::string msg("Pixel type "); \ msg.append(pixelType1.GetComponentTypeAsString()); \ msg.append(" or pixel type "); \ msg.append(pixelType2.GetComponentTypeAsString()); \ msg.append(" is not in " BOOST_PP_STRINGIZE(MITK_ACCESSBYITK_TYPES_DIMN_SEQ(dimension))); \ throw mitk::AccessByItkException(msg); \ } \ } #endif // of MITKIMAGEACCESSBYITK_H_HEADER_INCLUDED diff --git a/Modules/Core/include/mitkImageCaster.h b/Modules/Core/include/mitkImageCaster.h index 46aeb9fdaf..e855aac3fd 100644 --- a/Modules/Core/include/mitkImageCaster.h +++ b/Modules/Core/include/mitkImageCaster.h @@ -1,62 +1,62 @@ /*============================================================================ 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 MITKIMAGECASTER_H #define MITKIMAGECASTER_H #include #include #include #include #include #include #include #define DeclareMitkImageCasterMethods(r, data, type) \ static void CastToItkImage(const mitk::Image *, itk::SmartPointer> &); \ static void CastToMitkImage(const itk::Image *, itk::SmartPointer &); namespace mitk { /// /// \brief This class is just a proxy for global functions which are needed by the - /// python wrapping process since global functions cannnot be wrapped. Static method + /// python wrapping process since global functions cannot be wrapped. Static method /// can be wrapped though. /// class MITKCORE_EXPORT ImageCaster { public: BOOST_PP_SEQ_FOR_EACH(DeclareMitkImageCasterMethods, _, MITK_ACCESSBYITK_TYPES_DIMN_SEQ(2)) BOOST_PP_SEQ_FOR_EACH(DeclareMitkImageCasterMethods, _, MITK_ACCESSBYITK_TYPES_DIMN_SEQ(3)) static void CastBaseData(mitk::BaseData *const, itk::SmartPointer &); }; class MITKCORE_EXPORT Caster { public: static void Cast(BaseData *dat, Surface *surface); }; class MITKCORE_EXPORT RendererAccess { public: static void Set3DRenderer(vtkRenderer *renderer); static vtkRenderer *Get3DRenderer(); protected: static vtkRenderer *m_3DRenderer; }; } // namespace mitk #endif // MITKIMAGECASTER_H diff --git a/Modules/Core/include/mitkImageDescriptor.h b/Modules/Core/include/mitkImageDescriptor.h index e9e082a3d7..57fde4f2b3 100644 --- a/Modules/Core/include/mitkImageDescriptor.h +++ b/Modules/Core/include/mitkImageDescriptor.h @@ -1,122 +1,122 @@ /*============================================================================ 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 MITKIMAGEDESCRIPTOR_H #define MITKIMAGEDESCRIPTOR_H #include #include #include #include "mitkChannelDescriptor.h" #include "mitkCommon.h" /// Defines the maximum of 8 dimensions per image channel #define MAX_IMAGE_DIMENSIONS 8 namespace mitk { /** \brief An object to hold all essential information about an Image object The ImageDescriptor holds an std::vector of pointers to ChannelDescriptor together with the - information about the image's dimensions. The general assumption ist, that each channel of an image + information about the image's dimensions. The general assumption is that each channel of an image has to have the same geometry. \sa Image, ChannelDescriptor */ class MITKCORE_EXPORT ImageDescriptor : public itk::Object { public: mitkClassMacroItkParent(ImageDescriptor, itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Insert new channel @param ptype Pixel Type @param name channel's name */ void AddNewChannel(mitk::PixelType ptype, const char *name = nullptr); /** \brief Initialize the image descriptor by the dimensions */ void Initialize(const unsigned int *dims, const unsigned int dim); /** \brief Initialize the descriptor by an referenced Descriptor */ void Initialize(const ImageDescriptor::Pointer refDescriptor, unsigned int channel = 0); /** \brief Get the C-array of unsigned int holding the size for each dimension of the image - The C-array has allways lenght of MAX_IMAGE_DIMENSIONS + The C-array has always length of MAX_IMAGE_DIMENSIONS */ const unsigned int *GetDimensions() const { return m_Dimensions; } /** \brief Get the number dimensions used (e.g. non-zero size) The return value does not exceed MAX_IMAGE_DIMENSIONS */ unsigned int GetNumberOfDimensions() const { return m_NumberOfDimensions; } /** \brief Get the name of selected channel If the name of the channel wasn't initialized, the string returned is set to "Unnamed [ ]" \sa PixelType, ChannelDescriptor */ const std::string GetChannelName(unsigned int id) const; /** \brief Get the pixel type of a channel specified by its name Returns an uninitialized PixelType object if no channel with given name was found */ PixelType GetChannelTypeByName(const char *name) const; /** \brief Get the pixel type of a channel specified by its id Returns an uninitialized PixelType object if no channel with given id was found */ PixelType GetChannelTypeById(unsigned int id) const; /** \brief Get the ChannelDescriptor for a channel specified by its id */ ChannelDescriptor GetChannelDescriptor(unsigned int id = 0) const; /** \brief Get the count of channels used */ unsigned int GetNumberOfChannels() const { return m_NumberOfChannels; } protected: /** Protected constructor */ ImageDescriptor(); - /** Protected desctructor */ + /** Protected destructor */ ~ImageDescriptor() override{}; private: /** A std::vector holding a pointer to a ChannelDescriptor for each active channel of the image */ std::vector m_ChannelDesc; /** A vector holding the names of corresponding channels */ std::vector m_ChannelNames; /** Constant iterator for traversing the vector of channel's names */ typedef std::vector::const_iterator ConstChannelNamesIter; /** Constant iterator for traversing the vector of ChannelDescriptors */ typedef std::vector::const_iterator ConstChannelsIter; unsigned int m_NumberOfChannels; unsigned int m_NumberOfDimensions; unsigned int m_Dimensions[MAX_IMAGE_DIMENSIONS]; }; } // end namespace #endif // MITKIMAGEDESCRIPTOR_H diff --git a/Modules/Core/include/mitkImageGenerator.h b/Modules/Core/include/mitkImageGenerator.h index fee6d12c9d..b6700090f8 100644 --- a/Modules/Core/include/mitkImageGenerator.h +++ b/Modules/Core/include/mitkImageGenerator.h @@ -1,236 +1,236 @@ /*============================================================================ 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 ImageGenerator_H_HEADER_INCLUDED #define ImageGenerator_H_HEADER_INCLUDED #include "mitkImageWriteAccessor.h" #include #include #include #include namespace mitk { /** * @brief generator for synthetic MITK images * This is a helper class to generate synthetic MITK images (random or gradient). * * @ingroup IO */ class MITKCORE_EXPORT ImageGenerator { public: /** * \brief Generates gradient image with the defined size and spacing */ template static mitk::Image::Pointer GenerateGradientImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ, float spacingX = 1, float spacingY = 1, float spacingZ = 1) { typedef itk::Image ImageType; typename ImageType::RegionType imageRegion; imageRegion.SetSize(0, dimX); imageRegion.SetSize(1, dimY); imageRegion.SetSize(2, dimZ); typename ImageType::SpacingType spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; mitk::Point3D origin; origin.Fill(0.0); itk::Matrix directionMatrix; directionMatrix.SetIdentity(); typename ImageType::Pointer image = ImageType::New(); image->SetSpacing(spacing); image->SetOrigin(origin); image->SetDirection(directionMatrix); image->SetLargestPossibleRegion(imageRegion); image->SetBufferedRegion(imageRegion); image->SetRequestedRegion(imageRegion); image->Allocate(); image->FillBuffer(0.0); typedef itk::ImageRegionIterator IteratorOutputType; IteratorOutputType it(image, imageRegion); it.GoToBegin(); TPixelType val = 0; while (!it.IsAtEnd()) { it.Set(val); val++; ++it; } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk(image.GetPointer()); mitkImage->SetVolume(image->GetBufferPointer()); return mitkImage; } /** * \brief Generates an image of a same geometry as the one given as reference The image buffer is filled to the fill_value given as parameter */ template static mitk::Image::Pointer GenerateImageFromReference(mitk::Image::Pointer reference, TPixelType fill_value) { mitk::Image::Pointer output = mitk::Image::New(); mitk::PixelType output_type = MakeScalarPixelType(); // all metadata (except type) come from reference image output->SetGeometry(reference->GetGeometry()); output->Initialize(output_type, reference->GetDimension(), reference->GetDimensions()); // get a pointer to the image buffer to write into TPixelType *imageBuffer = nullptr; try { mitk::ImageWriteAccessor writeAccess(output); imageBuffer = static_cast(writeAccess.GetData()); } catch (...) { MITK_ERROR << "Write access not granted on mitk::Image."; } - // fill the buffer with the specifed value + // fill the buffer with the specified value for (unsigned int i = 0; i < output->GetVolumeData(0)->GetSize(); i++) { imageBuffer[i] = fill_value; } return output; } /*! \brief Generates random image with the defined size and spacing */ template static mitk::Image::Pointer GenerateRandomImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ = 1, unsigned int dimT = 1, mitk::ScalarType spacingX = 1, mitk::ScalarType spacingY = 1, mitk::ScalarType spacingZ = 1, const double randomMax = 1000.0f, const double randMin = 0.0f) { // set the data type according to the template mitk::PixelType type = MakeScalarPixelType(); // type.Initialize(typeid(TPixelType)); - // initialize the MITK image with given dimenion and data type + // initialize the MITK image with given dimension and data type mitk::Image::Pointer output = mitk::Image::New(); auto dimensions = new unsigned int[4]; unsigned int numberOfDimensions = 0; unsigned int bufferSize = 0; // check which dimension is needed if (dimT <= 1) { if (dimZ <= 1) { // 2D numberOfDimensions = 2; dimensions[0] = dimX; dimensions[1] = dimY; bufferSize = dimX * dimY; } else { // 3D numberOfDimensions = 3; dimensions[0] = dimX; dimensions[1] = dimY; dimensions[2] = dimZ; bufferSize = dimX * dimY * dimZ; } } else { // 4D numberOfDimensions = 4; dimensions[0] = dimX; dimensions[1] = dimY; dimensions[2] = dimZ; dimensions[3] = dimT; bufferSize = dimX * dimY * dimZ * dimT; } output->Initialize(type, numberOfDimensions, dimensions); mitk::Vector3D spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; output->SetSpacing(spacing); // get a pointer to the image buffer to write into TPixelType *imageBuffer = nullptr; try { mitk::ImageWriteAccessor writeAccess(output); imageBuffer = static_cast(writeAccess.GetData()); } catch (...) { MITK_ERROR << "Write access not granted on mitk::Image."; } // initialize the random generator itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize(); // fill the buffer for each pixel/voxel for (unsigned int i = 0; i < bufferSize; i++) { // the comparison of the component type is sufficient enough since the mitk::PixelType type object is // created as SCALAR and hence does not need the comparison against type.GetPixelTypeId() == // itk::IOPixelEnum::SCALAR if (type.GetComponentType() == itk::IOComponentEnum::INT) // call integer function { imageBuffer[i] = (TPixelType)randomGenerator->GetIntegerVariate((int)randomMax); // TODO random generator does not support integer values in a given range (e.g. from 5-10) // range is always [0, (int)randomMax] } else if ((type.GetComponentType() == itk::IOComponentEnum::DOUBLE) || (type.GetComponentType() == itk::IOComponentEnum::FLOAT)) // call floating point function { imageBuffer[i] = (TPixelType)randomGenerator->GetUniformVariate(randMin, randomMax); } else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR) { // use the integer randomGenerator with mod 256 to generate unsigned char values imageBuffer[i] = (unsigned char)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 256; } else if (type.GetComponentType() == itk::IOComponentEnum::USHORT) { imageBuffer[i] = (unsigned short)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 65536; } else { MITK_ERROR << "Datatype not supported yet."; // TODO call different methods for other datatypes } } return output; } }; } // namespace mitk #endif /* ImageGenerator_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkImagePixelWriteAccessor.h b/Modules/Core/include/mitkImagePixelWriteAccessor.h index 25b3b20943..a4dbea561b 100644 --- a/Modules/Core/include/mitkImagePixelWriteAccessor.h +++ b/Modules/Core/include/mitkImagePixelWriteAccessor.h @@ -1,138 +1,138 @@ /*============================================================================ 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 MITKIMAGEPIXELWRITEACCESSOR_H #define MITKIMAGEPIXELWRITEACCESSOR_H #include "mitkImagePixelAccessor.h" #include "mitkImageWriteAccessor.h" namespace mitk { /** * @brief Gives locked and index-based write access for a particular image part. * The class provides several set- and get-methods, which allow an easy pixel access. * It needs to know about pixel type and dimension of its image at compile time. * @tparam TPixel defines the PixelType * @tparam VDimension defines the dimension for accessing data * @ingroup Data */ template class ImagePixelWriteAccessor : public ImagePixelAccessor { friend class Image; public: typedef ImagePixelAccessor ImagePixelAccessorType; typedef itk::SmartPointer ImagePointer; /** \brief Instantiates a mitk::ImageWriteAccessor (see its doxygen page for more details) * \param iP specifies the associated Image * \param iDI specifies the allocated image part * \param OptionFlags properties from mitk::ImageAccessorBase::Options can be chosen and assembled with bitwise * unification. * \throws mitk::Exception if the Constructor was created inappropriately * \throws mitk::MemoryIsLockedException if requested image area is exclusively locked and * mitk::ImageAccessorBase::ExceptionIfLocked is set in OptionFlags * * Includes a check if typeid of PixelType coincides with templated TPixel * and a check if VDimension equals to the Dimension of the Image. * * \note - * To avoid intermittent Update() calls to a predecessing filter pipeline, a call to Modifed() method after the + * To avoid intermittent Update() calls to a predecessing filter pipeline, a call to Modified() method after the * access is finished is left to the developer. */ ImagePixelWriteAccessor(ImagePointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP.GetPointer(), iDI), m_WriteAccessor(iP, iDI, OptionFlags) { } /** \brief Gives full data access. */ virtual inline TPixel *GetData() const { return static_cast(m_WriteAccessor.m_AddressBegin); } /// Sets a pixel value at given index. void SetPixelByIndex(const itk::Index &idx, const TPixel &value) { unsigned int offset = ImagePixelAccessor::GetOffset(idx); *(((TPixel *)m_WriteAccessor.m_AddressBegin) + offset) = value; } /** Extends SetPixel by integrating index validation to prevent overflow. */ void SetPixelByIndexSafe(const itk::Index &idx, const TPixel &value) { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel *targetAddress = ((TPixel *)m_WriteAccessor.m_AddressBegin) + offset; if (targetAddress >= m_WriteAccessor.m_AddressBegin && targetAddress < m_WriteAccessor.m_AddressEnd) { *targetAddress = value; } else { // printf("image dimensions = %d, %d, %d\n", imageDims[0], imageDims[1], imageDims[2]); // printf("m_AddressBegin: %p, m_AddressEnd: %p, offset: %u\n", m_WriteAccessor.m_AddressBegin, // m_WriteAccessor.m_AddressEnd, offset); mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } } /** Returns a const reference to the pixel at given index. */ const TPixel &GetPixelByIndex(const itk::Index &idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); return *(((TPixel *)m_WriteAccessor.m_AddressBegin) + offset); } /** Extends GetPixel by integrating index validation to prevent overflow. * \throws mitk::Exception in case of overflow */ const TPixel &GetPixelByIndexSafe(const itk::Index &idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel *targetAddress = ((TPixel *)m_WriteAccessor.m_AddressBegin) + offset; if (!(targetAddress >= m_WriteAccessor.m_AddressBegin && targetAddress < m_WriteAccessor.m_AddressEnd)) { mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } return *targetAddress; } /** Returns a const reference to the pixel at given world coordinate - works only with three-dimensional * ImageAccessor */ const TPixel &GetPixelByWorldCoordinates(mitk::Point3D position) { itk::Index<3> itkIndex; m_WriteAccessor.GetImage()->GetGeometry()->WorldToIndex(position, itkIndex); return GetPixelByIndex(itkIndex); } /** Returns a reference to the pixel at given world coordinate */ void SetPixelByWorldCoordinates(const mitk::Point3D &, const TPixel &value, unsigned int timestep = 0); ~ImagePixelWriteAccessor() override {} private: ImageWriteAccessor m_WriteAccessor; ImagePixelWriteAccessor &operator=(const ImagePixelWriteAccessor &); // Not implemented on purpose. ImagePixelWriteAccessor(const ImagePixelWriteAccessor &); }; } #endif // MITKIMAGEWRITEACCESSOR_H diff --git a/Modules/Core/include/mitkImageSource.h b/Modules/Core/include/mitkImageSource.h index 52461c8159..1d886ec7b8 100644 --- a/Modules/Core/include/mitkImageSource.h +++ b/Modules/Core/include/mitkImageSource.h @@ -1,251 +1,251 @@ /*============================================================================ 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 IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC #define IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC #include "mitkBaseDataSource.h" #include "mitkImage.h" #include namespace mitk { /** * @brief Superclass of all classes generating Images (instances of class * Image) as output. * * In itk and vtk the generated result of a ProcessObject is only guaranteed * to be up-to-date, when Update() of the ProcessObject or the generated * DataObject is called immediately before access of the data stored in the * DataObject. This is also true for subclasses of mitk::BaseProcess and thus * for mitk::ImageSource. But there are also three access methods provided * that guarantee an up-to-date result (by first calling Update and then * returning the result of GetOutput()): GetData(), GetPic() and * GetVtkImageData(). * @ingroup Process */ class MITKCORE_EXPORT ImageSource : public BaseDataSource { public: mitkClassMacro(ImageSource, BaseDataSource); /** @brief Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** @brief Some convenient typedefs. */ typedef mitk::Image OutputImageType; typedef OutputImageType OutputType; typedef OutputImageType::Pointer OutputImagePointer; typedef SlicedData::RegionType OutputImageRegionType; /** * @brief Get the output data of this image source object. * * The output of this * function is not valid until an appropriate Update() method has * been called, either explicitly or implicitly. Both the filter * itself and the data object have Update() methods, and both * methods update the data. Here are three ways to use * GetOutput() and make sure the data is valid. In these * examples, \a image is a pointer to some Image object, and the * particular ProcessObjects involved are filters. The same * examples apply to non-image (e.g. Mesh) data as well. * * \code * anotherFilter->SetInput( someFilter->GetOutput() ); * anotherFilter->Update(); * \endcode * * In this situation, \a someFilter and \a anotherFilter are said * to constitute a \b pipeline. * * \code * image = someFilter->GetOutput(); * image->Update(); * \endcode * * \code * someFilter->Update(); * image = someFilter->GetOutput(); * \endcode * (In the above example, the two lines of code can be in * either order.) * * Note that Update() is not called automatically except within a * pipeline as in the first example. When \b streaming (using a * StreamingImageFilter) is activated, it may be more efficient to * use a pipeline than to call Update() once for each filter in * turn. * * For an image, the data generated is for the requested * Region, which can be set using ImageBase::SetRequestedRegion(). * By default, the largest possible region is requested. * * For Filters which have multiple outputs of different types, the * GetOutput() method assumes the output is of OutputImageType. For * the GetOutput(DataObjectPointerArraySizeType) method, a dynamic_cast is performed * incase the filter has outputs of different types or image * types. Derived classes should have named get methods for these * outputs. */ mitkBaseDataSourceGetOutputDeclarations /** @brief Make a DataObject of the correct type to used as the specified * output. * * Every ProcessObject subclass must be able to create a * DataObject that can be used as a specified output. This method * is automatically called when DataObject::DisconnectPipeline() is * called. DataObject::DisconnectPipeline, disconnects a data object * from being an output of its current source. When the data object * is disconnected, the ProcessObject needs to construct a replacement * output data object so that the ProcessObject is in a valid state. * So DataObject::DisconnectPipeline eventually calls * ProcessObject::MakeOutput. Note that MakeOutput always returns a * SmartPointer to a DataObject. If a subclass of ImageSource has * multiple outputs of different types, then that class must provide * an implementation of MakeOutput(). */ itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override; /** * This is a default implementation to make sure we have something. - * Once all the subclasses of ProcessObject provide an appopriate + * Once all the subclasses of ProcessObject provide an appropriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; virtual vtkImageData *GetVtkImageData(); virtual const vtkImageData *GetVtkImageData() const; protected: ImageSource(); ~ImageSource() override {} /** @brief A version of GenerateData() specific for image processing * filters. * * This implementation will split the processing across * multiple threads. The buffer is allocated by this method. Then * the BeforeThreadedGenerateData() method is called (if * provided). Then, a series of threads are spawned each calling * ThreadedGenerateData(). After all the threads have completed * processing, the AfterThreadedGenerateData() method is called (if * provided). If an image processing filter cannot be threaded, the * filter should provide an implementation of GenerateData(). That * implementation is responsible for allocating the output buffer. * If a filter an be threaded, it should NOT provide a * GenerateData() method but should provide a ThreadedGenerateData() * instead. * * \sa ThreadedGenerateData() */ void GenerateData() override; /** @brief If an imaging filter can be implemented as a multithreaded * algorithm, the filter will provide an implementation of * ThreadedGenerateData(). * * This superclass will automatically split * the output image into a number of pieces, spawn multiple threads, * and call ThreadedGenerateData() in each thread. Prior to spawning * threads, the BeforeThreadedGenerateData() method is called. After * all the threads have completed, the AfterThreadedGenerateData() * method is called. If an image processing filter cannot support * threading, that filter should provide an implementation of the * GenerateData() method instead of providing an implementation of * ThreadedGenerateData(). If a filter provides a GenerateData() * method as its implementation, then the filter is responsible for * allocating the output data. If a filter provides a * ThreadedGenerateData() method as its implementation, then the * output memory will allocated automatically by this superclass. * The ThreadedGenerateData() method should only produce the output * specified by "outputThreadRegion" * parameter. ThreadedGenerateData() cannot write to any other * portion of the output image (as this is responsibility of a * different thread). * * \sa GenerateData(), SplitRequestedRegion() */ virtual void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId); /** @brief This method is intentionally left blank. * * ImageSource's need not * Initialize their containers. The Image::Allocate() method (called * from GenerateData()) will resize the container if more memory is * needed. Otherwise, the memory can be reused. */ void PrepareOutputs() override; /** @brief The GenerateData method normally allocates the buffers for all of the * outputs of a filter. * * Some filters may want to override this default * behavior. For example, a filter may have multiple outputs with * varying resolution. Or a filter may want to process data in place by * grafting its input to its output.*/ virtual void AllocateOutputs(); /** @brief If an imaging filter needs to perform processing after the buffer * has been allocated but before threads are spawned, the filter can * can provide an implementation for BeforeThreadedGenerateData(). * * The execution flow in the default GenerateData() method will be: * 1) Allocate the output buffer * 2) Call BeforeThreadedGenerateData() * 3) Spawn threads, calling ThreadedGenerateData() in each thread. * 4) Call AfterThreadedGenerateData() * Note that this flow of control is only available if a filter provides * a ThreadedGenerateData() method and NOT a GenerateData() method. */ virtual void BeforeThreadedGenerateData() {} /** @brief If an imaging filter needs to perform processing after all * processing threads have completed, the filter can can provide an * implementation for AfterThreadedGenerateData(). * * The execution * flow in the default GenerateData() method will be: * 1) Allocate the output buffer * 2) Call BeforeThreadedGenerateData() * 3) Spawn threads, calling ThreadedGenerateData() in each thread. * 4) Call AfterThreadedGenerateData() * Note that this flow of control is only available if a filter provides * a ThreadedGenerateData() method and NOT a GenerateData() method. */ virtual void AfterThreadedGenerateData() {} /** @brief Split the output's RequestedRegion into "num" pieces, returning * region "i" as "splitRegion". * * This method is called "num" times. The * regions must not overlap. The method returns the number of pieces that * the routine is capable of splitting the output RequestedRegion, * i.e. return value is less than or equal to "num". */ virtual unsigned int SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion); /** @brief Static function used as a "callback" by the MultiThreader. * * The threading library will call this routine for each thread, which will delegate the * control to ThreadedGenerateData(). */ static itk::ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg); /** @brief Internal structure used for passing image data into the threading library */ struct ThreadStruct { Pointer Filter; }; private: ImageSource(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // namespace mitk #endif /* IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC */ diff --git a/Modules/Core/include/mitkImageStatisticsHolder.h b/Modules/Core/include/mitkImageStatisticsHolder.h index c6b2a5387d..e7c5e66b8d 100644 --- a/Modules/Core/include/mitkImageStatisticsHolder.h +++ b/Modules/Core/include/mitkImageStatisticsHolder.h @@ -1,176 +1,176 @@ /*============================================================================ 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 MITKIMAGESTATISTICSHOLDER_H #define MITKIMAGESTATISTICSHOLDER_H #include "mitkImage.h" #include "mitkImageTimeSelector.h" #include #ifndef __itkHistogram_h #include #endif namespace mitk { /** - @brief Class holding the statistics informations about a single mitk::Image + @brief Class holding the statistics information about a single mitk::Image This computation was previously directly included in the definition and implementation of the mitk::Image class but for having a clear interface, all statistics computation is moved to the ImageStatisticsHolder class. Each mitk::Image holds a normal pointer to its StatisticsHolder object. To get access to the methods, use the GetStatistics() method in mitk::Image class. Minimum or maximum might by infinite values. 2nd minimum and maximum are guaranteed to be finite values. */ class MITKCORE_EXPORT ImageStatisticsHolder { public: /** Constructor */ ImageStatisticsHolder(mitk::Image *image); - /** Desctructor */ + /** Destructor */ virtual ~ImageStatisticsHolder(); typedef itk::Statistics::Histogram HistogramType; virtual const HistogramType *GetScalarHistogram(int t = 0, unsigned int = 0); //##Documentation //## \brief Get the minimum for scalar images. Recomputation performed only when necessary. virtual ScalarType GetScalarValueMin(int t = 0, unsigned int component = 0); //##Documentation //## \brief Get the maximum for scalar images. Recomputation performed only when necessary. virtual ScalarType GetScalarValueMax(int t = 0, unsigned int component = 0); //##Documentation //## \brief Get the second smallest value for scalar images. //## Recomputation performed only when necessary. //## \post The returned value is always a finite value. virtual ScalarType GetScalarValue2ndMin(int t = 0, unsigned int component = 0); //##Documentation //## \brief Get the smallest value for scalar images, but do not recompute it first virtual mitk::ScalarType GetScalarValueMinNoRecompute(unsigned int t = 0) const { if (t < m_ScalarMin.size()) return m_ScalarMin[t]; else return itk::NumericTraits::max(); } //##Documentation //## \brief Get the second smallest value for scalar images, but do not recompute it first //## \post The returned value is always a finite value. virtual mitk::ScalarType GetScalarValue2ndMinNoRecompute(unsigned int t = 0) const { if (t < m_Scalar2ndMin.size()) return m_Scalar2ndMin[t]; else return itk::NumericTraits::max(); } //##Documentation //## \brief Get the second largest value for scalar images //## \post The returned value is always a finite value. virtual ScalarType GetScalarValue2ndMax(int t = 0, unsigned int component = 0); //##Documentation //## \brief Get the largest value for scalar images, but do not recompute it first //## \post The returned value is always a finite value. virtual mitk::ScalarType GetScalarValueMaxNoRecompute(unsigned int t = 0) { if (t < m_ScalarMax.size()) return m_ScalarMax[t]; else return itk::NumericTraits::NonpositiveMin(); } //##Documentation //## \brief Get the second largest value for scalar images, but do not recompute it first virtual mitk::ScalarType GetScalarValue2ndMaxNoRecompute(unsigned int t = 0) { if (t < m_Scalar2ndMax.size()) return m_Scalar2ndMax[t]; else return itk::NumericTraits::NonpositiveMin(); } //##Documentation //## \brief Get the count of voxels with the smallest scalar value in the dataset mitk::ScalarType GetCountOfMinValuedVoxels(int t = 0, unsigned int component = 0); //##Documentation //## \brief Get the count of voxels with the largest scalar value in the dataset mitk::ScalarType GetCountOfMaxValuedVoxels(int t = 0, unsigned int component = 0); //##Documentation //## \brief Get the count of voxels with the largest scalar value in the dataset virtual unsigned int GetCountOfMaxValuedVoxelsNoRecompute(unsigned int t = 0) { if (t < m_CountOfMaxValuedVoxels.size()) return m_CountOfMaxValuedVoxels[t]; else return 0; } //##Documentation //## \brief Get the count of voxels with the smallest scalar value in the dataset virtual unsigned int GetCountOfMinValuedVoxelsNoRecompute(unsigned int t = 0) const { if (t < m_CountOfMinValuedVoxels.size()) return m_CountOfMinValuedVoxels[t]; else return 0; } bool IsValidTimeStep(int t) const; template friend void _ComputeExtremaInItkImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t); template friend void _ComputeExtremaInItkVectorImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t, unsigned int component); protected: virtual void ResetImageStatistics(); virtual void ComputeImageStatistics(int t = 0, unsigned int component = 0); virtual void Expand(unsigned int timeSteps); ImageTimeSelector::Pointer GetTimeSelector(); mitk::Image *m_Image; mutable itk::Object::Pointer m_HistogramGeneratorObject; mutable itk::Object::Pointer m_TimeSelectorForExtremaObject; mutable std::vector m_CountOfMinValuedVoxels; mutable std::vector m_CountOfMaxValuedVoxels; mutable std::vector m_ScalarMin; mutable std::vector m_ScalarMax; mutable std::vector m_Scalar2ndMin; mutable std::vector m_Scalar2ndMax; itk::TimeStamp m_LastRecomputeTimeStamp; }; } // end namespace #endif // MITKIMAGESTATISTICSHOLDER_H diff --git a/Modules/Core/include/mitkImageToImageFilter.h b/Modules/Core/include/mitkImageToImageFilter.h index 199d7c2bdf..a0dc60c25f 100644 --- a/Modules/Core/include/mitkImageToImageFilter.h +++ b/Modules/Core/include/mitkImageToImageFilter.h @@ -1,80 +1,80 @@ /*============================================================================ 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 IMAGETOIMAGEFILTER_H_HEADER_INCLUDED_C1E5E869 #define IMAGETOIMAGEFILTER_H_HEADER_INCLUDED_C1E5E869 #include "mitkImageSource.h" #include namespace mitk { //##Documentation //## @brief Superclass of all classes having one or more Images as input and //## generating Images as output //## @ingroup Process class MITKCORE_EXPORT ImageToImageFilter : public ImageSource { public: mitkClassMacro(ImageToImageFilter, ImageSource); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Superclass typedefs. */ typedef Superclass::OutputImageRegionType OutputImageRegionType; /** Some convenient typedefs. */ typedef mitk::Image InputImageType; typedef InputImageType::Pointer InputImagePointer; typedef InputImageType::ConstPointer InputImageConstPointer; typedef SlicedData::RegionType InputImageRegionType; using itk::ProcessObject::SetInput; /** Set/Get the image input of this process object. */ virtual void SetInput(const InputImageType *image); virtual void SetInput(unsigned int, const InputImageType *image); InputImageType *GetInput(void); InputImageType *GetInput(unsigned int idx); const InputImageType *GetInput(void) const; const InputImageType *GetInput(unsigned int idx) const; protected: ImageToImageFilter(); ~ImageToImageFilter() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; /** What is the input requested region that is required to produce the * output requested region? The base assumption for image processing * filters is that the input requested region can be set to match the * output requested region. If a filter requires more input (for instance * a filter that uses neighborhoods needs more input than output to avoid * introducing artificial boundary conditions) or less input (for instance * a magnify filter) will have to override this method. In doing so, it * should call its superclass' implementation as its first step. Note that * this imaging filters operate differently than the classes to this - * point in the class hierachy. Up till now, the base assumption has been + * point in the class hierarchy. Up till now, the base assumption has been * that the largest possible region will be requested of the input. * * \sa ProcessObject::GenerateInputRequestedRegion(), * ImageSource::GenerateInputRequestedRegion() */ void GenerateInputRequestedRegion() override; private: void operator=(const Self &); // purposely not implemented }; } // namespace mitk #endif /* IMAGETOIMAGEFILTER_H_HEADER_INCLUDED_C1E5E869 */ diff --git a/Modules/Core/include/mitkImageToItk.txx b/Modules/Core/include/mitkImageToItk.txx index 29abe89a77..b765b5a2de 100644 --- a/Modules/Core/include/mitkImageToItk.txx +++ b/Modules/Core/include/mitkImageToItk.txx @@ -1,290 +1,290 @@ /*============================================================================ 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 IMAGETOITK_TXX_INCLUDED_C1C2FCD2 #define IMAGETOITK_TXX_INCLUDED_C1C2FCD2 #include "itkImportMitkImageContainer.h" #include "mitkBaseProcess.h" #include "mitkException.h" #include "mitkImageReadAccessor.h" #include "mitkImageToItk.h" #include "mitkImageWriteAccessor.h" #include template struct SetLengthHelper { SetLengthHelper(TImageType *in) { m_Image = in; } private: TImageType *m_Image; }; template struct SetLengthHelper> { typedef itk::Image TImageType; SetLengthHelper(TImageType *in) { m_Image = in; } void SetVectorLength(size_t) {} private: TImageType *m_Image; }; template struct SetLengthHelper> { typedef itk::VectorImage TImageType; SetLengthHelper(TImageType *in) { m_Image = in; } void SetVectorLength(size_t len) { m_Image->SetVectorLength(len); } private: TImageType *m_Image; }; template void mitk::ImageToItk::SetInput(mitk::Image *input) { this->SetInput(static_cast(input)); m_ConstInput = false; } template void mitk::ImageToItk::SetInput(const mitk::Image *input) { this->CheckInput(input); // Process object is not const-correct so the const_cast is required here itk::ProcessObject::PushFrontInput(input); m_ConstInput = true; } template mitk::Image *mitk::ImageToItk::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(itk::ProcessObject::GetInput(0)); } template const mitk::Image *mitk::ImageToItk::GetInput() const { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(itk::ProcessObject::GetInput(0)); } template void mitk::ImageToItk::GenerateData() { // Allocate output mitk::Image::Pointer input = this->GetInput(); typename Superclass::OutputImageType::Pointer output = this->GetOutput(); unsigned long noBytes = input->GetDimension(0); for (unsigned int i = 1; i < TOutputImage::GetImageDimension(); ++i) { noBytes = noBytes * input->GetDimension(i); } const mitk::PixelType pixelType = input->GetPixelType(); if (pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR) { noBytes *= pixelType.GetNumberOfComponents(); SetLengthHelper helper(output.GetPointer()); helper.SetVectorLength(pixelType.GetNumberOfComponents()); } std::unique_ptr imageAccess; if (m_ConstInput) { imageAccess.reset(new mitk::ImageReadAccessor(input, nullptr, m_Options)); } else { imageAccess.reset(new mitk::ImageWriteAccessor(input, nullptr, m_Options)); } // hier wird momentan wohl nur der erste Channel verwendet??!! if (imageAccess->GetData() == nullptr) { itkWarningMacro(<< "no image data to import in ITK image"); RegionType bufferedRegion; output->SetBufferedRegion(bufferedRegion); return; } if (m_CopyMemFlag) { itkDebugMacro("copyMem ..."); output->Allocate(); memcpy(output->GetBufferPointer(), imageAccess->GetData(), sizeof(InternalPixelType) * noBytes); } else { itkDebugMacro("do not copyMem ..."); typedef itk::ImportMitkImageContainer ImportContainerType; typename ImportContainerType::Pointer import; import = ImportContainerType::New(); import->Initialize(); itkDebugMacro(<< "size of container = " << import->Size()); // import->SetImageDataItem(m_ImageDataItem); import->SetImageAccessor(imageAccess.release(), sizeof(InternalPixelType) * noBytes); output->SetPixelContainer(import); itkDebugMacro(<< "size of container = " << import->Size()); } } template void mitk::ImageToItk::UpdateOutputInformation() { mitk::Image::Pointer input = this->GetInput(); if (input.IsNotNull() && (input->GetSource().IsNotNull()) && input->GetSource()->Updating()) { typename Superclass::OutputImageType::Pointer output = this->GetOutput(); unsigned long t1 = input->GetUpdateMTime() + 1; if (t1 > this->m_OutputInformationMTime.GetMTime()) { output->SetPipelineMTime(t1); this->GenerateOutputInformation(); this->m_OutputInformationMTime.Modified(); } return; } Superclass::UpdateOutputInformation(); } template void mitk::ImageToItk::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); typename Superclass::OutputImageType::Pointer output = this->GetOutput(); // allocate size, origin, spacing, direction in types of output image SizeType size; const unsigned int itkDimMin3 = (TOutputImage::ImageDimension > 3 ? TOutputImage::ImageDimension : 3); const unsigned int itkDimMax3 = (TOutputImage::ImageDimension < 3 ? TOutputImage::ImageDimension : 3); typename Superclass::OutputImageType::PointType::ValueType origin[itkDimMin3]; typename Superclass::OutputImageType::SpacingType::ComponentType spacing[itkDimMin3]; typename Superclass::OutputImageType::DirectionType direction; // copy as much information as possible into size and spacing unsigned int i; for (i = 0; i < itkDimMax3; ++i) { size[i] = input->GetDimension(i); spacing[i] = input->GetGeometry()->GetSpacing()[i]; } for (; i < TOutputImage::ImageDimension; ++i) { origin[i] = 0.0; size[i] = input->GetDimension(i); spacing[i] = 1.0; } // build region from size IndexType start; start.Fill(0); RegionType region; region.SetIndex(start); region.SetSize(size); // copy as much information as possible into origin const mitk::Point3D &mitkorigin = input->GetGeometry()->GetOrigin(); itk2vtk(mitkorigin, origin); // copy as much information as possible into direction direction.SetIdentity(); unsigned int j; const AffineTransform3D::MatrixType &matrix = input->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); /// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix. /// If it is only a rotation around the axial plane normal, it can be express with a 2x2 matrix. /// In this case, the ITK image conservs this information and is identical to the MITK image! /// If the MITK image contains any other rotation, the ITK image will have no rotation at all. /// Spacing is of course conserved in both cases. - // the following loop devides by spacing now to normalize columns. + // the following loop divides by spacing now to normalize columns. // counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092. // Check if information is lost if (TOutputImage::ImageDimension <= 2) { if ((TOutputImage::ImageDimension == 2) && ((matrix[0][2] != 0) || (matrix[1][2] != 0) || (matrix[2][0] != 0) || (matrix[2][1] != 0) || ((matrix[2][2] != 1) && (matrix[2][2] != -1)))) { // The 2D MITK image contains 3D rotation information. // This cannot be expressed in a 2D ITK image, so the ITK image will have no rotation } else { // The 2D MITK image can be converted to an 2D ITK image without information loss! for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) direction[i][j] = matrix[i][j] / spacing[j]; } } else { // Normal 3D image. Conversion possible without problem! for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) direction[i][j] = matrix[i][j] / spacing[j]; } // set information into output image output->SetRegions(region); output->SetOrigin(origin); output->SetSpacing(spacing); output->SetDirection(direction); } template void mitk::ImageToItk::CheckInput(const mitk::Image *input) const { if (input == nullptr) { itkExceptionMacro(<< "image is null"); } if (input->GetDimension() != TOutputImage::GetImageDimension()) { itkExceptionMacro(<< "image has dimension " << input->GetDimension() << " instead of " << TOutputImage::GetImageDimension()); } if (!(input->GetPixelType() == mitk::MakePixelType(input->GetPixelType().GetNumberOfComponents()))) { itkExceptionMacro(<< "image has wrong pixel type "); } } template void mitk::ImageToItk::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } #endif // IMAGETOITK_TXX_INCLUDED_C1C2FCD2 diff --git a/Modules/Core/include/mitkImageToSurfaceFilter.h b/Modules/Core/include/mitkImageToSurfaceFilter.h index 01c0d93c22..937c76a07b 100644 --- a/Modules/Core/include/mitkImageToSurfaceFilter.h +++ b/Modules/Core/include/mitkImageToSurfaceFilter.h @@ -1,247 +1,247 @@ /*============================================================================ 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 _MITKIMAGETOSURFACEFILTER_h__ #define _MITKIMAGETOSURFACEFILTER_h__ #include "MitkCoreExports.h" #include #include #include #include #include #include #include #include namespace mitk { /** * @brief Converts pixel data to surface data by using a threshold * The mitkImageToSurfaceFilter is used to create a new surface out of an mitk image. The filter * uses a threshold to define the surface. It is based on the vtkMarchingCube algorithm. By default * a vtkPolyData surface based on an input threshold for the input image will be created. Optional * it is possible to reduce the number of triangles/polygones [SetDecimate(mitk::ImageToSurfaceFilter::DecimatePro) and * SetTargetReduction (float _arg)] * or smooth the surface-data [SetSmooth(true), SetSmoothIteration(int smoothIteration) and SetSmoothRelaxation(float * smoothRelaxation)]. * * The resulting vtk-surface has the same size as the input image. The surface * can be generally smoothed by vtkDecimatePro reduce complexity of triangles * and vtkSmoothPolyDataFilter to relax the mesh. Both are enabled by default * and connected in the common way of pipelining in ITK. It's also possible * to create time sliced surfaces. * * @ingroup ImageFilters * @ingroup Process */ class MITKCORE_EXPORT ImageToSurfaceFilter : public SurfaceSource { public: /* * To decide whether a reduction of polygons in the created surface shall be * done or not by using the vtkDecimatePro Filter. Till vtk 4.x an vtkDecimateFilter existed, - * but was patented. So since vtk 5.x it was replaced by the (worser?) vtkDecimateProFilter + * but was patented. So since vtk 5.x it was replaced by the (much worse?) vtkDecimateProFilter * Maybe another Filter will come soon. */ enum DecimationType { NoDecimation, DecimatePro, QuadricDecimation }; mitkClassMacro(ImageToSurfaceFilter, SurfaceSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * For each image time slice a surface will be created. This method is * called by Update(). */ void GenerateData() override; /** * Initializes the output information ( i.e. the geometry information ) of * the output of the filter */ void GenerateOutputInformation() override; /** - * Returns a const reference to the input image (e.g. the original input image that ist used to create the surface) + * Returns a const reference to the input image (e.g. the original input image that is used to create the surface) */ const mitk::Image *GetInput(void); /** * Set the source image to create a surface for this filter class. As input every mitk * 3D or 3D+t image can be used. */ using itk::ProcessObject::SetInput; virtual void SetInput(const mitk::Image *image); /** * Set the number of iterations that is used to smooth the surface. Used is the vtkSmoothPolydataFilter that uses * the * laplacian filter. The higher the number of iterations that stronger the smooth-result * - * @param smoothIteration As smoothIteration default in that case 50 was choosen. The VTK documentation recommends + * @param smoothIteration As smoothIteration default in that case 50 was chosen. The VTK documentation recommends * small relaxation factors and large numbers of iterations. */ void SetSmoothIteration(int smoothIteration); /** * Set number of relaxation. Specify the relaxation factor for Laplacian * smoothing. The VTK documentation recommends small relaxation factors * and large numbers of iterations. * - * @param smoothRelaxation As smoothRelaxation default in that case 0.1 was choosen. The VTK documentation + * @param smoothRelaxation As smoothRelaxation default in that case 0.1 was chosen. The VTK documentation * recommends * small relaxation factors and large numbers of iterations. */ void SetSmoothRelaxation(float smoothRelaxation); /** * Threshold that is used to create the surface. All pixel in the input image that are higher than that * value will be considered in the surface. The threshold referees to * vtkMarchingCube. Default value is 1. See also SetThreshold (ScalarType _arg) */ itkSetMacro(Threshold, ScalarType); /** * Get Threshold from vtkMarchingCube. Threshold can be manipulated by * inherited classes. */ itkGetConstMacro(Threshold, ScalarType); /** * Enables vtkSmoothPolyDataFilter. With Laplacian smoothing this filter * will relax the surface. You can control the Filter by manipulating the * number of iterations and the relaxing factor. * */ itkSetMacro(Smooth, bool); /* * Enable/Disable surface smoothing. */ itkBooleanMacro(Smooth); /* * Returns if surface smoothing is enabled */ itkGetConstMacro(Smooth, bool); /** * Get the state of decimation mode to reduce triangle in the - * surface represantation. Modes can only be NoDecimation or DecimatePro + * surface representation. Modes can only be NoDecimation or DecimatePro * (till vtk 4.x also Decimate) * */ itkGetConstMacro(Decimate, DecimationType); /** * Enable the decimation filter to reduce the number of triangles in the * mesh and produce a good approximation to the original image. The filter * has support for vtk-5 and earlier versions. More detailed information * check the vtkDecimatePro and vtkDecimate. * */ itkSetMacro(Decimate, DecimationType); /** * Set desired TargetReduction of triangles in the range from 0.0 to 1.0. * The destroyed triangles are in relation with the size of data. For example 0.9 * will reduce the data set to 10%. */ itkSetMacro(TargetReduction, float); /** * Returns the reduction factor for the VtkDecimatePro Decimation Filter as a float value */ itkGetConstMacro(TargetReduction, float); /** * Transforms a point by a 4x4 matrix */ template inline void mitkVtkLinearTransformPoint(T1 matrix[4][4], T2 in[3], T3 out[3]) { T3 x = matrix[0][0] * in[0] + matrix[0][1] * in[1] + matrix[0][2] * in[2] + matrix[0][3]; T3 y = matrix[1][0] * in[0] + matrix[1][1] * in[1] + matrix[1][2] * in[2] + matrix[1][3]; T3 z = matrix[2][0] * in[0] + matrix[2][1] * in[1] + matrix[2][2] * in[2] + matrix[2][3]; out[0] = x; out[1] = y; out[2] = z; } protected: ImageToSurfaceFilter(); /** * Destructor * */ ~ImageToSurfaceFilter() override; /** * With the given threshold vtkMarchingCube creates the surface. By default nothing a * vtkPolyData surface based on a threshold of the input image will be created. Optional * it is possible to reduce the number of triangles/polygones [SetDecimate(mitk::ImageToSurfaceFilter::DecimatePro) * and * SetTargetReduction (float _arg)] * or smooth the data [SetSmooth(true), SetSmoothIteration(int smoothIteration) and SetSmoothRelaxation(float * smoothRelaxation)]. * * @param time selected slice or "0" for single * @param *vtkimage input image * @param *surface output * @param threshold can be different from SetThreshold() */ void CreateSurface(int time, vtkImageData *vtkimage, mitk::Surface *surface, const ScalarType threshold); /** * Flag whether the created surface shall be smoothed or not (default is "false"). SetSmooth (bool _arg) * */ bool m_Smooth; /** * Decimation mode, default mode is "NoDecimation". See also SetDecimate (DecimationType _arg) * */ DecimationType m_Decimate; /** * Threshold that is used to create the surface. All pixel in the input image that are higher than that * value will be considered in the surface. Default value is 1. See also SetThreshold (ScalarType _arg) * */ ScalarType m_Threshold; /** * The Reduction factor of the Decimation Filter for the created surface. See also SetTargetReduction (float _arg) * */ float m_TargetReduction; /** * The Iteration value for the Smooth Filter of the created surface. See also SetSmoothIteration (int * smoothIteration) * */ int m_SmoothIteration; /** * The Relaxation value for the Smooth Filter of the created surface. See also SetSmoothRelaxation (float * smoothRelaxation) * */ float m_SmoothRelaxation; }; } // namespace mitk #endif //_MITKIMAGETOSURFACEFILTER_h__ diff --git a/Modules/Core/include/mitkImageVtkMapper2D.h b/Modules/Core/include/mitkImageVtkMapper2D.h index 52d3efc792..d1ee542a8f 100644 --- a/Modules/Core/include/mitkImageVtkMapper2D.h +++ b/Modules/Core/include/mitkImageVtkMapper2D.h @@ -1,321 +1,321 @@ /*============================================================================ 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 MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E #define MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E // MITK #include // MITK Rendering #include "mitkBaseRenderer.h" #include "mitkExtractSliceFilter.h" #include "mitkVtkMapper.h" // VTK #include #include class vtkActor; class vtkPolyDataMapper; class vtkPlaneSource; class vtkImageData; class vtkLookupTable; class vtkImageExtractComponents; class vtkImageReslice; class vtkImageChangeInformation; class vtkPoints; class vtkMitkThickSlicesFilter; class vtkPolyData; class vtkMitkApplyLevelWindowToRGBFilter; class vtkMitkLevelWindowFilter; namespace mitk { /** \brief Mapper to resample and display 2D slices of a 3D image. * * The following image gives a brief overview of the mapping and the involved parts. * * \image html imageVtkMapper2Darchitecture.png * * First, the image is resliced by means of vtkImageReslice. The volume image * serves as input to the mapper in addition to spatial placement of the slice and a few other * properties such as thick slices. This code was already present in the old version * (mitkImageMapperGL2D). * * Next, the obtained slice (m_ReslicedImage) is put into a vtkMitkLevelWindowFilter * and the scalar levelwindow, opacity levelwindow and optional clipping to * local image bounds are applied * * Next, the output of the vtkMitkLevelWindowFilter is used to create a texture * (m_Texture) and a plane onto which the texture is rendered (m_Plane). For * mapping purposes, a vtkPolyDataMapper (m_Mapper) is utilized. Orthographic * projection is applied to create the effect of a 2D image. The mapper and the * texture are assigned to the actor (m_Actor) which is passed to the VTK rendering * pipeline via the method GetVtkProp(). * * In order to transform the textured plane to the correct position in space, the * same transformation as used for reslicing is applied to both the camera and the * vtkActor. All important steps are explained in more detail below. The resulting * 2D image (by reslicing the underlying 3D input image appropriately) can either * be directly rendered in a 2D view or just be calculated to be used later by another * rendering entity, e.g. in texture mapping in a 3D view. * * Properties that can be set for images and influence the imageMapper2D are: * * - \b "opacity": (FloatProperty) Opacity of the image * - \b "color": (ColorProperty) Color of the image * - \b "LookupTable": (mitkLookupTableProperty) If this property is set, * the default lookuptable will be ignored and the "LookupTable" value * will be used instead. * - \b "Image Rendering.Mode": This property decides which mode is used to render images. (E.g. if a lookup table or a transferfunction is applied). Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink * - \b "Image Rendering.Transfer Function": (mitkTransferFunctionProperty) If this * property is set, a color transferfunction will be used to color the image. * - \b "binary": (BoolProperty) is the image a binary image or not * - \b "outline binary": (BoolProperty) show outline of the image or not * - \b "texture interpolation": (BoolProperty) texture interpolation of the image * - \b "reslice interpolation": (VtkResliceInterpolationProperty) reslice interpolation of the image * - \b "in plane resample extent by geometry": (BoolProperty) Do it or not * - \b "bounding box": (BoolProperty) Is the Bounding Box of the image shown or not * - \b "layer": (IntProperty) Layer of the image * - \b "volume annotation color": (ColorProperty) color of the volume annotation, TODO has to be reimplemented * - \b "volume annotation unit": (StringProperty) annotation unit as string (does not implicit convert the unit!) unit is ml or cm3, TODO has to be reimplemented * The default properties are: * - \b "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ) * - \b "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ) * - \b "binary", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ) * - \b "texture interpolation", mitk::BoolProperty::New( false ) ) * - \b "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ) * - \b "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ) * - \b "bounding box", mitk::BoolProperty::New( false ) ) * - \b "layer", mitk::IntProperty::New(10), renderer, overwrite) * - \b "Image Rendering.Transfer Function": Default color transfer function for CTs * - \b "LookupTable": Rainbow color. * If the modality-property is set for an image, the mapper uses modality-specific default properties, * e.g. color maps, if they are defined. * \ingroup Mapper */ class MITKCORE_EXPORT ImageVtkMapper2D : public VtkMapper { public: /** Standard class typedefs. */ mitkClassMacro(ImageVtkMapper2D, VtkMapper); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** \brief Get the Image to map */ const mitk::Image *GetInput(void); /** \brief Checks whether this mapper needs to update itself and generate * data. */ void Update(mitk::BaseRenderer *renderer) override; //### methods of MITK-VTK rendering pipeline vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; //### end of methods of MITK-VTK rendering pipeline /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ /** * To render axial, coronal, and sagittal, the mapper is called three times. * For performance reasons, the corresponding data for each view is saved in the * internal helper class LocalStorage. This allows rendering n views with just * 1 mitkMapper using n vtkMapper. * */ class MITKCORE_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /** \brief Actor of the image in a 2D render window. */ vtkSmartPointer m_ImageActor; /** \brief Actor of the shadowimage in a 2D render window. */ vtkSmartPointer m_ShadowOutlineActor; - /** Prop assembly containting everything for a regular display of the image.*/ + /** Prop assembly containing everything for a regular display of the image.*/ vtkSmartPointer m_Actors; /** Prop assembly used if workspace is in an invalid state (e.g. invalid time point or * invalid world coordinate position is selected) and mapper has to "early out" * in Update() or GenerateDataForRenderer()*/ vtkSmartPointer m_EmptyActors; /** Prop assembly exposed publicly via ImagVtkMapper2D::GetVTKProp()*/ vtkProp* m_PublicActors = nullptr; /** \brief Mapper of a 2D render window. */ vtkSmartPointer m_Mapper; vtkSmartPointer m_VectorComponentExtractor; /** \brief Current slice of a 2D render window.*/ vtkSmartPointer m_ReslicedImage; /** \brief Empty vtkPolyData that is set when rendering geometry does not * intersect the image geometry. * \warning This member variable is set to nullptr, * if no image geometry is inside the plane geometry * of the respective render window. Any user of this * slice has to check whether it is set to nullptr! */ vtkSmartPointer m_EmptyPolyData; /** \brief Plane on which the slice is rendered as texture. */ vtkSmartPointer m_Plane; /** \brief The texture which is used to render the current slice. */ vtkSmartPointer m_Texture; /** \brief The lookuptables for colors and level window */ vtkSmartPointer m_DefaultLookupTable; vtkSmartPointer m_BinaryLookupTable; vtkSmartPointer m_ColorLookupTable; /** \brief The actual reslicer (one per renderer) */ mitk::ExtractSliceFilter::Pointer m_Reslicer; /** \brief Filter for thick slices */ vtkSmartPointer m_TSFilter; - /** \brief PolyData object containg all lines/points needed for outlining the contour. + /** \brief PolyData object containing all lines/points needed for outlining the contour. This container is used to save a computed contour for the next rendering execution. For instance, if you zoom or pann, there is no need to recompute the contour. */ vtkSmartPointer m_OutlinePolyData; /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastUpdateTime; /** \brief mmPerPixel relation between pixel and mm. (World spacing).*/ mitk::ScalarType *m_mmPerPixel; /** \brief This filter is used to apply the level window to Grayvalue and RBG(A) images. */ vtkSmartPointer m_LevelWindowFilter; /** \brief Default constructor of the local storage. */ LocalStorage(); /** \brief Default deconstructor of the local storage. */ ~LocalStorage() override; }; /** \brief Get the LocalStorage corresponding to the current renderer. */ const LocalStorage *GetConstLocalStorage(mitk::BaseRenderer *renderer); /** \brief Set the default properties for general image rendering. */ static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function). * Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink */ void ApplyRenderingMode(mitk::BaseRenderer *renderer); protected: /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; /** \brief Get the LocalStorage corresponding to the current renderer. */ LocalStorage* GetLocalStorage(mitk::BaseRenderer* renderer); /** \brief Transforms the actor to the actual position in 3D. * \param renderer The current renderer corresponding to the render window. */ void TransformActor(mitk::BaseRenderer *renderer); - /** \brief Generates a plane according to the size of the resliced image in milimeters. + /** \brief Generates a plane according to the size of the resliced image in millimeters. * * In VTK a vtkPlaneSource is defined through three points. The origin and two * points defining the axes of the plane (see VTK documentation). The origin is * set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the * resliced image in space. Z is relevant for blending and the layer property. * The center of the plane (C) is also the center of the view plane (cf. the image above). * * \note For the standard MITK view with three 2D render windows showing three * different slices, three such planes are generated. All these planes are generated * in the XY-plane (even if they depict a YZ-slice of the volume). * */ void GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]); /** \brief Generates a vtkPolyData object containing the outline of a given binary slice. \param renderer: Pointer to the renderer containing the needed information \note This code is based on code from the iil library. */ template vtkSmartPointer CreateOutlinePolyData(mitk::BaseRenderer *renderer); /** Default constructor */ ImageVtkMapper2D(); /** Default deconstructor */ ~ImageVtkMapper2D() override; /** \brief Does the actual resampling, without rendering the image yet. * All the data is generated inside this method. The vtkProp (or Actor) * is filled with content (i.e. the resliced image). * * After generation, a 4x4 transformation matrix(t) of the current slice is obtained * from the vtkResliceImage object via GetReslicesAxis(). This matrix is * applied to each textured plane (actor->SetUserTransform(t)) to transform everything * to the actual 3D position (cf. the following image). * * \image html cameraPositioning3D.png * */ void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; /** \brief This method uses the vtkCamera clipping range and the layer property - * to calcualte the depth of the object (e.g. image or contour). The depth is used + * to calculate the depth of the object (e.g. image or contour). The depth is used * to keep the correct order for the final VTK rendering.*/ float CalculateLayerDepth(mitk::BaseRenderer *renderer); /** \brief This method applies (or modifies) the lookuptable for all types of images. * \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode' * which uses the lookup table must be set. */ void ApplyLookuptable(mitk::BaseRenderer *renderer); /** \brief This method applies a color transfer function. - * Internally, a vtkColorTransferFunction is used. This is usefull for coloring continous + * Internally, a vtkColorTransferFunction is used. This is useful for coloring continuous * images (e.g. float) * \warning To use the color transfer function, the property 'Image Rendering.Transfer Function' must be set and a * 'Image Rendering.Mode' which uses the color transfer function must be set. */ void ApplyColorTransferFunction(mitk::BaseRenderer *renderer); /** * @brief ApplyLevelWindow Apply the level window for the given renderer. * \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses * the level window must be set. * @param renderer Level window for which renderer? */ void ApplyLevelWindow(mitk::BaseRenderer *renderer); /** \brief Set the color of the image/polydata */ void ApplyColor(mitk::BaseRenderer *renderer); /** \brief Set the opacity of the actor. */ void ApplyOpacity(mitk::BaseRenderer *renderer); /** * \brief Calculates whether the given rendering geometry intersects the * given SlicedGeometry3D. * * This method checks if the given PlaneGeometry intersects the given * SlicedGeometry3D. It calculates the distance of the PlaneGeometry to all * 8 cornerpoints of the SlicedGeometry3D. If all distances have the same * sign (all positive or all negative) there is no intersection. * If the distances have different sign, there is an intersection. **/ bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry); /** Helper function to reset the local storage in order to indicate an invalid state.*/ void SetToInvalidState(mitk::ImageVtkMapper2D::LocalStorage* localStorage); }; } // namespace mitk #endif /* MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E */ diff --git a/Modules/Core/include/mitkInteractionConst.h b/Modules/Core/include/mitkInteractionConst.h index c74f992edc..def5e21ed5 100644 --- a/Modules/Core/include/mitkInteractionConst.h +++ b/Modules/Core/include/mitkInteractionConst.h @@ -1,776 +1,776 @@ /*============================================================================ 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 MITKINTERACTCONST_H #define MITKINTERACTCONST_H //##Documentation //## @file mitkInteractionConst.h //## @brief Constants for most interaction classes, due to the generic StateMachines. //## //## Changes in Type, ButtonState or Key has to be don in mitkEventMapper.cpp, too. //## @ingroup Interaction /*Prefixes for Constants: E = Enumeration EID = EventId's Op = Operations Ac = Action Type_ = Type of Event BS_ = ButtonStates and Buttons Key_ = Keys like in QT */ namespace mitk { // Constants for EventIds; use the according constant to through an event in the code enum EEventIds { EIDNULLEVENT = 0, EIDLEFTMOUSEBTN = 1, EIDRIGHTMOUSEBTN = 2, EIDLEFTMOUSEBTNANDSHIFT = 3, EIDMIDDLEMOUSEBTN = 4, EIDLEFTMOUSEBTNANDCTRL = 5, EIDMIDDLEMOUSEBTNANDCTRL = 6, EIDRIGHTMOUSEBTNANDCTRL = 7, EIDLEFTMOUSEBTNDOUBLECLICK = 8, EIDMOUSEWHEEL = 9, EIDLEFTMOUSERELEASE = 505, EIDMIDDLEMOUSERELEASE = 506, EIDRIGHTMOUSERELEASE = 507, EIDLEFTMOUSERELEASEANDSHIFT = 508, EIDMOUSEMOVE = 520, EIDLEFTMOUSEBTNANDMOUSEWHEEL = 521, EIDRIGHTMOUSEBTNANDMOUSEWHEEL = 522, EIDMIDDLEMOUSEBTNANDMOUSEWHEEL = 523, EIDLEFTMOUSEBTNANDMOUSEMOVE = 530, EIDRIGHTMOUSEBTNANDMOUSEMOVE = 531, EIDMIDDLEMOUSEBTNANDMOUSEMOVE = 533, EIDCTRLANDLEFTMOUSEBTNANDMOUSEMOVE = 534, EIDCTRLANDRIGHTMOUSEBTNANDMOUSEMOVE = 535, EIDCTRLANDMIDDLEMOUSEBTNANDMOUSEMOVE = 536, EIDCTRLANDLEFTMOUSEBTNRELEASE = 537, EIDCTRLANDRIGHTMOUSEBTNRELEASE = 538, EIDCTRLANDMIDDLEMOUSEBTNRELEASE = 539, EIDSHIFTANDCTRLANDMIDDLEMOUSEBTN = 540, EIDSHIFTANDLEFTMOUSEBTNANDMOUSEMOVE = 541, EIDSHIFTANDCTRLANDMOUSEMOVE = 542, EIDSHIFTANDCTRLANDMOUSERELEASE = 543, EIDALTANDLEFTMOUSEBTN = 600, EIDALTANDLEFTMOUSEBTNANDMOUSEMOVE = 610, EIDALTANDLEFTMOUSERELEASE = 620, EIDCTRLANDLEFTMOUSEWHEEL = 630, EIDALTANDMOUSEWHEEL = 640, EIDALTANDMIDDLEMOUSEBTN = 641, EIDALTANDMIDDLEMOUSEBTNANDMOVE = 642, EIDALTANDMIDDLEMOUSEBTNRELEASE = 643, EIDALTANDSHIFTANDRIGHTMOUSEBTN = 644, EIDALTANDSHIFTANDRIGHTMOUSEBTNANDMOUSEMOVE = 645, EIDALTANDSHIFTANDRIGHTMOUSEBTNRELEASE = 646, EIDSHIFTANDRIGHTMOUSEPRESS = 2000, EIDSHIFTANDRIGHTMOUSEMOVE = 2001, EIDSHIFTANDRIGHTMOUSERELEASE = 2002, EIDSHIFTANDMIDDLEMOUSEPRESS = 2003, EIDSHIFTANDMIDDLEMOUSEMOVE = 2004, EIDSHIFTANDMIDDLEMOUSERELEASE = 2005, EIDSPACENAVIGATORINPUT = 4001, // 3d Mouse, SpaceNavigator input EIDSPACENAVIGATORKEYDOWN = 4002, // 3d Mouse, KeyDown EIDWIIMOTEINPUT = 4003, // WiiMote input EIDWIIMOTEBUTTON = 4004, // WiiMote home button EIDWIIMOTEBUTTONB = 4005, // WiiMote b button EIDSTRGANDN = 10, EIDSTRGANDE = 11, EIDDELETE = 12, EIDN = 13, EIDESCAPE = 14, EIDP = 15, EIDR = 16, EIDT = 17, EIDS = 18, EIDE = 19, EIDSTRGANDALTANDA = 20, EIDSTRGANDALTANDB = 21, EIDH = 22, EIDRETURN = 23, EIDENTER = 24, EIDSPACE = 25, EIDPLUS = 26, EIDMINUS = 27, EIDSTRGANDALTANDH = 30, EIDSTRGANDALTANDI = 31, EIDSTRGANDALTANDS = 40, EIDALT = 90, EIDSTRGANDB = 91, EIDNEW = 1000, EIDOLD = 1001, EIDFINISHED = 1002, EIDNO = 1003, EIDYES = 1004, EIDSAME = 1005, EIDNOANDLASTOBJECT = 1006, EIDNOANDNOTLASTOBJECT = 1007, EIDLAST = 1008, EIDNOTLAST = 1009, EIDSTSMALERNMINUS1 = 1010, EIDSTLARGERNMINUS1 = 1011, EIDPOSITIONEVENT = 1012, EIDEDIT = 1013, EIDSMALLERN = 1014, EIDEQUALSN = 1015, EIDLARGERN = 1016, EIDEMPTY = 1017, EIDSUBDESELECT = 1020, EIDSMTOSELECTED = 1030, EIDSMTODESELECTED = 1031, EIDTIP = 1050, EIDHEAD = 1051, EIDBODY = 1052, EIDCLEAR = 1100, EIDACTIVATETOOL = 1300, EIDPRINT = 3001, EV_INIT = 5551001, EV_PREVIOUS = 5551002, EV_PATH_COLLECTION_SELECTED = 5551003, EV_NAVIGATION_SELECTED = 5551004, EV_LESS_THEN_MIN_COUNT = 5551005, EV_READY = 5551006, EV_NEXT = 5551007, EV_DONE = 5551008, EV_NEW_LANDMARK = 5551009, EV_REMOVE_LANDMARK = 5551010, EIDINSIDE = 2500, EIDA = 4001, EIDB = 4002, EIDC = 4003, EIDD = 4004, EIDF = 4005, EIDG = 4006, EIDI = 4007, EIDJ = 4008, EIDK = 4009, EIDL = 4010, EIDM = 4011, EIDO = 4012, EIDQ = 4013, EIDU = 4014, EIDV = 4015, EIDW = 4016, EIDX = 4017, EIDY = 4018, EIDZ = 4019, EID1 = 4020, EID2 = 4021, EID3 = 4022, EID4 = 4023, EID5 = 4024, EID6 = 4025, EID7 = 4026, EID8 = 4027, EID9 = 4028, EID0 = 4029, EIDFIGUREHOVER = 12340, EIDNOFIGUREHOVER = 12341 }; //##Constants for Operations //## xomments are always examples of the usage enum EOperations { OpNOTHING = 0, OpTEST = 1, OpNEWCELL = 10, // add a new cell OpADD = 100, // add a point or a vessel OpUNDOADD = 101, OpADDLINE = 1001, // add a line OpINSERT = 200, // insert a point at position OpINSERTLINE = 201, // insert a line at position OpINSERTPOINT = 202, OpCLOSECELL = 250, // close a cell (to a polygon) OpOPENCELL = 251, // close a cell (to a polygon) OpMOVE = 300, // move a point OpMOVELINE = 301, // move a line OpMOVECELL = 302, // move a line OpUNDOMOVE = 303, OpMOVEPOINTUP = 304, OpMOVEPOINTDOWN = 305, OpREMOVE = 400, // remove a point at position OpREMOVELINE = 401, // remove a line at position OpREMOVECELL = 402, // remove a cell OpREMOVEPOINT = 403, OpDELETE = 500, // delete OpDELETELINE = 501, // delete the last line in a cell OpUNDELETE = 502, OpDELETECELL = 505, OpSTATECHANGE = 600, // change a state OpTIMECHANGE = 601, // change a state OpTERMINATE = 666, // change a state OpSELECTPOINT = 700, OpSELECTLINE = 701, OpSELECTCELL = 702, OpSELECTSUBOBJECT = 703, // for VesselGraphInteractor // OpSELECTNEWSUBOBJECT = 704, //for VesselGraphInteractor OpSELECT = 705, OpDESELECTPOINT = 800, OpDESELECTLINE = 801, OpDESELECTCELL = 802, OpDESELECTSUBOBJECT = 803, // for VesselGraphInteractor OpDESELECTALL = 804, // for VesselGraphInteractor OpDESELECT = 805, OpNAVIGATE = 900, OpZOOM = 1000, OpSCALE = 1100, OpROTATE = 1200, OpORIENT = 1201, OpRESTOREPLANEPOSITION = 1202, OpAPPLYTRANSFORMMATRIX = 1203, OpSETPOINTTYPE = 1210, OpMODECHANGE = 1500, OpSENDCOORDINATES = 1600, OpPERIPHERYSEARCH = 2000, // used in VesselGraphInteractor OpROOTSEARCH = 2001, // used in VesselGraphInteractor OpTHICKSTVESSELSEARCH = 2002, // used in VesselGraphInteractor OpSHORTESTPATHSEARCH = 2003, // used in VesselGraphInteractor OpATTRIBUTATION = 2004, // used in VesselGraphInteractor OpDEFAULT = 2006, // used in VesselGraphInteractor OpSURFACECHANGED = 3000, // used for changing polydata in surfaces }; //##Constants for EventMapping... //##connects the statemachine.xml-File with the implemented conditions. //##within one statemachine the choice of the actionconstants is freely //## //## ActionId enum EActions { AcDONOTHING = 0, AcINITNEWOBJECT = 5, AcINITEDITOBJECT = 6, AcINITEDITGROUP = 7, AcINITMOVEMENT = 8, AcINITMOVE = 9, AcINITFOREGROUND = 45, // used in SeedsInteractor for setting the foreground seeds AcINITBACKGROUND = 46, // used in SeedsInteractor for setting the background seeds AcINITNEUTRAL = 47, // used in SeedsInteractor for setting the neutral seeds (rubber) AcINITUPDATE = 1235, // For shape model deformation AcADDPOINT = 10, AcADDPOINTRMB = 6000, // in mitralPointSetInteractor used to set a different type of point AcADD = 11, AcADDLINE = 12, AcADDANDFINISH = 13, AcADDSELECTEDTOGROUP = 64, AcCHECKPOINT = 21, AcCHECKLINE = 22, AcCHECKCELL = 23, AcCHECKELEMENT = 30, // check if there is a element close enough (picking) AcCHECKOBJECT = 31, // check if an object is hit AcCHECKNMINUS1 = 32, // check if the number of elements is equal to N-1 AcCHECKEQUALS1 = 33, // check if the number of elements in the data is equal to 1 AcCHECKNUMBEROFPOINTS = 330, // check the number of elements in the data AcCHECKSELECTED = 34, // check if the given element is selected or not AcCHECKONESELECTED = 340, // check if there is an element that is selected AcCHECKHOVERING = 341, // check if there is an element that is selected AcCHECKGREATERZERO = 35, // check if the current number of elements is greater than 0 AcCHECKGREATERTWO = 36, // check if the current number of elements is greater than two AcCHECKOPERATION = 37, // check if the operation is of one spectial type AcCHECKONESUBINTERACTOR = 38, AcCHECKSUBINTERACTORS = 39, AcFINISHOBJECT = 40, AcFINISHGROUP = 41, AcFINISHMOVEMENT = 42, AcFINISHMOVE = 43, AcFINISH = 44, AcSEARCHOBJECT = 50, AcSEARCHGROUP = 51, AcSEARCHANOTHEROBJECT = 52, // one object is selected and another object is to be added to selection AcSELECTPICKEDOBJECT = 60, // select the picked object and deselect others AcSELECTANOTHEROBJECT = 61, AcSELECTGROUP = 62, AcSELECTALL = 63, AcSELECT = 65, AcSELECTPOINT = 66, AcSELECTLINE = 68, AcSELECTCELL = 67, AcSELECTSUBOBJECT = 69, // used in VesselGraphInteractor AcDESELECTOBJECT = 70, // deselect picked from group AcDESELECTALL = 72, AcDESELECT = 75, AcDESELECTPOINT = 76, AcDESELECTLINE = 78, AcDESELECTCELL = 77, AcNEWPOINT = 80, AcNEWSUBOBJECT = 81, AcMOVEPOINT = 90, AcMOVESELECTED = 91, AcMOVE = 92, AcMOVEPOINTUP = 93, AcMOVEPOINTDOWN = 94, AcREMOVEPOINT = 100, AcREMOVE = 101, AcREMOVELINE = 102, AcREMOVEALL = 103, AcREMOVESELECTEDSUBOBJECT = 104, // used in VesselGraphInteractor AcWHEEL = 105, AcPLUS = 106, AcMINUS = 107, AcDELETEPOINT = 120, AcCLEAR = 130, // clear all elements from a list AcINSERTPOINT = 110, AcINSERTLINE = 111, AC_SET_NEXT_BUTTON_VISIBLE = 5550001, AC_SET_NEXT_BUTTON_INVISIBLE = 5550002, AC_SET_PREVIOUS_BUTTON_VISIBLE = 5550003, AC_SET_PREVIOUS_BUTTON_INVISIBLE = 5550004, AC_SET_ASSISTAND_WIDGET_STECK = 5550005, AC_SETMAX_COUNT_REF_POINTS = 5550006, AC_SET_NEXT_BUTTON_TEXT = 5550007, AC_CHECK_LANDMARK_COUNT = 5550008, AC_SET_DONE_FALSE = 5550009, AC_INIT = 55500010, AC_SET_APPLICATION_SELECTED_FALSE = 55500011, AC_SENSOR_ATTACHED = 55500012, AC_CLOSE_ASSISTENT = 55500013, AC_START_APPLICATION_TEXT = 55500014, AC_START_NAVIGATION = 55500015, AC_START_PATHCOLLECTION = 55500016, AC_LOAD_LANDMARKS = 55500017, AC_CALCULATE_LANDMARK_TRANSFORM = 55500018, AcTERMINATE_INTERACTION = 666, AcTRANSLATESTART = 1000, AcTRANSLATE = 1001, AcSCALESTART = 1002, AcSCALE = 1003, AcROTATESTART = 1004, AcROTATE = 1005, AcINITAFFINEINTERACTIONS = 1006, AcFINISHAFFINEINTERACTIONS = 1007, AcTRANSLATEEND = 1008, AcSCALEEND = 1009, AcROTATEEND = 1010, AcINITZOOM = 1011, AcZOOM = 1012, AcSCROLL = 1013, AcLEVELWINDOW = 1014, AcSCROLLMOUSEWHEEL = 1015, AcSETSTARTPOINT = 1050, AcMODEDESELECT = 1100, // set interactor in not selected mode AcMODESELECT = 1101, // set interactor in selected mode AcMODESUBSELECT = 1102, // set interacor in sub selected mode AcINFORMLISTENERS = 1200, AcASKINTERACTORS = 1201, AcCHECKGREATERONE = 1500, AcCHECKBOUNDINGBOX = 1510, AcFORCESUBINTERACTORS = 1550, AcSENDCOORDINATES = 1600, AcTRANSMITEVENT = 2000, // to transmit an event to a lower Interactor/Statemachine AcPERIPHERYSEARCH = 3000, // used in VesselGraphInteractor AcROOTSEARCH = 3001, // used in VesselGraphInteractor AcTHICKSTVESSELSEARCH = 3002, // used in VesselGraphInteractor AcSHORTESTPATHSEARCH = 3003, // used in VesselGraphInteractor AcSINGLE = 3004, // used in VesselGraphInteractor AcATTRIBUTATION = 3005, // used in VesselGraphInteractor AcDEFAULT = 3007, // used in VesselGraphInteractor AcSETVESSELELEMENT = 3008, // used in VesselGraphInteractor AcCHECKBARRIERSTATUS = 3010, // used in VesselGraphInteractor AcUPDATEMESH = 1234, // For Shape Model Interaction AcINCREASE = 49012, AcDECREASE = 49013, AcMODIFY = 49014, AcUNDOUPDATE = 1236, // For restoring a mesh after an update AcENTEROBJECT = 48000, AcLEAVEOBJECT = 48001, AcSWITCHOBJECT = 48002, AcUPDATELINE = 48003, AcINITLINE = 48004, AcTERMINATELINE = 48005, AcCREATEBOX = 48006, AcCREATEOBJECTFROMLINE = 48007, AcCANCEL = 48008, AcACTIVATETOOL = 48009, AcROTATEAROUNDPOINT1 = 49002, AcROTATEAROUNDPOINT2 = 49003, AcMOVEPOINT1 = 49004, AcMOVEPOINT2 = 49005, AcUPDATEPOINT = 49006, AcUPDATERADIUSMOUSEWHEEL = 49007, AcDISPLAYOPTIONS = 49009, AcCYCLE = 49010, AcACCEPT = 49011, AcONSPACENAVIGATORMOUSEINPUT = 4001, // On input of 3D Mouse AcONPACENAVIGATORKEYDOWN = 4002, // On input of 3D Mouse AcONWIIMOTEINPUT = 4003, // used for wiimote to signal IR input AcRESETVIEW = 4004, // used for wiimote to reset view AcONWIIMOTEBUTTONRELEASED = 4005, // stops the surface interaction AcCHECKPOSITION = 5000, AcINITIALIZECONTOUR = 5001, AcCALCULATENEWSEGMENTATION_SP = 5002, AcINTERACTOR = 5003, AcCALCULATENEWSEGMENTATION_BB = 5004 }; /* //!!!!!!!!!!!!!!!!!!!!!!!! //!!!!!!!!!!!!!!!!!!!!!!!! //EventMechanism: //If you change anything from here on, then change in mitkEventMapper.cpp (Array of constants) as well. //!!!!!!!!!!!!!!!!!!!!!!!! //!!!!!!!!!!!!!!!!!!!!!!!! */ // Type of an Event; enum EEventType { Type_None = 0, // invalid event Type_Timer = 1, // timer event Type_MouseButtonPress = 2, // mouse button pressed Type_MouseButtonRelease = 3, // mouse button released Type_MouseButtonDblClick = 4, // mouse button double click Type_MouseMove = 5, // mouse move Type_KeyPress = 6, // key pressed Type_KeyRelease = 7, // key released Type_FocusIn = 8, // keyboard focus received Type_FocusOut = 9, // keyboard focus lost Type_Enter = 10, // mouse enters widget Type_Leave = 11, // mouse leaves widget Type_Paint = 12, // paint widget Type_Move = 13, // move widget Type_Resize = 14, // resize widget Type_Create = 15, // after object creation Type_Destroy = 16, // during object destruction Type_Show = 17, // widget is shown Type_Hide = 18, // widget is hidden Type_Close = 19, // request to close widget Type_Quit = 20, // request to quit application Type_Reparent = 21, // widget has been reparented Type_ShowMinimized = 22, // widget is shown minimized Type_ShowNormal = 23, // widget is shown normal Type_WindowActivate = 24, // window was activated Type_WindowDeactivate = 25, // window was deactivated Type_ShowToParent = 26, // widget is shown to parent Type_HideToParent = 27, // widget is hidden to parent Type_ShowMaximized = 28, // widget is shown maximized Type_ShowFullScreen = 29, // widget is shown full-screen Type_Accel = 30, // accelerator event Type_Wheel = 31, // wheel event Type_AccelAvailable = 32, // accelerator available event Type_CaptionChange = 33, // caption changed Type_IconChange = 34, // icon changed Type_ParentFontChange = 35, // parent font changed Type_ApplicationFontChange = 36, // application font changed Type_ParentPaletteChange = 37, // parent palette changed Type_ApplicationPaletteChange = 38, // application palette changed Type_PaletteChange = 39, // widget palette changed Type_Clipboard = 40, // internal clipboard event Type_Speech = 42, // reserved for speech input Type_SockAct = 50, // socket activation Type_AccelOverride = 51, // accelerator override event Type_DeferredDelete = 52, // deferred delete event Type_DragEnter = 60, // drag moves into widget Type_DragMove = 61, // drag moves in widget Type_DragLeave = 62, // drag leaves or is cancelled Type_Drop = 63, // actual drop Type_DragResponse = 64, // drag accepted/rejected Type_ChildInserted = 70, // new child widget Type_ChildRemoved = 71, // deleted child widget Type_LayoutHint = 72, // child min/max size changed Type_ShowWindowRequest = 73, // widget's window should be mapped Type_ActivateControl = 80, // ActiveX activation Type_DeactivateControl = 81, // ActiveX deactivation Type_ContextMenu = 82, // context popup menu Type_IMStart = 83, // input method composition start Type_IMCompose = 84, // input method composition Type_IMEnd = 85, // input method composition end Type_Accessibility = 86, // accessibility information is requested Type_TabletMove = 87, // Wacom tablet event Type_LocaleChange = 88, // the system locale changed Type_LanguageChange = 89, // the application language changed Type_LayoutDirectionChange = 90, // the layout direction changed Type_Style = 91, // internal style event Type_TabletPress = 92, // tablet press Type_TabletRelease = 93, // tablet release Type_User = 1000, // first user event id - Type_SpaceNavigatorInput = 1094, // 3D mouse input occured - Type_SpaceNavigatorKeyDown = 1095, // 3D mouse input occured - Type_WiiMoteInput = 1096, // WiiMote input occured + Type_SpaceNavigatorInput = 1094, // 3D mouse input occurred + Type_SpaceNavigatorKeyDown = 1095, // 3D mouse input occurred + Type_WiiMoteInput = 1096, // WiiMote input occurred Type_WiiMoteButton = 1097, // WiiMote button pressed Type_MaxUser = 65535 }; //##ButtonState // mouse/keyboard state values // QT combinations if MOUSEBUTTONRelease: left MouseButton + ControlButton: 0x201 enum EButtonStates { BS_NoButton = 0x0000, BS_LeftButton = 0x0001, BS_RightButton = 0x0002, BS_MidButton = 0x0004, BS_MouseButtonMask = 0x0007, BS_ShiftButton = 0x0100, BS_ControlButton = 0x0200, BS_AltButton = 0x0400, BS_MetaButton = 0x0800, BS_KeyButtonMask = 0x0f00, BS_Keypad = 0x4000 }; //##Key enum EKeys { Key_Escape = 0x1000, // misc keys Key_Tab = 0x1001, Key_Backtab = 0x1002, Key_BackTab = 0x1002, //= Key_Backtab Key_Backspace = 0x1003, Key_BackSpace = 0x1003, //= Key_Backspace Key_Return = 0x1004, Key_Enter = 0x1005, Key_Insert = 0x1006, Key_Delete = 0x1007, Key_Pause = 0x1008, Key_Print = 0x1009, Key_SysReq = 0x100a, Key_Home = 0x1010, // cursor movement Key_End = 0x1011, Key_Left = 0x1012, Key_Up = 0x1013, Key_Right = 0x1014, Key_Down = 0x1015, Key_Prior = 0x1016, Key_PageUp = 0x1016, //=Key_Prior Key_Next = 0x1017, Key_PageDown = 0x1017, //=Key_Next Key_Shift = 0x1020, // modifiers Key_Control = 0x1021, Key_Meta = 0x1022, Key_Alt = 0x1023, Key_CapsLock = 0x1024, Key_NumLock = 0x1025, Key_ScrollLock = 0x1026, Key_F1 = 0x1030, // function keys Key_F2 = 0x1031, Key_F3 = 0x1032, Key_F4 = 0x1033, Key_F5 = 0x1034, Key_F6 = 0x1035, Key_F7 = 0x1036, Key_F8 = 0x1037, Key_F9 = 0x1038, Key_F10 = 0x1039, Key_F11 = 0x103a, Key_F12 = 0x103b, Key_F13 = 0x103c, Key_F14 = 0x103d, Key_F15 = 0x103e, Key_F16 = 0x103f, Key_F17 = 0x1040, Key_F18 = 0x1041, Key_F19 = 0x1042, Key_F20 = 0x1043, Key_F21 = 0x1044, Key_F22 = 0x1045, Key_F23 = 0x1046, Key_F24 = 0x1047, Key_F25 = 0x1048, // F25 .. F35 only on X11 Key_F26 = 0x1049, Key_F27 = 0x104a, Key_F28 = 0x104b, Key_F29 = 0x104c, Key_F30 = 0x104d, Key_F31 = 0x104e, Key_F32 = 0x104f, Key_F33 = 0x1050, Key_F34 = 0x1051, Key_F35 = 0x1052, Key_Super_L = 0x1053, // extra keys Key_Super_R = 0x1054, Key_Menu = 0x1055, Key_Hyper_L = 0x1056, Key_Hyper_R = 0x1057, Key_Help = 0x1058, // International input method support (X keycode - = 0xEE00) // Only interesting if you are writing your own input method Key_Muhenkan = 0x1122, // Cancel Conversion Key_Henkan = 0x1123, // Start/Stop Conversion Key_Hiragana_Katakana = 0x1127, // Hiragana/Katakana toggle Key_Zenkaku_Hankaku = 0x112A, // Zenkaku/Hankaku toggle Key_Space = 0x20, // 7 bit printable ASCII Key_Any = 0x20, //= Key_Space Key_Exclam = 0x21, Key_QuoteDbl = 0x22, Key_NumberSign = 0x23, Key_Dollar = 0x24, Key_Percent = 0x25, Key_Ampersand = 0x26, Key_Apostrophe = 0x27, Key_ParenLeft = 0x28, Key_ParenRight = 0x29, Key_Asterisk = 0x2a, Key_Plus = 0x2b, Key_Comma = 0x2c, Key_Minus = 0x2d, Key_Period = 0x2e, Key_Slash = 0x2f, Key_0 = 0x30, Key_1 = 0x31, Key_2 = 0x32, Key_3 = 0x33, Key_4 = 0x34, Key_5 = 0x35, Key_6 = 0x36, Key_7 = 0x37, Key_8 = 0x38, Key_9 = 0x39, Key_Colon = 0x3a, Key_Semicolon = 0x3b, Key_Less = 0x3c, Key_Equal = 0x3d, Key_Greater = 0x3e, Key_Question = 0x3f, Key_At = 0x40, Key_A = 0x41, Key_B = 0x42, Key_C = 0x43, Key_D = 0x44, Key_E = 0x45, Key_F = 0x46, Key_G = 0x47, Key_H = 0x48, Key_I = 0x49, Key_J = 0x4a, Key_K = 0x4b, Key_L = 0x4c, Key_M = 0x4d, Key_N = 0x4e, Key_O = 0x4f, Key_P = 0x50, Key_Q = 0x51, Key_R = 0x52, Key_S = 0x53, Key_T = 0x54, Key_U = 0x55, Key_V = 0x56, Key_W = 0x57, Key_X = 0x58, Key_Y = 0x59, Key_Z = 0x5a, Key_BracketLeft = 0x5b, Key_Backslash = 0x5c, Key_BracketRight = 0x5d, Key_AsciiCircum = 0x5e, Key_Underscore = 0x5f, Key_QuoteLeft = 0x60, Key_BraceLeft = 0x7b, Key_Bar = 0x7c, Key_BraceRight = 0x7d, Key_AsciiTilde = 0x7e, Key_nobreakspace = 0x0a0, Key_exclamdown = 0x0a1, Key_cent = 0x0a2, Key_sterling = 0x0a3, Key_currency = 0x0a4, Key_yen = 0x0a5, Key_brokenbar = 0x0a6, Key_section = 0x0a7, Key_diaeresis = 0x0a8, Key_copyright = 0x0a9, Key_ordfeminine = 0x0aa, Key_guillemotleft = 0x0ab, // left angle quotation mark Key_notsign = 0x0ac, Key_hyphen = 0x0ad, Key_registered = 0x0ae, Key_macron = 0x0af, Key_degree = 0x0b0, Key_plusminus = 0x0b1, Key_twosuperior = 0x0b2, Key_threesuperior = 0x0b3, Key_acute = 0x0b4, Key_mu = 0x0b5, Key_paragraph = 0x0b6, Key_periodcentered = 0x0b7, Key_cedilla = 0x0b8, Key_onesuperior = 0x0b9, Key_masculine = 0x0ba, Key_guillemotright = 0x0bb, // right angle quotation mark Key_onequarter = 0x0bc, Key_onehalf = 0x0bd, Key_threequarters = 0x0be, Key_questiondown = 0x0bf, Key_Agrave = 0x0c0, Key_Aacute = 0x0c1, Key_Acircumflex = 0x0c2, Key_Atilde = 0x0c3, Key_Adiaeresis = 0x0c4, Key_Aring = 0x0c5, Key_AE = 0x0c6, Key_Ccedilla = 0x0c7, Key_Egrave = 0x0c8, Key_Eacute = 0x0c9, Key_Ecircumflex = 0x0ca, Key_Ediaeresis = 0x0cb, Key_Igrave = 0x0cc, Key_Iacute = 0x0cd, Key_Icircumflex = 0x0ce, Key_Idiaeresis = 0x0cf, Key_ETH = 0x0d0, Key_Ntilde = 0x0d1, Key_Ograve = 0x0d2, Key_Oacute = 0x0d3, Key_Ocircumflex = 0x0d4, Key_Otilde = 0x0d5, Key_Odiaeresis = 0x0d6, Key_multiply = 0x0d7, Key_Ooblique = 0x0d8, Key_Ugrave = 0x0d9, Key_Uacute = 0x0da, Key_Ucircumflex = 0x0db, Key_Udiaeresis = 0x0dc, Key_Yacute = 0x0dd, Key_THORN = 0x0de, Key_ssharp = 0x0df, Key_agrave = 0x0e0, Key_aacute = 0x0e1, Key_acircumflex = 0x0e2, Key_atilde = 0x0e3, Key_adiaeresis = 0x0e4, Key_aring = 0x0e5, Key_ae = 0x0e6, Key_ccedilla = 0x0e7, Key_egrave = 0x0e8, Key_eacute = 0x0e9, Key_ecircumflex = 0x0ea, Key_ediaeresis = 0x0eb, Key_igrave = 0x0ec, Key_iacute = 0x0ed, Key_icircumflex = 0x0ee, Key_idiaeresis = 0x0ef, Key_eth = 0x0f0, Key_ntilde = 0x0f1, Key_ograve = 0x0f2, Key_oacute = 0x0f3, Key_ocircumflex = 0x0f4, Key_otilde = 0x0f5, Key_odiaeresis = 0x0f6, Key_division = 0x0f7, Key_oslash = 0x0f8, Key_ugrave = 0x0f9, Key_uacute = 0x0fa, Key_ucircumflex = 0x0fb, Key_udiaeresis = 0x0fc, Key_yacute = 0x0fd, Key_thorn = 0x0fe, Key_ydiaeresis = 0x0ff, Key_unknown = 0xffff, Key_none = 0xffff //= Key_unknown }; } // namespace mitk #endif // ifndef MITKINTERACTCONST_H diff --git a/Modules/Core/include/mitkInteractionEventHandler.h b/Modules/Core/include/mitkInteractionEventHandler.h index 53fb42c44c..7d95e471a9 100644 --- a/Modules/Core/include/mitkInteractionEventHandler.h +++ b/Modules/Core/include/mitkInteractionEventHandler.h @@ -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. ============================================================================*/ #ifndef MITKEVENTHANDLER_H_ #define MITKEVENTHANDLER_H_ #include "itkLightObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkEventConfig.h" #include "mitkPropertyList.h" #include #include namespace us { class Module; } namespace mitk { /** * \class EventHandler * Serves as a base class for all objects and classes that handle mitk::InteractionEvents. * * It provides an interface to load configuration objects map of events to variant names. */ class InteractionEvent; class MITKCORE_EXPORT InteractionEventHandler : public itk::Object { public: mitkClassMacroItkParent(InteractionEventHandler, itk::Object); /** * @brief Loads a configuration from an XML resource. * * Loads an event configuration from an XML resource file contained in the given module. * Default is the Mitk module (core). * The files have to be placed in the Resources/Interactions folder of their respective module. * This method will remove all existing configuration and replaces it with the new one. * * @see SetEventConfig(const EventConfig&) * * @param filename The resource name relative to the Interactions resource folder. * @param module The module containing the resource. Defaults to the Mitk module. * @return \c true if the resource was successfully loaded, \c false otherwise. */ bool SetEventConfig(const std::string &filename, const us::Module *module = nullptr); /** * @brief Loads a configuration from an EventConfig object. * * Loads an event configuration from the given EventConfig object. This method will remove * all existing configuration and replaces it with the new one. * * @see SetEventConfig(const std::string&, const Module*) * * @param config The EventConfig object containing the new configuration. * @return \c true if the configuration was successfully loaded, \c false otherwise. */ bool SetEventConfig(const EventConfig &config); /** * @brief Returns the current configuration. * @return A EventConfig object representing the current event configuration. */ EventConfig GetEventConfig() const; /** * @brief This method \e extends the configuration. * * The configuration from the resource provided is loaded and only the ones conflicting are replaced by the new one. * This way several configuration files can be combined. * * @see AddEventConfig(const EventConfig&) * * @param filename The resource name relative to the Interactions resource folder. * @param module The module containing the resource. Defaults to the Mitk module. * @return \c true if the configuration was successfully added, \c false otherwise. */ bool AddEventConfig(const std::string &filename, const us::Module *module = nullptr); /** * @brief This method \e extends the configuration. * The configuration from the EventConfig object is loaded and only the ones conflicting are replaced by the new * one. * This way several configurations can be combined. * * @see AddEventConfig(const std::string&, const Module*) * * @param config The EventConfig object whose configuration should be added. * @return \c true if the configuration was successfully added, \c false otherwise. */ bool AddEventConfig(const EventConfig &config); protected: InteractionEventHandler(); ~InteractionEventHandler() override; /** * Returns a PropertyList in which the parameters defined in the config file are listed. */ PropertyList::Pointer GetAttributes() const; std::string MapToEventVariant(InteractionEvent *interactionEvent); /** - * Is called whenever a new config object ist set. + * Is called whenever a new config object is set. * Overwrite this method e.g. to initialize EventHandler with parameters in configuration file. */ virtual void ConfigurationChanged(); private: EventConfig m_EventConfig; }; } /* namespace mitk */ #endif /* MITKEVENTHANDLER_H_ */ diff --git a/Modules/Core/include/mitkLevelWindow.h b/Modules/Core/include/mitkLevelWindow.h index 538fe6e56c..6855dc4ce4 100644 --- a/Modules/Core/include/mitkLevelWindow.h +++ b/Modules/Core/include/mitkLevelWindow.h @@ -1,263 +1,263 @@ /*============================================================================ 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 LEVELWINDOW_H_HEADER_INCLUDED_C1F4F02C #define LEVELWINDOW_H_HEADER_INCLUDED_C1F4F02C #include "mitkNumericTypes.h" #include namespace mitk { class Image; /** * @brief The LevelWindow class Class to store level/window values. * * Current min and max value are stored in m_LowerWindowBound and m_UpperWindowBound. * m_DefaultLevel amd m_DefaultWindow store the initial Level/Window values for the image. * m_DefaultRangeMin and m_DefaultRangeMax store the initial minrange and maxrange for the image. * * The finite maximum and minimum of valid value range is stored in m_RangeMin and m_RangeMax. * If deduced from an image by default the minimum or maximum of it statistics is used. If one * of these values are infinite the 2nd extrimum (which is guaranteed to be finite), will be used. * * See documentation of SetAuto for information on how the level window is initialized from an image. * * @ingroup DataManagement * * @note If you want to apply the mitk::LevelWindow to an mitk::Image, make sure * to use the mitk::LevelWindowProperty and set the mitk::RenderingModeProperty * to a mode which supports level window (e.g. LEVELWINDOW_COLOR). * Make sure to check the documentation of the mitk::RenderingModeProperty. For a * code example how to use the mitk::LevelWindowProperty check the * mitkImageVtkMapper2DLevelWindowTest.cpp in Core/Code/Testing. */ class MITKCORE_EXPORT LevelWindow { public: LevelWindow(ScalarType level = 127.5, ScalarType window = 255.0); LevelWindow(const mitk::LevelWindow &levWin); virtual ~LevelWindow(); /*! * \brief method that returns the level value, i.e. the center of * the current grey value interval */ ScalarType GetLevel() const; /*! * \brief returns the current window size, i.e the range size of the current grey value interval */ ScalarType GetWindow() const; /*! * \brief method returns the default level value for the image */ ScalarType GetDefaultLevel() const; /*! * \brief returns the default window size for the image */ ScalarType GetDefaultWindow() const; /*! * \brief Resets the level and the window value to the default values */ void ResetDefaultLevelWindow(); /*! * Returns the minimum Value of the window */ ScalarType GetLowerWindowBound() const; /*! * Returns the upper window bound value of the window */ ScalarType GetUpperWindowBound() const; /*! * To set the level and the window value */ void SetLevelWindow(ScalarType level, ScalarType window, bool expandRangesIfNecessary = true); /*! * Set the lower and upper bound of the window */ void SetWindowBounds(ScalarType lowerBound, ScalarType upperBound, bool expandRangesIfNecessary = true); /*! * sets the window to its maximum Size in scaleRange */ void SetToMaxWindowSize(); /*! * Set the range minimum and maximum value */ void SetRangeMinMax(ScalarType min, ScalarType max); /*! * Get the range minimum value */ ScalarType GetRangeMin() const; /*! * Get the range maximum value */ ScalarType GetRangeMax() const; /*! * Get the default range minimum value */ ScalarType GetDefaultLowerBound() const; /*! * Get the default range maximum value */ ScalarType GetDefaultUpperBound() const; /*! * \brief the default min and max range for image will be reset */ void ResetDefaultRangeMinMax(); /**! * \brief returns the size of the grey value range */ ScalarType GetRange() const; /*! * set the default level and window value */ void SetDefaultLevelWindow(ScalarType level, ScalarType window); /*! - * set the default Bounderies + * set the default Boundaries */ void SetDefaultBoundaries(ScalarType low, ScalarType up); /**! * \brief sets level/window to optimize the contrast of the given Image */ void SetAuto(const Image *image, bool tryPicTags = true, bool guessByCentralSlice = true, unsigned selectedComponent = 0); /**! * \brief sets level/window to the min/max greyvalues of the given Image */ void SetToImageRange(const Image *image); /** * If a level window is set to fixed, the set and get methods won't accept * modifications to the level window settings anymore. This behaviour can * be turned of by setting fixed to false; */ void SetFixed(bool fixed); /** * Returns whether the level window settings are fixed (@see SetFixed(bool)) or not */ bool GetFixed() const; /** * Returns whether the level window settings are fixed (@see SetFixed(bool)) or not */ bool IsFixed() const; /*! * \brief equality operator implementation that allows to compare two level windows */ virtual bool operator==(const LevelWindow &levWin) const; /*! * \brief non equality operator implementation that allows to compare two level windows */ virtual bool operator!=(const LevelWindow &levWin) const; /*! * \brief implementation necessary because operator made * private in itk::Object */ virtual LevelWindow &operator=(const LevelWindow &levWin); /*! * \brief Shows if floating values are accepted */ bool IsFloatingValues() const; /*! * \brief Sets the floating image value */ void SetFloatingValues(bool value); protected: /*! * lower bound of current window */ ScalarType m_LowerWindowBound; /*! * upper bound of current window */ ScalarType m_UpperWindowBound; /*! * minimum gray value of the window */ ScalarType m_RangeMin; /*! * maximum gray value of the window */ ScalarType m_RangeMax; /*! * default minimum gray value of the window */ ScalarType m_DefaultLowerBound; /*! * default maximum gray value of the window */ ScalarType m_DefaultUpperBound; /*! * Image with floating values */ bool m_IsFloatingImage; /*! * Defines whether the level window settings may be changed after * initialization or not. */ bool m_Fixed; /*! * confidence tests * * if m_LowerWindowBound > m_UpperWindowBound, then the values for m_LowerWindowBound and m_UpperWindowBound will be * exchanged * * if m_LowerWindowBound < m_RangeMin, m_LowerWindowBound will be set to m_RangeMin. m_UpperWindowBound will be * decreased the same as m_LowerWindowBound will be increased, but minimum value for m_UpperWindowBound is also * m_RangeMin. * * if m_UpperWindowBound > m_RangeMax, m_UpperWindowBound will be set to m_RangeMax. m_LowerWindowBound will be * increased the same as m_UpperWindowBound will be decreased, but maximum value for m_LowerWindowBound is also * m_RangeMax. * */ inline void EnsureConsistency(); }; } // namespace mitk #endif /* LEVELWINDOW_H_HEADER_INCLUDED_C1F4F02C */ diff --git a/Modules/Core/include/mitkLimitedLinearUndo.h b/Modules/Core/include/mitkLimitedLinearUndo.h index 3d724b1db7..01a20f8a53 100644 --- a/Modules/Core/include/mitkLimitedLinearUndo.h +++ b/Modules/Core/include/mitkLimitedLinearUndo.h @@ -1,159 +1,159 @@ /*============================================================================ 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 LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 #define LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 // MITK header #include "mitkOperationEvent.h" #include "mitkUndoModel.h" #include // STL header #include // ITK header #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include namespace mitk { //##Documentation //## @brief A linear undo model with one undo and one redo stack. //## //## Derived from UndoModel AND itk::Object. Invokes ITK-events to signal listening //## GUI elements, whether each of the stacks is empty or not (to enable/disable button, ...) class MITKCORE_EXPORT LimitedLinearUndo : public UndoModel { public: typedef std::deque UndoContainer; typedef std::deque::reverse_iterator UndoContainerRevIter; mitkClassMacro(LimitedLinearUndo, UndoModel); itkFactorylessNewMacro(Self); itkCloneMacro(Self); bool SetOperationEvent(UndoStackItem *stackItem) override; //##Documentation //## @brief Undoes the last changes //## //## Reads the top element of the Undo-Stack, //## executes the operation, //## swaps the OperationEvent-Undo with the Operation //## and sets it to Redo-Stack bool Undo() override; bool Undo(bool) override; //##Documentation //## @brief Undoes all changes until ObjectEventID oeid virtual bool Undo(int oeid); //##Documentation //## @brief Undoes the last changes //## //## Reads the top element of the Redo-Stack, //## executes the operation, //## swaps the OperationEvent-Operation with the Undo-Operation //## and sets it to Undo-Stack bool Redo() override; bool Redo(bool) override; //##Documentation //## @brief Redoes all changes until ObjectEventID oeid virtual bool Redo(int oeid); //##Documentation //## @brief Clears UndoList and RedoList void Clear() override; //##Documentation //## @brief Clears the RedoList void ClearRedoList() override; //##Documentation //## @brief True, if RedoList is empty bool RedoListEmpty() override; //##Documentation //## @brief Gets the limit on the size of the undo history. //## The undo limit determines how many items can be stored //## in the undo stack. If the value is 0 that means that //## there is no limit. std::size_t GetUndoLimit() const override; //##Documentation //## @brief Sets a limit on the size of the undo history. //## If the limit is reached, the oldest undo items will //## be dropped from the bottom of the undo stack. //## The 0 value means that there is no limit. //## @param limit the maximum number of items on the stack void SetUndoLimit(std::size_t limit) override; //##Documentation //## @brief Returns the ObjectEventId of the //## top element in the OperationHistory int GetLastObjectEventIdInList() override; //##Documentation //## @brief Returns the GroupEventId of the //## top element in the OperationHistory int GetLastGroupEventIdInList() override; //##Documentation //## @brief Returns the last specified OperationEvent in Undo-list //## corresponding to the given values; if nothing found, then returns nullptr OperationEvent *GetLastOfType(OperationActor *destination, OperationType opType) override; protected: //##Documentation //## Constructor LimitedLinearUndo(); //##Documentation //## Destructor ~LimitedLinearUndo() override; //## @brief Convenience method to free the memory of //## elements in the list and to clear the list void ClearList(UndoContainer *list); UndoContainer m_UndoList; UndoContainer m_RedoList; private: int FirstObjectEventIdOfCurrentGroup(UndoContainer &stack); std::size_t m_UndoLimit; }; #pragma GCC visibility push(default) - /// Some itk events to notify listening GUI elements, when the undo or redo stack is empty (diable undo button) + /// Some itk events to notify listening GUI elements, when the undo or redo stack is empty (disable undo button) /// or when there are items in the stack (enable button) itkEventMacroDeclaration(UndoStackEvent, itk::ModifiedEvent); itkEventMacroDeclaration(UndoEmptyEvent, UndoStackEvent); itkEventMacroDeclaration(RedoEmptyEvent, UndoStackEvent); itkEventMacroDeclaration(UndoNotEmptyEvent, UndoStackEvent); itkEventMacroDeclaration(RedoNotEmptyEvent, UndoStackEvent); /// Additional unused events, if anybody wants to put an artificial limit to the possible number of items in the stack itkEventMacroDeclaration(UndoFullEvent, UndoStackEvent); itkEventMacroDeclaration(RedoFullEvent, UndoStackEvent); #pragma GCC visibility pop } // namespace mitk #endif /* LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 */ diff --git a/Modules/Core/include/mitkLine.h b/Modules/Core/include/mitkLine.h index 936060e4f5..1c11c873c6 100644 --- a/Modules/Core/include/mitkLine.h +++ b/Modules/Core/include/mitkLine.h @@ -1,413 +1,413 @@ /*============================================================================ 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 MITKLINE_H_HEADER_INCLUDED_C19C01E2 #define MITKLINE_H_HEADER_INCLUDED_C19C01E2 #include "mitkNumericTypes.h" #include #include #include #include namespace mitk { //##Documentation - //## @brief Descibes a line + //## @brief Describes a line //## @ingroup Geometry template class Line { public: Line() { m_Point.Fill(0); m_Direction.Fill(0); } //##Documentation //## @brief Define line by point and direction //## //## Length of direction defines the the length of the line Line(const itk::Point &point, const itk::Vector &direction) { this->m_Point = point; this->m_Direction = direction; } //##Documentation //## @brief Get start point of the line const itk::Point &GetPoint() const { return m_Point; } //##Documentation //## @brief Get start point of the line itk::Point &GetPoint() { return m_Point; } //##Documentation //## @brief Get point on the line with parameter @a t //## //## @return m_Point+t*m_Direction const itk::Point GetPoint(TCoordRep t) const { return m_Point + m_Direction * t; } //##Documentation //## @brief Set/change start point of the line void SetPoint(const itk::Point &point1) { itk::Point point2; point2 = m_Point + m_Direction; m_Point = point1; m_Direction = point2.GetVectorFromOrigin() - point1.GetVectorFromOrigin(); } //##Documentation //## @brief Get the direction vector of the line const itk::Vector &GetDirection() const { return m_Direction; } //##Documentation //## @brief Get the direction vector of the line itk::Vector &GetDirection() { return m_Direction; } //##Documentation //## @brief Set the direction vector of the line void SetDirection(const itk::Vector &direction) { m_Direction = direction; } //##Documentation //## @brief Define line by point and direction //## //## Length of direction defines the the length of the line void Set(const itk::Point &point, const itk::Vector &direction) { this->m_Point = point; this->m_Direction = direction; } //##Documentation //## @brief Define line by two points void SetPoints(const itk::Point &point1, const itk::Point &point2) { this->m_Point = point1; // this->m_Direction.sub( point2, point1 ); m_Direction = point2 - point1; } //##Documentation //## @brief Set/change start point of the line void SetPoint1(const itk::Point &point1) { itk::Vector point2; point2 = m_Point.GetVectorFromOrigin() + m_Direction; m_Point = point1; m_Direction = point2 - point1.GetVectorFromOrigin(); } //##Documentation //## @brief Get start point of the line const itk::Point &GetPoint1() const { return m_Point; } //##Documentation //## @brief Set/change end point of the line void SetPoint2(const itk::Point &point2) { m_Direction = point2 - m_Point; } //##Documentation //## @brief Get end point of the line itk::Point GetPoint2() const { itk::Point point2; point2 = m_Point + m_Direction; return point2; } //##Documentation //## @brief Transform the line with a Transform void Transform(itk::Transform &transform) { m_Direction = transform.TransformVector(m_Direction); m_Point = transform.TransformPoint(m_Point); } //##Documentation //## @brief Transform the line with a matrix //## //## Only the direction will be changed, not the start point. void Transform(const itk::Matrix &matrix) { m_Direction = matrix * m_Direction; } //##Documentation //## @brief Distance of a point from the line double Distance(const itk::Point &point) const { itk::Vector diff; diff = Project(point) - point; return diff.GetNorm(); } //##Documentation //## @brief Project a point on the line itk::Point Project(const itk::Point &point) const { if (m_Direction.GetNorm() == 0) return this->m_Point; itk::Vector diff; diff = point - this->m_Point; itk::Vector normalizedDirection = m_Direction; normalizedDirection.Normalize(); normalizedDirection *= dot_product(diff.GetVnlVector(), normalizedDirection.GetVnlVector()); return this->m_Point + normalizedDirection; } //##Documentation //## @brief Test if a point is part of the line //## //## Length of the direction vector defines the length of the line bool IsPartOfStraightLine(const itk::Point &point) const { if (Distance(point) > eps) return false; itk::Vector diff; diff = point - this->m_Point; if (diff * m_Direction < 0) return false; if (diff.GetSquaredNorm() <= m_Direction.GetSquaredNorm()) return true; return false; } //##Documentation //## @brief Test if a point is part of the line (line having infinite length) bool IsPartOfLine(const itk::Point &point) const { if (Distance(point) < eps) return true; return false; } //##Documentation //## @brief Test if a lines is parallel to this line bool IsParallel(const Line &line) const { vnl_vector normal; normal = vnl_cross_3d(m_Direction.GetVnlVector(), line.GetDirection().GetVnlVector()); if (normal.squared_magnitude() < eps) return true; return false; } //##Documentation //## @brief Test if a line is part of the line (line having infinite length) bool IsPartOfLine(const Line &line) const { return (Distance(line.GetPoint()) < 0) && (IsParallel(line)); } //##Documentation //## @brief Test if the two lines are identical //## //## Start point and direction and length of direction vector must be //## equal for identical lines. bool operator==(const Line &line) const { itk::Vector diff; diff = GetPoint1() - line.GetPoint1(); if (diff.GetSquaredNorm() > eps) return false; diff = GetPoint2() - line.GetPoint2(); if (diff.GetSquaredNorm() > eps) return false; return true; } //##Documentation //## @brief Set the line by another line inline const Line &operator=(const Line &line) { m_Point = line.GetPoint(); m_Direction = line.GetDirection(); return *this; } //##Documentation //## @brief Test if two lines are not identical //## //## \sa operator== bool operator!=(const Line &line) const { return !((*this) == line); } //##Documentation //## @brief Calculates the intersection points of a straight line in 2D //## with a rectangle //## //## @param x1,y1,x2,y2 rectangle //## @param p,d straight line: p point on it, d direction of line //## @param s1 first intersection point (valid only if s_num>0) //## @param s2 second intersection point (valid only if s_num==2) //## @return number of intersection points (0<=s_num<=2) static int RectangleLineIntersection(TCoordRep x1, TCoordRep y1, TCoordRep x2, TCoordRep y2, itk::Point p, itk::Vector d, itk::Point &s1, itk::Point &s2) { int s_num; TCoordRep t; s_num = 0; /*test if intersecting with the horizontal axis*/ if (fabs(d[0]) > eps) { t = (x1 - p[0]) / d[0]; itk::Point l = p + d * t; if ((l[1] >= y1) && (l[1] < y2)) { // yes, intersection point within the bounds of the border-line if (s_num) s2 = l; else s1 = l; ++s_num; } } if (fabs(d[0]) > eps) { t = (x2 - p[0]) / d[0]; itk::Point l = p + d * t; if ((l[1] >= y1) && (l[1] < y2)) { // yes, intersection point within the bounds of the border-line if (s_num) s2 = l; else s1 = l; ++s_num; } } /*test if intersecting with the vertical axis*/ if (fabs(d[1]) > eps) { t = (y1 - p[1]) / d[1]; itk::Point l = p + d * t; if ((l[0] >= x1) && (l[0] < x2)) { // yes, intersection point within the bounds of the border-line if (s_num) s2 = l; else s1 = l; ++s_num; } } if (fabs(d[1]) > eps) { t = (y2 - p[1]) / d[1]; itk::Point l = p + d * t; if ((l[0] >= x1) && (l[0] < x2)) { // yes, intersection point within the bounds of the border-line if (s_num) s2 = l; else s1 = l; ++s_num; } } return s_num; } /** * \brief Calculates the intersection points of a straight line in 3D with * a box. * * \param x1,y1,z1 first corner of the box * \param x2,y2,z2 second corner of the box * \param p,d straight line: p point on it, d direction of line * \param s1 first intersection point (valid only if s_num>0) * \param s2 second intersection point (valid only if s_num==2) * \return number of intersection points (0<=s_num<=2) */ static int BoxLineIntersection(TCoordRep x1, TCoordRep y1, TCoordRep z1, TCoordRep x2, TCoordRep y2, TCoordRep z2, itk::Point p, itk::Vector d, itk::Point &s1, itk::Point &s2) { int num = 0; ScalarType box[6]; box[0] = x1; box[1] = x2; box[2] = y1; box[3] = y2; box[4] = z1; box[5] = z2; itk::Point point; int i, j; for (i = 0; i < 6; ++i) { j = i / 2; if (fabs(d[j]) > eps) { ScalarType lambda = (box[i] - p[j]) / d[j]; point = p + d * lambda; int k = (j + 1) % 3; int l = (j + 2) % 3; if ((((point[k] >= box[k * 2]) && (point[k] <= box[k * 2 + 1])) || ((point[k] <= box[k * 2]) && (point[k] >= box[k * 2 + 1]))) && (((point[l] >= box[l * 2]) && (point[l] <= box[l * 2 + 1])) || ((point[l] <= box[l * 2]) && (point[l] >= box[l * 2 + 1]))) ) { if (num == 0) { s1 = point; } else { s2 = point; } ++num; } } } return num; } protected: itk::Point m_Point; itk::Vector m_Direction; }; typedef Line Line3D; } // namespace mitk #endif /* MITKLINE_H_HEADER_INCLUDED_C19C01E2 */ diff --git a/Modules/Core/include/mitkLocalStorageHandler.h b/Modules/Core/include/mitkLocalStorageHandler.h index 86349b4138..b20eb19b3d 100644 --- a/Modules/Core/include/mitkLocalStorageHandler.h +++ b/Modules/Core/include/mitkLocalStorageHandler.h @@ -1,112 +1,112 @@ /*============================================================================ 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 LOCALSTORAGEHANDLER_H_HEADER_INCLUDED_C1E6EA08 #define LOCALSTORAGEHANDLER_H_HEADER_INCLUDED_C1E6EA08 #include "mitkBaseRenderer.h" #include "mitkCommon.h" #include "mitkLevelWindow.h" #include "mitkVtkPropRenderer.h" #include #include #include class vtkWindow; class vtkProp; namespace mitk { /** \brief Interface for accessing (templated) LocalStorageHandler instances. */ class BaseLocalStorageHandler { public: virtual ~BaseLocalStorageHandler() {} virtual void ClearLocalStorage(mitk::BaseRenderer *renderer, bool unregisterFromBaseRenderer = true) = 0; }; /** \brief Templated class for management of LocalStorage implementations in Mappers. * * The LocalStorageHandler is responsible for providing a LocalStorage to a * concrete mitk::Mapper subclass. Each RenderWindow / mitk::BaseRenderer is - * assigned its own LocalStorage instance so that all contained ressources + * assigned its own LocalStorage instance so that all contained resources * (actors, shaders, textures, ...) are provided individually per window. * */ template class LocalStorageHandler : public mitk::BaseLocalStorageHandler { protected: std::map m_BaseRenderer2LS; public: - /** \brief deallocates a local storage for a specifc BaseRenderer (if the + /** \brief deallocates a local storage for a specific BaseRenderer (if the * BaseRenderer is itself deallocating it in its destructor, it has to set * unregisterFromBaseRenderer=false) */ void ClearLocalStorage(mitk::BaseRenderer *renderer, bool unregisterFromBaseRenderer = true) override { // MITK_INFO << "deleting a localstorage on a mapper request"; if (unregisterFromBaseRenderer) renderer->UnregisterLocalStorageHandler(this); L *l = m_BaseRenderer2LS[renderer]; m_BaseRenderer2LS.erase(renderer); delete l; } std::vector GetRegisteredBaseRenderer() { std::vector baserenderers; typename std::map::iterator it; for (it = m_BaseRenderer2LS.begin(); it != m_BaseRenderer2LS.end(); ++it) { baserenderers.push_back(it->first); } return baserenderers; } /** \brief Retrieves a LocalStorage for a specific BaseRenderer. * * Should be used by mappers in GenerateDataForRenderer() */ L *GetLocalStorage(mitk::BaseRenderer *forRenderer) { L *l = m_BaseRenderer2LS[forRenderer]; if (!l) { // MITK_INFO << "creating new localstorage"; l = new L; m_BaseRenderer2LS[forRenderer] = l; forRenderer->RegisterLocalStorageHandler(this); } return l; } ~LocalStorageHandler() override { typename std::map::iterator it; for (it = m_BaseRenderer2LS.begin(); it != m_BaseRenderer2LS.end(); it++) { (*it).first->UnregisterLocalStorageHandler(this); delete (*it).second; } m_BaseRenderer2LS.clear(); } }; } // namespace mitk #endif /* LOCALSTORAGEHANDLER_H_HEADER_INCLUDED_C1E6EA08 */ diff --git a/Modules/Core/include/mitkLocaleSwitch.h b/Modules/Core/include/mitkLocaleSwitch.h index 601ac02d63..0d02161310 100644 --- a/Modules/Core/include/mitkLocaleSwitch.h +++ b/Modules/Core/include/mitkLocaleSwitch.h @@ -1,72 +1,72 @@ /*============================================================================ 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 __mitkLocaleSwitch_h #define __mitkLocaleSwitch_h #include "MitkCoreExports.h" namespace mitk { /** \brief Convenience class to temporarily change the current locale. This helper class can be used to switch to a specific locale for a couple of operations. Once the class is destroyed, the previous locale will be restored. This avoids calling or forgetting to call setlocale() in multiple return locations. Typically this is used to switch to a "C" locale when parsing or printing numbers, in order to consistently get "." and not "," as a decimal separator. WARNING: Please be aware that using setlocale and there for is not thread - safe. So use this class with care (see tast T24295 for more information. + safe. So use this class with care (see task T24295 for more information. This switch is especially use full if you have to deal with third party code - where you have to controll the locale via set locale + where you have to control the locale via set locale \code { mitk::LocaleSwitch localeSwitch("C");// installs C locale until the end of the function ExternalLibraryCall(); //that might throw or what ever. } \endcode If you just want to control you own stringstream operations use imbue instead, as it is threadsafe. E.G.: \code std::string toString(int number) { std::ostringstream parser; parser.imbue(std::locale("C")); parser << number; return parser.str(); } \endcode */ struct MITKCORE_EXPORT LocaleSwitch { explicit LocaleSwitch(const char *newLocale); ~LocaleSwitch(); LocaleSwitch(LocaleSwitch &) = delete; LocaleSwitch operator=(LocaleSwitch &) = delete; private: struct Impl; Impl *m_LocaleSwitchImpl; }; } #endif // __mitkLocaleSwitch_h diff --git a/Modules/Core/include/mitkLog.h b/Modules/Core/include/mitkLog.h index 1d74b0d687..95c6a7086c 100644 --- a/Modules/Core/include/mitkLog.h +++ b/Modules/Core/include/mitkLog.h @@ -1,104 +1,104 @@ /*============================================================================ 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 _MITK_LOG_H #define _MITK_LOG_H #include #include namespace mitk { /*! \brief mbilog backend implementation for mitk */ class MITKCORE_EXPORT LoggingBackend : public mbilog::TextBackendBase { public: /** \brief overloaded method for receiving log message from mbilog */ void ProcessMessage(const mbilog::LogMessage &) override; /** \brief registers MITK logging backend at mbilog */ static void Register(); /** \brief Unregisters MITK logging backend at mbilog */ static void Unregister(); /** \brief Sets extra log file path (additionally to the console log) */ static void SetLogFile(const char *file); /** @brief Activates and handles a rolling log file with the given prefix and path. * This method handles 10 log files with a given prefix, e.g. "myLogFile". * The 10 log files will then look like this: * myLogFile-0.log (current file) * myLogFile-1.log (last file) * myLogFile-2.log * [...] * myLogFile-9.log * * Everytime this method this called, normally when MITK is started, then * all log files are copied one file below (0 to 1, 1 to 2, and so on). - * The oldes logfile (number 9) is always deleted. So users have access to + * The oldest logfile (number 9) is always deleted. So users have access to * the log files of the last 10 runs. * @throws mitk::Exception Throws an exception if there is a problem with renaming the logfiles, e.g., because of * file access problems. * @param prefixPath Should hold the prefix of the logfile together with its path. E.g., * "C:/programs/mitk/myLogFile". */ static void RotateLogFiles(const std::string &prefixPath); /** @brief Increments the names of logfiles with the given prefixPath. * This means, if the prefixPath is "myLogFile" myLogFile-0.log * is renamed to myLogFile-1.log, myLogFile-1.log to myLogFile-2.log, * and so on. The oldest logfile is deleted. The number of log files is * defined by the parameter "numLogFiles". The first logfile name is - * "free" (e.g., [prefix]-0.log) again. This name is retured. + * "free" (e.g., [prefix]-0.log) again. This name is returned. * @param prefixPath Should hold the prefix of the logfile together with its path. E.g., * "C:/programs/mitk/myLogFile". * @param numLogFiles Sets the number of logfiles. Default value is 10. This means logfiles from [prefix]-0.log * to [prefix]-1.log are stored. * @return Returns a new logfile name which is free again because the old first log file was renamed. * @throws mitk::Exception Throws an exception if there is a problem with renaming the logfiles, e.g., because of * file access problems. */ static std::string IncrementLogFileNames(const std::string &prefixPath, int numLogFiles = 10); /** @return Returns the log file if there is one. Returns an empty string * if no log file is active. */ static std::string GetLogFile(); /** \brief Enables an additional logging output window by means of itk::outputwindow * This might be relevant for showing log output in applications with no default output console */ static void EnableAdditionalConsoleWindow(bool enable); /** \brief Automatically extracts and removes the "--logfile " parameters from the standard C main(argc,argv) * parameter list and calls SetLogFile if needed */ static void CatchLogFileCommandLineParameter(int &argc, char **argv); mbilog::OutputType GetOutputType() const override; protected: /** Checks if a file exists. * @return Returns true if the file exists, false if not. */ static bool CheckIfFileExists(const std::string &filename); }; } #endif diff --git a/Modules/Core/include/mitkMapper.h b/Modules/Core/include/mitkMapper.h index c30e6411e0..b4200f76b4 100644 --- a/Modules/Core/include/mitkMapper.h +++ b/Modules/Core/include/mitkMapper.h @@ -1,211 +1,211 @@ /*============================================================================ 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 MAPPER_H_HEADER_INCLUDED_C1E6EA08 #define MAPPER_H_HEADER_INCLUDED_C1E6EA08 #include "mitkBaseRenderer.h" #include "mitkCommon.h" #include "mitkLevelWindow.h" #include "mitkLocalStorageHandler.h" #include "mitkVtkPropRenderer.h" #include #include #include class vtkWindow; class vtkProp; namespace mitk { class BaseRenderer; class BaseData; class DataNode; /** \brief Base class of all mappers, Vtk as well as OpenGL mappers * * By the help of mappers, the input data is transformed to tangible primitives, * such as surfaces, points, lines, etc. * This is the base class of all mappers, Vtk as well as OpenGL mappers. * Subclasses of mitk::Mapper control the creation of rendering primitives * that interface to the graphics library (e.g., OpenGL, vtk). * * \todo Should Mapper be a subclass of ImageSource? * \ingroup Mapper */ class MITKCORE_EXPORT Mapper : public itk::Object { public: mitkClassMacroItkParent(Mapper, itk::Object); /** \brief Set the DataNode containing the data to map */ itkSetObjectMacro(DataNode, DataNode); /** \brief Get the DataNode containing the data to map. * Method only returns valid DataNode Pointer if the mapper belongs to a data node. * Otherwise, the returned DataNode Pointer might be invalid. */ virtual DataNode *GetDataNode() const; /**\brief Get the data to map * * Returns the mitk::BaseData object associated with this mapper. * \return the mitk::BaseData associated with this mapper. * \deprecatedSince{2013_03} Use GetDataNode()->GetData() instead to access the data */ DEPRECATED(BaseData *GetData() const); /** \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode()->GetColor(...) instead to get the color */ DEPRECATED(virtual bool GetColor(float rgb[3], BaseRenderer *renderer, const char *name = "color") const); /** \brief Convenience access method for visibility properties (instances * of BoolProperty) * \return \a true property was found * \sa IsVisible * \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead to get the visibility */ DEPRECATED(virtual bool GetVisibility(bool &visible, BaseRenderer *renderer, const char *name = "visible") const); /** \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode()->GetOpacity(...) instead to get the opacity */ DEPRECATED(virtual bool GetOpacity(float &opacity, BaseRenderer *renderer, const char *name = "opacity") const); /** \brief Convenience access method for color properties (instances of * LevelWindoProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode->GetLevelWindow(...) instead to get the levelwindow */ DEPRECATED(virtual bool GetLevelWindow(LevelWindow &levelWindow, BaseRenderer *renderer, const char *name = "levelwindow") const); /** \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead */ DEPRECATED(virtual bool IsVisible(BaseRenderer *renderer, const char *name = "visible") const); /** \brief Returns whether this is an vtk-based mapper * \deprecatedSince{2013_03} All mappers of superclass VTKMapper are vtk based, use a dynamic_cast instead */ virtual bool IsVtkBased() const { return true; } /** \brief Calls the time step of the input data for the specified renderer and checks * whether the time step is valid and calls method GenerateDataForRenderer() */ virtual void Update(BaseRenderer *renderer); /** \brief Responsible for calling the appropriate render functions. * To be implemented in sub-classes. */ virtual void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) = 0; /** * \brief Apply specific color and opacity properties read from the PropertyList. * Reimplemented in GLmapper (does not use the actor) and the VtkMapper class. * The function is called by the individual mapper (mostly in the ApplyProperties() or ApplyAllProperties() * method). */ virtual void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor *actor = nullptr) = 0; /** \brief Set default values of properties used by this mapper * to \a node * * \param node The node for which the properties are set * \param overwrite overwrite existing properties (default: \a false) * \param renderer defines which property list of node is used * (default: \a nullptr, i.e. default property list) */ static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief Returns the current time step as calculated from the renderer */ int GetTimestep() const { return m_TimeStep; } /** Returns true if this Mapper currently allows for Level-of-Detail rendering. * This reflects whether this Mapper currently invokes StartEvent, EndEvent, and * ProgressEvent on BaseRenderer. */ virtual bool IsLODEnabled(BaseRenderer * /*renderer*/) const { return false; } protected: /** \brief explicit constructor which disallows implicit conversions */ explicit Mapper(); /** \brief virtual destructor in order to derive from this class */ ~Mapper() override; /** \brief Generate the data needed for rendering (independent of a specific renderer) * \deprecatedSince{2013_03} Use GenerateDataForRenderer(BaseRenderer* renderer) instead. */ DEPRECATED(virtual void GenerateData()) {} /** \brief Generate the data needed for rendering into \a renderer */ virtual void GenerateDataForRenderer(BaseRenderer * /* renderer */) {} /** \brief Updates the time step, which is sometimes needed in subclasses */ virtual void CalculateTimeStep(BaseRenderer *renderer); /** \brief Reset the mapper (i.e., make sure that nothing is displayed) if no * valid data is present. In most cases the reimplemented function * disables the according actors (toggling visibility off) * * To be implemented in sub-classes. */ virtual void ResetMapper(BaseRenderer * /*renderer*/) {} mitk::DataNode *m_DataNode; private: /** \brief The current time step of the dataset to be rendered, * for use in subclasses. * The current timestep can be accessed via the GetTimestep() method. */ int m_TimeStep; /** \brief copy constructor */ Mapper(const Mapper &); /** \brief assignment operator */ Mapper &operator=(const Mapper &); public: - /** \brief Base class for mapper specific rendering ressources. + /** \brief Base class for mapper specific rendering resources. */ class MITKCORE_EXPORT BaseLocalStorage { public: BaseLocalStorage() = default; virtual ~BaseLocalStorage() = default; BaseLocalStorage(const BaseLocalStorage &) = delete; BaseLocalStorage & operator=(const BaseLocalStorage &) = delete; bool IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) const; inline void UpdateGenerateDataTime() { m_LastGenerateDataTime.Modified(); } inline itk::TimeStamp &GetLastGenerateDataTime() { return m_LastGenerateDataTime; } protected: /** \brief timestamp of last update of stored data */ itk::TimeStamp m_LastGenerateDataTime; }; }; } // namespace mitk #endif /* MAPPER_H_HEADER_INCLUDED_C1E6EA08 */ diff --git a/Modules/Core/include/mitkMaterial.h b/Modules/Core/include/mitkMaterial.h index 271a6bb2dc..bf42e9e30f 100644 --- a/Modules/Core/include/mitkMaterial.h +++ b/Modules/Core/include/mitkMaterial.h @@ -1,453 +1,453 @@ /*============================================================================ 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 _MITK_MATERIAL_H_ #define _MITK_MATERIAL_H_ #include #include #include #include #include #include #include namespace mitk { /** * Encapsulates 3D visualization properties which are forwarded to vtk for * color mapping. This includes color, specular coefficient and power, opacity * interpolation type (flat, gouraud, phong) and representation (points, * wireframe or surface). * * @see vtkProperty */ class MITKCORE_EXPORT Material : public itk::Object { public: mitkClassMacroItkParent(Material, itk::Object); typedef itk::RGBPixel Color; enum InterpolationType { Flat, Gouraud, Phong }; enum RepresentationType { Points, Wireframe, Surface }; /** * Constructor. Materials are set to the following default values: * Color (0.5, 0.5, 0.5) color coefficient 1.0, specular color (1.0, 1.0, 1.0), * specular coefficient 1.0, specular power 10, opacity 1.0, interpolation * Gouraud, representation Surface. */ static Pointer New() { Pointer smartPtr = new Material(); smartPtr->UnRegister(); return smartPtr; } /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * @param color the material color in RGB. Each RGB value should be in the * range [0..1] * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ static Pointer New(Color color, double opacity = 1.0f) { Pointer smartPtr = new Material(color, opacity); smartPtr->UnRegister(); return smartPtr; } /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * @param red the red component of the materials color (range [0..1]) * @param green the green component of the materials color (range [0..1]) * @param blue the blue component of the materials color (range [0..1]) * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ static Pointer New(double red, double green, double blue, double opacity = 1.0f) { Pointer smartPtr = new Material(red, green, blue, opacity); smartPtr->UnRegister(); return smartPtr; } /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * @param red the red component of the materials color (range [0..1]) * @param green the green component of the materials color (range [0..1]) * @param blue the blue component of the materials color (range [0..1]) * @param colorCoefficient a scaling factor for the color coefficient which * will be multiplied with each color component (range [0..1]). * @param specularCoefficient controls in combination with the specular power * how shiny the material will appear (range [0..1]). * @param specularPower controls in combination with the specular coefficient * how shiny the material will appear (range [0..inf]). * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ static Pointer New(double red, double green, double blue, double colorCoefficient, double specularCoefficient, double specularPower, double opacity) { Pointer smartPtr = new Material(red, green, blue, colorCoefficient, specularCoefficient, specularPower, opacity); smartPtr->UnRegister(); return smartPtr; } /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * * @param color the material color in RGB. Each RGB value should be in the * range [0..1] * @param colorCoefficient a scaling factor for the color coefficient which * will be multiplied with each color component (range [0..1]). * @param specularCoefficient controls in combination with the specular power * how shiny the material will appear (range [0..1]). * @param specularPower controls in combination with the specular coefficient * how shiny the material will appear (range [0..inf]). * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ static Pointer New( Color color, double colorCoefficient, double specularCoefficient, double specularPower, double opacity) { Pointer smartPtr = new Material(color, colorCoefficient, specularCoefficient, specularPower, opacity); smartPtr->UnRegister(); return smartPtr; } /** * Copy constructor */ mitkNewMacro1Param(Material, const Material &); /** - * Copy constructor, provided for convinience. The values are copied from property + * Copy constructor, provided for convenience. The values are copied from property * and afterwards the values provided for red green blue and opacity are written into the object. */ static Pointer New( const Material &property, double red, double green, double blue, double opacity = 1.0, std::string name = "") { Pointer smartPtr = new Material(property, red, green, blue, opacity, name); smartPtr->UnRegister(); return smartPtr; } virtual bool Assignable(const Material &other) const; virtual Material &operator=(const Material &other); /* Sets the materials color in RGB space. The rgb components have to be * in the range [0..1] * @param color the new color of the material */ virtual void SetColor(Color color); /** * Sets the materials color in RGB space. The rgb components have to be * in the range [0..1] * @param red the red component of the materials color (range [0..1]) * @param green the green component of the materials color (range [0..1]) * @param blue the blue component of the materials color (range [0..1]) */ virtual void SetColor(double red, double green, double blue); /** * Sets a attenuation coefficient for the color. A value of 0 results in * a black object. VAlid range is [0..1] * @param coefficient the color attenuation coefficient */ virtual void SetColorCoefficient(double coefficient); /** * Sets the specular color * @param color the specular color in RGB. Each RGB value should be in the * range [0..1] */ virtual void SetSpecularColor(Color color); /** * Sets the specular color * @param red the red component of the specular color (range [0..1]) * @param green the green component of the specular color (range [0..1]) * @param blue the blue component of the specular color (range [0..1]) */ virtual void SetSpecularColor(double red, double green, double blue); /** * Sets the specular coefficient which controls the shininess of the object * together with the specular power * @param specularCoefficient the new specular coefficient. Valid range * is [0..1] */ virtual void SetSpecularCoefficient(double specularCoefficient); /** * Sets the specular power which controls the shininess of the object * together with the specular coefficient * @param specularPower the new specular coefficient. Valid range * is [0..inf] */ virtual void SetSpecularPower(double specularPower); /** * Sets the opacity of the material, which controls how transparent the * object appears. Valid range is [0..1], where 0 means fully transparent * and 1 means a solid surface. * @param opacity the new opacity of the material */ virtual void SetOpacity(double opacity); /** * Sets the surface interpolation method of the object rendered using the * given materials. Valid Interopation types are Flat, Gouraud and Phong. * See any computer graphics book for their meaning * @param interpolation the interpolation method used for rendering of * surfaces. */ virtual void SetInterpolation(InterpolationType interpolation); /** * Sets the surface representation method of the object rendered using the * given materials. Valid Interopation types are Points, Wireframe and * Surface. * @param representation the representation method used for rendering of * surfaces. */ virtual void SetRepresentation(RepresentationType representation); /** * Set/Get the width of a Line. The width is expressed in screen units. The default is 1.0. */ virtual void SetLineWidth(float lineWidth); /** * @returns the color of the material */ virtual Color GetColor() const; /** * @returns the color coefficient of the material. Range is [0..1] */ virtual double GetColorCoefficient() const; /** * @returns the specular color of the material in rgb values, which * range from 0 .. 1 */ virtual Color GetSpecularColor() const; /** * @returns the specular coefficient used for rendering. Range is [0..1] */ virtual double GetSpecularCoefficient() const; /** * @returns the specular power. Ranges from 0 to infinity */ virtual double GetSpecularPower() const; /** * @returns the opacity of the material. Ranges from 0 to 1 */ virtual double GetOpacity() const; /** * @returns the interpolation method used for rendering. */ virtual InterpolationType GetInterpolation() const; /** * @returns the representation type used for rendering. */ virtual RepresentationType GetRepresentation() const; /** * @returns the interpolation method used for rendering using the predefined * vtk constants. */ virtual int GetVtkInterpolation() const; /** * @returns the representation type used for rendering using the predefined * vtk constants. */ virtual int GetVtkRepresentation() const; /** * @returns the line width used for wireframe rendering as a fraction of screen units */ virtual float GetLineWidth() const; /** * Fills the current materials with the properties of the * given material. * @param property the materials which should be copied in the * current materials */ virtual void Initialize(const Material &property); /** * comparison operator which uses the member variables for * comparison */ virtual bool operator==(const Material &property) const; /** * Dumps the properties to the out stream out */ void PrintSelf(std::ostream &os, itk::Indent) const override; /** * Sets an optional name which may be associated with the material property * Please note, that this name is NOT forwarded to the data tree node * as the node name */ itkSetMacro(Name, std::string); /** * returns the name associated with the material property */ itkGetConstMacro(Name, std::string); protected: /** * Constructor. Materials are set to the following default values: * Color (0.5, 0.5, 0.5) color coefficient 1.0, specular color (1.0, 1.0, 1.0), * specular coefficient 1.0, specular power 10, opacity 1.0, interpolation * Gouraud, representation Surface. */ Material(); /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * @param color the material color in RGB. Each RGB value should be in the * range [0..1] * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ Material(Color color, double opacity = 1.0f); /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * @param red the red component of the materials color (range [0..1]) * @param green the green component of the materials color (range [0..1]) * @param blue the blue component of the materials color (range [0..1]) * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ Material(double red, double green, double blue, double opacity = 1.0f); /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * @param red the red component of the materials color (range [0..1]) * @param green the green component of the materials color (range [0..1]) * @param blue the blue component of the materials color (range [0..1]) * @param colorCoefficient a scaling factor for the color coefficient which * will be multiplied with each color component (range [0..1]). * @param specularCoefficient controls in combination with the specular power * how shiny the material will appear (range [0..1]). * @param specularPower controls in combination with the specular coefficient * how shiny the material will appear (range [0..inf]). * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ Material(double red, double green, double blue, double colorCoefficient, double specularCoefficient, double specularPower, double opacity); /** * Constructor. All values besides the given ones are set to defaults as * described in the default constructor * * @param color the material color in RGB. Each RGB value should be in the * range [0..1] * @param colorCoefficient a scaling factor for the color coefficient which * will be multiplied with each color component (range [0..1]). * @param specularCoefficient controls in combination with the specular power * how shiny the material will appear (range [0..1]). * @param specularPower controls in combination with the specular coefficient * how shiny the material will appear (range [0..inf]). * @param opacity the opacity of the material. 0.0 means fully transparent * and 1.0 means solid. */ Material(Color color, double colorCoefficient, double specularCoefficient, double specularPower, double opacity); /** * Copy constructor */ Material(const Material &property); /** - * Copy constructor, provided for convinience. The values are copied from property + * Copy constructor, provided for convenience. The values are copied from property * and afterwards the values provided for red green blue and opacity are written into the object. */ Material( const Material &property, double red, double green, double blue, double opacity = 1.0, std::string name = ""); virtual void InitializeStandardValues(); virtual void Update(); std::string m_Name; Color m_Color; Color m_SpecularColor; double m_ColorCoefficient; double m_SpecularCoefficient; double m_SpecularPower; double m_Opacity; float m_LineWidth; InterpolationType m_Interpolation; RepresentationType m_Representation; }; typedef itk::VectorContainer MaterialVectorContainer; } #endif diff --git a/Modules/Core/include/mitkMemoryUtilities.h b/Modules/Core/include/mitkMemoryUtilities.h index 502faf095c..33e94a49bc 100644 --- a/Modules/Core/include/mitkMemoryUtilities.h +++ b/Modules/Core/include/mitkMemoryUtilities.h @@ -1,91 +1,91 @@ /*============================================================================ 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 _MITK_MEMORY_UTILITIES_H_ #define _MITK_MEMORY_UTILITIES_H_ #include #include namespace mitk { class MITKCORE_EXPORT MemoryUtilities { public: /** * Returns the memory usage of the current process in bytes. * On linux, this refers to the virtual memory allocated by * the process (the VIRT column in top). * On windows, this refery to the size in bytes of the working * set pages (the "Speicherauslastung" column in the task manager). */ static size_t GetProcessMemoryUsage(); /** - * Returns the total size of phyiscal memory in bytes + * Returns the total size of physical memory in bytes */ static size_t GetTotalSizeOfPhysicalRam(); /** * Allocates an array of a given number of elements. Each element * has a size of sizeof(ElementType). The function returns nullptr, if the array * could not be allocated. * @param numberOfElements the number of elements of the array * @param noThrow if set to false, an exception is thrown if memory allocation * fails. If set to true, a itk::MemoryAllocationError is thrown * @returns a pointer to the allocated array. If noThrow == true, nullptr is returned * if memory allocation failed. */ template static ElementType *AllocateElements(size_t numberOfElements, bool noThrow = false) { // Encapsulate all image memory allocation here to throw an // exception when memory allocation fails even when the compiler // does not do this by default. ElementType *data = nullptr; try { data = new ElementType[numberOfElements]; } catch (...) { data = nullptr; } if ((data == nullptr) && (noThrow == false)) { throw itk::MemoryAllocationError(__FILE__, __LINE__, "Failed to allocate memory.", ITK_LOCATION); } return data; } /** * Deletes an array of elements previously allocated by AllocateElements. * @param elements the array to delete. Not that nullptr is an accepted value. */ template static void DeleteElements(ElementType *elements) { if (elements != nullptr) { delete[] elements; } } protected: #ifndef _MSC_VER static int ReadStatmFromProcFS( int *size, int *res, int *shared, int *text, int *sharedLibs, int *stack, int *dirtyPages); #endif }; } // end of namespace mitk #endif diff --git a/Modules/Core/include/mitkMessage.h b/Modules/Core/include/mitkMessage.h index 564472316c..3b8723a8a2 100644 --- a/Modules/Core/include/mitkMessage.h +++ b/Modules/Core/include/mitkMessage.h @@ -1,697 +1,697 @@ /*============================================================================ 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 mitkMessageHIncluded #define mitkMessageHIncluded #include #include #include /** * Adds a Message<> variable and methods to add/remove message delegates to/from * this variable. */ #define mitkNewMessageMacro(msgHandleObject) \ private: \ ::mitk::Message<> m_##msgHandleObject##Message; \ \ public: \ inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessageWithReturnMacro(msgHandleObject, returnType) \ private: \ ::mitk::Message m_##msgHandleObject##Message; \ \ public: \ inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessage1Macro(msgHandleObject, type1) \ private: \ ::mitk::Message1 m_##msgHandleObject##Message; \ \ public: \ void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1 &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1 &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessage2Macro(msgHandleObject, type1, type2) \ private: \ ::mitk::Message2 m_##msgHandleObject##Message; \ \ public: \ void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2 &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2 &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } namespace mitk { template class MessageAbstractDelegate { public: virtual ~MessageAbstractDelegate() {} virtual A Execute() const = 0; virtual bool operator==(const MessageAbstractDelegate *cmd) const = 0; virtual MessageAbstractDelegate *Clone() const = 0; }; template class MessageAbstractDelegate1 { public: virtual ~MessageAbstractDelegate1() {} virtual A Execute(T t) const = 0; virtual bool operator==(const MessageAbstractDelegate1 *cmd) const = 0; virtual MessageAbstractDelegate1 *Clone() const = 0; }; template class MessageAbstractDelegate2 { public: virtual ~MessageAbstractDelegate2() {} virtual A Execute(T t, U u) const = 0; virtual bool operator==(const MessageAbstractDelegate2 *cmd) const = 0; virtual MessageAbstractDelegate2 *Clone() const = 0; }; template class MessageAbstractDelegate3 { public: virtual ~MessageAbstractDelegate3() {} virtual A Execute(T t, U u, V v) const = 0; virtual bool operator==(const MessageAbstractDelegate3 *cmd) const = 0; virtual MessageAbstractDelegate3 *Clone() const = 0; }; template class MessageAbstractDelegate4 { public: virtual ~MessageAbstractDelegate4() {} virtual A Execute(T t, U u, V v, W w) const = 0; virtual bool operator==(const MessageAbstractDelegate4 *cmd) const = 0; virtual MessageAbstractDelegate4 *Clone() const = 0; }; /** * This class essentially wraps a function pointer with signature * A(R::*function)(). A is the return type of your callback function * and R the type of the class implementing the function. * * Use this class to add a callback function to * messages without parameters. */ template class MessageDelegate : public MessageAbstractDelegate { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate(R *object, A (R::*memberFunctionPointer)()) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate() override {} // override function "Call" A Execute() const override { return (m_Object->*m_MemberFunctionPointer)(); // execute member function } bool operator==(const MessageAbstractDelegate *c) const override { const MessageDelegate *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate *Clone() const override { return new MessageDelegate(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(); // pointer to member function }; /** * This class essentially wraps a function pointer with signature * A(R::*function)(T). A is the return type of your callback function, * R the type of the class implementing the function and T the type * of the argument. * * Use this class to add a callback function to * messages with one parameter. * * If you need more parameters, use MessageDelegate2 etc. */ template class MessageDelegate1 : public MessageAbstractDelegate1 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate1(R *object, A (R::*memberFunctionPointer)(T)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate1() override {} // override function "Call" A Execute(T t) const override { return (m_Object->*m_MemberFunctionPointer)(t); // execute member function } bool operator==(const MessageAbstractDelegate1 *c) const override { const MessageDelegate1 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate1 *Clone() const override { return new MessageDelegate1(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T); // pointer to member function }; template class MessageDelegate2 : public MessageAbstractDelegate2 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate2(R *object, A (R::*memberFunctionPointer)(T, U)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate2() override {} // override function "Call" A Execute(T t, U u) const override { return (m_Object->*m_MemberFunctionPointer)(t, u); // execute member function } bool operator==(const MessageAbstractDelegate2 *c) const override { const MessageDelegate2 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate2 *Clone() const override { return new MessageDelegate2(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U); // pointer to member function }; template class MessageDelegate3 : public MessageAbstractDelegate3 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate3(R *object, A (R::*memberFunctionPointer)(T, U, V)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate3() override {} // override function "Call" A Execute(T t, U u, V v) const override { return (m_Object->*m_MemberFunctionPointer)(t, u, v); // execute member function } bool operator==(const MessageAbstractDelegate3 *c) const override { const MessageDelegate3 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate3 *Clone() const override { return new MessageDelegate3(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U, V); // pointer to member function }; template class MessageDelegate4 : public MessageAbstractDelegate4 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate4(R *object, A (R::*memberFunctionPointer)(T, U, V, W)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~MessageDelegate4() {} // override function "Call" virtual A Execute(T t, U u, V v, W w) const { return (m_Object->*m_MemberFunctionPointer)(t, u, v, w); // execute member function } bool operator==(const MessageAbstractDelegate4 *c) const { const MessageDelegate4 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate4 *Clone() const { return new MessageDelegate4(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U, V, W); // pointer to member function }; template class MessageBase { public: typedef std::vector ListenerList; virtual ~MessageBase() { for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { delete *iter; } } MessageBase() {} MessageBase(const MessageBase &o) { for (typename ListenerList::iterator iter = o.m_Listeners.begin(); iter != o.m_Listeners.end(); ++iter) { m_Listeners.push_back((*iter)->Clone()); } } MessageBase &operator=(const MessageBase &o) { MessageBase tmp(o); std::swap(tmp.m_Listeners, this->m_Listeners); return *this; } void AddListener(const AbstractDelegate &delegate) const { AbstractDelegate *msgCmd = delegate.Clone(); m_Mutex.lock(); for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { if ((*iter)->operator==(msgCmd)) { delete msgCmd; m_Mutex.unlock(); return; } } m_Listeners.push_back(msgCmd); m_Mutex.unlock(); } void operator+=(const AbstractDelegate &delegate) const { this->AddListener(delegate); } void RemoveListener(const AbstractDelegate &delegate) const { m_Mutex.lock(); for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { if ((*iter)->operator==(&delegate)) { delete *iter; m_Listeners.erase(iter); m_Mutex.unlock(); return; } } m_Mutex.unlock(); } void operator-=(const AbstractDelegate &delegate) const { this->RemoveListener(delegate); } const ListenerList &GetListeners() const { return m_Listeners; } bool HasListeners() const { return !m_Listeners.empty(); } bool IsEmpty() const { return m_Listeners.empty(); } protected: /** * \brief List of listeners. * * This is declared mutable for a reason: Imagine an object that sends out notifications and * someone gets a const Database object, because he/she should not write to the * database. He/she should anyway be able to register for notifications about changes in the database * -- this is why AddListener and RemoveListener are declared const. m_Listeners must be * mutable so that AddListener and RemoveListener can modify it regardless of the object's constness. */ mutable ListenerList m_Listeners; mutable std::mutex m_Mutex; }; /** * \brief Event/message/notification class. * * \sa mitk::BinaryThresholdTool * \sa QmitkBinaryThresholdToolGUI * * This totally ITK, Qt, VTK, whatever toolkit independent class * allows one class to send out messages and another class to * receive these message. This class is templated over the * return type (A) of the callback functions. * There are variations of this class * (Message1, Message2, etc.) for sending * one, two or more parameters along with the messages. * * This is an implementation of the Observer pattern. * * \li There is no guarantee about the order of which observer is notified first. At the moment the observers which * register first will be notified first. * \li Notifications are synchronous, by direct method calls. There is no support for asynchronous messages. * * To conveniently add methods for registering/unregistering observers * to Message variables of your class, you can use the mitkNewMessageMacro * macros. */ template class Message : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send() const { ListenerList listeners; { this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(); } } void operator()() const { this->Send(); } }; // message with 1 parameter and return type template class Message1 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t) const { ListenerList listeners; { this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t); } } void operator() (T t) const { this->Send(t); } }; // message with 2 parameters and return type template class Message2 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u) const { ListenerList listeners; { this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u); } } void operator()(T t, U u) const { this->Send(t, u); } }; // message with 3 parameters and return type template class Message3 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u, V v) const { ListenerList listeners; { this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); this->m_Mutex.unlock(); } for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u, v); } } void operator()(T t, U u, V v) const { this->Send(t, u, v); } }; // message with 4 parameters and return type template class Message4 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u, V v, W w) const { ListenerList listeners; { this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); this->m_Mutex.unlock(); } for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u, v, w); } } void operator()(T t, U u, V v, W w) const { this->Send(t, u, v, w); } }; /* Here is an example how to use the macros and templates: * * // An object to be send around * class Law * { * private: * std::string m_Description; * * public: * * Law(const std::string law) : m_Description(law) * { } * * std::string GetDescription() const * { * return m_Description; * } * }; * * // The NewtonMachine will issue specific events * class NewtonMachine * { * mitkNewMessageMacro(AnalysisStarted); * mitkNewMessage1Macro(AnalysisStopped, bool); * mitkNewMessage1Macro(LawDiscovered, const Law&); * * public: * * void StartAnalysis() * { * // send the "started" signal * m_AnalysisStartedMessage(); * * // we found a new law of nature by creating one :-) * Law massLaw("F=ma"); * m_LawDiscoveredMessage(massLaw); * } * * void StopAnalysis() * { * // send the "stop" message with false, indicating -* // that no error occured +* // that no error occurred * m_AnalysisStoppedMessage(false); * } * }; * * class Observer * { * private: * * NewtonMachine* m_Machine; * * public: * * Observer(NewtonMachine* machine) : m_Machine(machine) * { * // Add "observers", i.e. function pointers to the machine * m_Machine->AddAnalysisStartedListener( * ::mitk::MessageDelegate(this, &Observer::MachineStarted)); * m_Machine->AddAnalysisStoppedListener( * ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); * m_Machine->AddLawDiscoveredListener( * ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); * } * * ~Observer() * { * // Always remove your observers when finished * m_Machine->RemoveAnalysisStartedListener( * ::mitk::MessagDelegate(this, &Observer::MachineStarted)); * m_Machine->RemoveAnalysisStoppedListener( * ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); * m_Machine->RemoveLawDiscoveredListener( * ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); * } * * void MachineStarted() * { * std::cout << "Observed machine has started" << std::endl; * } * * void MachineStopped(bool error) * { * std::cout << "Observed machine stopped " << (error ? "with an error" : "") << std::endl; * } * * void LawDiscovered(const Law& law) * { * std::cout << "New law of nature discovered: " << law.GetDescription() << std::endl; * } * }; * * NewtonMachine newtonMachine; * Observer observer(&newtonMachine); * * // This will send two events to registered observers * newtonMachine.StartAnalysis(); * // This will send one event to registered observers * newtonMachine.StopAnalysis(); * * Another example of how to use these message classes can be * found in the directory Testing, file mitkMessageTest.cpp * */ } // namespace #endif diff --git a/Modules/Core/include/mitkMultiComponentImageDataComparisonFilter.h b/Modules/Core/include/mitkMultiComponentImageDataComparisonFilter.h index 169a6436f2..da5b55c6fa 100644 --- a/Modules/Core/include/mitkMultiComponentImageDataComparisonFilter.h +++ b/Modules/Core/include/mitkMultiComponentImageDataComparisonFilter.h @@ -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. ============================================================================*/ #ifndef MITKMULTICOMPONENTIMAGEDATACOMPARISONFILTER_H #define MITKMULTICOMPONENTIMAGEDATACOMPARISONFILTER_H // mitk includes #include "mitkCompareImageDataFilter.h" #include "mitkImageToImageFilter.h" // struct CompareFilterResults; namespace mitk { /*! Documentation: * \brief Filter for comparing two multi channel mitk::Image objects by channel wise by pixel values * * The comparison is channel- / pixel-wise. */ class MITKCORE_EXPORT MultiComponentImageDataComparisonFilter : public ImageToImageFilter { public: mitkClassMacro(MultiComponentImageDataComparisonFilter, ImageToImageFilter); itkSimpleNewMacro(Self); /*! /brief */ void SetTestImage(const Image *_arg); const Image *GetTestImage(); /*! /brief */ void SetValidImage(const Image *_arg); const Image *GetValidImage(); /*! /brief Specify the tolerance of the image data comparison /param Tolerance Default is 0.0f. */ itkSetMacro(Tolerance, double); itkGetMacro(Tolerance, double); /*! /brief */ void SetCompareFilterResult(CompareFilterResults *results); /*! /brief Get the detailed results of the comparison run * /sa CompareFilterResults */ CompareFilterResults *GetCompareFilterResult(); /*! /brief Get the result of the comparison * The method compares only the number of pixels with differences. It returns true if the amount * is under the specified threshold. To get the complete results, use the GetCompareResults method. - * Returns false also if the itk ComparisionImageFilter raises an exception during update. + * Returns false also if the itk ComparisonImageFilter raises an exception during update. * /param threshold Allowed percentage of pixels with differences (between 0.0...1.0) */ bool GetResult(double threshold = 0.0f); protected: MultiComponentImageDataComparisonFilter(); ~MultiComponentImageDataComparisonFilter() override; void GenerateData() override; template void CompareMultiComponentImage(const Image *testImage, const Image *validImage); double m_Tolerance; bool m_CompareResult; CompareFilterResults *m_CompareDetails; }; } // end namespace mitk #endif // MITKMULTICOMPONENTIMAGEDATACOMPARISONFILTER_H diff --git a/Modules/Core/include/mitkNodePredicateGeometry.h b/Modules/Core/include/mitkNodePredicateGeometry.h index fc33950b04..23c9c0e089 100644 --- a/Modules/Core/include/mitkNodePredicateGeometry.h +++ b/Modules/Core/include/mitkNodePredicateGeometry.h @@ -1,107 +1,107 @@ /*============================================================================ 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 MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object has the same geometry (in terms of spacing, origin, orientation) like the reference geometry. One can either check the whole time geometry of - the date node by defining a referenc time geometry or check against one given + the date node by defining a reference time geometry or check against one given reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. One can specify the tolerance/precision of the check via SetCheckPrecision(), SetCheckCoordinatePrecision() or SetCheckDirectionPrecision(). @remark The default tolerance for coordinate checks is defined by NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION. The default tolerance for direction checks is defined by NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION. Both are not as strict as mitk::eps. The reason is, that, for the typical use of the node predicate, mitk::eps would be to pedantic, as we encounter often rounding differences/errors in real world data sets. For more details, see the documentation of the aforementioned constants. We have introduced two different precision values because differences are less impactful for coordinates than for direction values. Therefore we can relax coordinate checks more then direction checks. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateGeometry, const TimeGeometry*); mitkNewMacro1Param(NodePredicateGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateGeometry, const BaseGeometry*, TimePointType); /** Sets CheckCoordinatePrecision and CheckDirectionPrecision to the passed value.*/ void SetCheckPrecision(mitk::ScalarType precision); itkSetMacro(CheckCoordinatePrecision, mitk::ScalarType); itkGetMacro(CheckCoordinatePrecision, mitk::ScalarType); itkSetMacro(CheckDirectionPrecision, mitk::ScalarType); itkGetMacro(CheckDirectionPrecision, mitk::ScalarType); ~NodePredicateGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ NodePredicateGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); /**Constructor that is used configures the predicate to check against the whole time geometry.*/ NodePredicateGeometry(const TimeGeometry* refGeometry); BaseGeometry::ConstPointer m_RefGeometry; TimeGeometry::ConstPointer m_RefTimeGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; /**Precision that should be used for the equal coordinate checks.*/ mitk::ScalarType m_CheckCoordinatePrecision; /**Precision that should be used for the equal direction checks.*/ mitk::ScalarType m_CheckDirectionPrecision; }; /** The default tolerance for the comparison of spatial/world coordinate equality. This tolerance is as strict as mitk::eps. The reason is, that, for the typical use of the node predicate, mitk::eps would be to pedantic. We often encounter floating point differences and practically it makes no difference e.g. if two images differ something like 0.0001 mm in size or spacing or origin.*/ constexpr double NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION = 1e-4; /** The default tolerance for the comparison of direction matrix equality. This tolerance is as strict as mitk::eps. The reason is, that, for the typical use of the node predicate, mitk::eps would be to pedantic. We often encounter floating point differences and practically it makes no difference e.g. if the elements of the direction/orientation matrix differ something like 0.000001.*/ constexpr double NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION = 1e-6; } // namespace mitk #endif /* MITKNodePredicateGeometry_H_HEADER_INCLUDED_ */ diff --git a/Modules/Core/include/mitkNodePredicateSubGeometry.h b/Modules/Core/include/mitkNodePredicateSubGeometry.h index 755d058dbf..eb7cb6f175 100644 --- a/Modules/Core/include/mitkNodePredicateSubGeometry.h +++ b/Modules/Core/include/mitkNodePredicateSubGeometry.h @@ -1,90 +1,90 @@ /*============================================================================ 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 MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object - has a geometry that is a sub geomety of the reference geometry. + has a geometry that is a sub geometry of the reference geometry. Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, - orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal + origin is on voxel grid), but the bounding box of the checked geometry is contained or equal to the bounding box of the reference geometry.\n One can either check the whole time geometry of - the data node by defining a referenc time geometry or check against one given2 + the data node by defining a reference time geometry or check against one given2 reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. One can specify the tolerance/precision of the check via SetCheckPrecision(), SetCheckCoordinatePrecision() or SetCheckDirectionPrecision(). @remark The default tolerance for coordinate checks is defined by NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION. The default tolerance for direction checks is defined by NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION. Both are not as strict as mitk::eps. The reason is, that, for the typical use of the node predicate, mitk::eps would be to pedantic, as we encounter often rounding differences/errors in real world data sets. For more details, see the documentation of the aforementioned constants. We have introduced two different precision values because differences are less impactful for coordinates than for direction values. Therefore we can relax coordinate checks more then direction checks. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateSubGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateSubGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateSubGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateSubGeometry, const BaseGeometry*, TimePointType); /** Sets CheckCoordinatePrecision and CheckDirectionPrecision to the passed value.*/ void SetCheckPrecision(mitk::ScalarType precision); itkSetMacro(CheckCoordinatePrecision, mitk::ScalarType); itkGetMacro(CheckCoordinatePrecision, mitk::ScalarType); itkSetMacro(CheckDirectionPrecision, mitk::ScalarType); itkGetMacro(CheckDirectionPrecision, mitk::ScalarType); ~NodePredicateSubGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ NodePredicateSubGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateSubGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); BaseGeometry::ConstPointer m_RefGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; /**Precision that should be used for the equal coordinate checks.*/ mitk::ScalarType m_CheckCoordinatePrecision; /**Precision that should be used for the equal direction checks.*/ mitk::ScalarType m_CheckDirectionPrecision; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkOperationActor.h b/Modules/Core/include/mitkOperationActor.h index 4dd66b28db..b3b5ee92d5 100644 --- a/Modules/Core/include/mitkOperationActor.h +++ b/Modules/Core/include/mitkOperationActor.h @@ -1,53 +1,53 @@ /*============================================================================ 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 OPERATIONACTOR_H_HEADER_INCLUDED_C16E28BD #define OPERATIONACTOR_H_HEADER_INCLUDED_C16E28BD #include #include /** Macro for checking the type of an operation */ #define mitkCheckOperationTypeMacro(OperationType, operation, newOperationName) \ \ OperationType *newOperationName = dynamic_cast(operation); \ \ if (newOperationName == nullptr) \ \ { \ - itkWarningMacro("Recieved wrong type of operation!"); \ + itkWarningMacro("Received wrong type of operation!"); \ return; \ } namespace mitk { class Operation; class OperationEvent; /** * \brief abstract class, that can be used by Undo to undo an operation. * * \ingroup Undo */ class MITKCORE_EXPORT OperationActor { public: itkTypeMacroNoParent(OperationActor) virtual ~OperationActor() { } virtual void ExecuteOperation(Operation *operation) = 0; }; } #endif /* OPERATIONACTOR_H_HEADER_INCLUDED_C16E28BD */ diff --git a/Modules/Core/include/mitkOperationEvent.h b/Modules/Core/include/mitkOperationEvent.h index 2d170eff24..97aaacbeae 100644 --- a/Modules/Core/include/mitkOperationEvent.h +++ b/Modules/Core/include/mitkOperationEvent.h @@ -1,201 +1,201 @@ /*============================================================================ 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 OPERATIONEVENT_H_HEADER_INCLUDED_C16E83FC #define OPERATIONEVENT_H_HEADER_INCLUDED_C16E83FC #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkUndoModel.h" #include #include #include namespace mitk { //##Documentation //## @brief Represents an entry of the undo or redo stack. //## //## This basic entry includes a textual description of the item and a pair of IDs. Static //## member functions handle creation and incrementing of these IDs. //## //## The GroupEventID is intended for logical grouping of several related Operations. //## Currently this is used only by PointSetDataInteractor. How this is done and when to use //## GroupEventIDs is still undocumented. //## @ingroup Undo class MITKCORE_EXPORT UndoStackItem { public: UndoStackItem(std::string description = ""); virtual ~UndoStackItem(); //##Documentation //## @brief For combining operations in groups //## //## This ID is used in the undo mechanism. - //## For separation of the seperate operations + //## For separation of the separate operations //## If the GroupEventId of two OperationEvents is equal, //## then they share one group and will be undone in case of Undo(fine==false) static int GetCurrGroupEventId(); //##Documentation //## @brief For combining operations in Objects //## //## This ID is used in the Undo-Mechanism. - //## For separation of the seperate operations + //## For separation of the separate operations //## If the ObjectEventId of two OperationEvents is equal, //## then they share one Object and will be undone in all cases of Undo(true and false). - //## they shal not be seperated, because they were produced to realize one object-change. + //## they shal not be separated, because they were produced to realize one object-change. //## for example: OE_statechange and OE_addlastpoint static int GetCurrObjectEventId(); //##Documentation //## @brief Returns the GroupEventId for this object int GetGroupEventId(); //##Documentation //## @brief Returns the ObjectEventId for this object int GetObjectEventId(); //##Documentation //## @brief Returns the textual description of this object std::string GetDescription(); virtual void ReverseOperations(); virtual void ReverseAndExecute(); //##Documentation //## @brief Increases the current ObjectEventId //## For example if a button click generates operations the ObjectEventId has to be incremented to be able to undo //the // operations. //## Difference between ObjectEventId and GroupEventId: The ObjectEventId capsulates all operations caused by one // event. //## A GroupEventId capsulates several ObjectEventIds so that several operations caused by several events can be // undone with one Undo call. static void IncCurrObjectEventId(); //##Documentation //## @brief Increases the current GroupEventId //## For example if a button click generates operations the GroupEventId has to be incremented to be able to undo //the // operations. //## Difference between ObjectEventId and GroupEventId: The ObjectEventId capsulates all operations caused by one // event. //## A GroupEventId capsulates several ObjectEventIds so that several operations caused by several events can be // undone with one Undo call. static void IncCurrGroupEventId(); protected: //##Documentation - //## @brief true, if operation and undooperation have been swaped/changed + //## @brief true, if operation and undooperation have been swapped/changed bool m_Reversed; private: static int m_CurrObjectEventId; static int m_CurrGroupEventId; int m_ObjectEventId; int m_GroupEventId; std::string m_Description; UndoStackItem(UndoStackItem &); // hide copy constructor void operator=(const UndoStackItem &); // hide operator= }; //##Documentation //## @brief Represents a pair of operations: undo and the according redo. //## //## Additionally to the base class UndoStackItem, which only provides a description of an //## item, OperationEvent does the actual accounting of the undo/redo stack. This class //## holds two Operation objects (operation and its inverse operation) and the corresponding //## OperationActor. The operations may be swapped by the //## undo models, when an OperationEvent is moved from their undo to their redo //## stack or vice versa. //## //## Note, that memory management of operation and undooperation is done by this class. //## Memory of both objects is freed in the destructor. For this, the method IsValid() is needed which holds //## information of the state of m_Destination. In case the object referenced by m_Destination is already deleted, //## isValid() returns false. //## In more detail if the destination happens to be an itk::Object (often the case), OperationEvent is informed as //soon //## as the object is deleted - from this moment on the OperationEvent gets invalid. You should //## check this flag before you call anything on destination //## //## @ingroup Undo class MITKCORE_EXPORT OperationEvent : public UndoStackItem { public: //## @brief default constructor OperationEvent(OperationActor *destination, Operation *operation, Operation *undoOperation, std::string description = ""); //## @brief default destructor //## //## removes observers if destination is valid //## and frees memory referenced by m_Operation and m_UndoOperation ~OperationEvent() override; //## @brief Returns the operation Operation *GetOperation(); //## @brief Returns the destination of the operations OperationActor *GetDestination(); friend class UndoModel; //## @brief Swaps the two operations and sets a flag, //## that it has been swapped and doOp is undoOp and undoOp is doOp void ReverseOperations() override; //##reverses and executes both operations (used, when moved from undo to redo stack) void ReverseAndExecute() override; //## @brief returns true if the destination still is present //## and false if it already has been deleted virtual bool IsValid(); protected: void OnObjectDeleted(); private: // Has to be observed for itk::DeleteEvents. // When destination is deleted, this stack item is invalid! OperationActor *m_Destination; //## reference to the operation Operation *m_Operation; //## reference to the undo operation Operation *m_UndoOperation; //## hide copy constructor OperationEvent(OperationEvent &); //## hide operator= void operator=(const OperationEvent &); // observertag used to listen to m_Destination unsigned long m_DeleteTag; //## stores if destination is valid or already has been freed bool m_Invalid; }; } // namespace mitk #endif /* OPERATIONEVENT_H_HEADER_INCLUDED_C16E83FC */ diff --git a/Modules/Core/include/mitkPixelTypeList.h b/Modules/Core/include/mitkPixelTypeList.h index 1ecbad2720..f7d0982a17 100644 --- a/Modules/Core/include/mitkPixelTypeList.h +++ b/Modules/Core/include/mitkPixelTypeList.h @@ -1,182 +1,182 @@ /*============================================================================ 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 namespace mitk { struct EmptyType { }; template struct PixelTypeList; template struct PixelTypeList { typedef T0 head; typedef PixelTypeList tail; enum { length = tail::length + 1 }; }; template <> struct PixelTypeList { enum { length = 0 }; }; template struct PixelTypeLength { enum { value = TypeList::length }; }; template ::value == 0 // out of range flag > struct GetPixelType { typedef typename GetPixelType::type type; }; //"out of range" specialization template struct GetPixelType { // if OutOfRange is 'true' the 'type' is undefined // so we'll get a compile-time error }; //"element found" specialization template struct GetPixelType { // the index is equal to the recursion step // so the result type is the head of the Typlist and stop! typedef typename TypeList::head type; }; //////////////////////////////////////////////////////////// // run-time type switch template ::value)> struct PixelTypeSwitch; template struct PixelTypeSwitch { template bool operator()(int i, F &f) { if (i == Index) { return f.operator()::type>(); } else { PixelTypeSwitch next; return next(i, f); } } }; template struct PixelTypeSwitch { template bool operator()(int, F &) { throw std::out_of_range("Index out of range"); } }; template struct AccessItkImageFunctor { typedef void (*CallBack)(T1, T2, T3); AccessItkImageFunctor( X *cl, CallBack callBack, const mitk::Image *mitkImage, T1 t1 = T1(), T2 t2 = T2(), T3 t3 = T3()) : cl(cl), callBack(callBack), mitkImage(mitkImage), pixelType(mitkImage->GetPixelType()), t1(t1), t2(t2), t3(t3) { } template bool operator()() { if (pixelType != typeid(PixelType)) return false; if (mitkImage->GetDimension() != VDimension) return false; const_cast(mitkImage)->Update(); typedef itk::Image ImageType; typedef mitk::ImageToItk ImageToItkType; itk::SmartPointer imagetoitk = ImageToItkType::New(); imagetoitk->SetInput(mitkImage); imagetoitk->Update(); cl->*callBack(imagetoitk->GetOutput(), t1, t2, t3); return true; } private: X *cl; CallBack callBack; const mitk::Image *mitkImage; const mitk::PixelType &pixelType; T1 t1; T2 t2; T3 t3; }; } // namespace mitk diff --git a/Modules/Core/include/mitkPixelTypeTraits.h b/Modules/Core/include/mitkPixelTypeTraits.h index a5f190d33a..bf91d49adc 100644 --- a/Modules/Core/include/mitkPixelTypeTraits.h +++ b/Modules/Core/include/mitkPixelTypeTraits.h @@ -1,272 +1,272 @@ /*============================================================================ 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 PIXELTYPETRAITS_H #define PIXELTYPETRAITS_H #include #include #include #include #include #include /** \file mitkPixelTypeTraits.h * * The pixel type traits are in general used for compile time resolution of the component type and * the number of components for compound types like the ones in ItkImageType. * The default values are used to define the corresponding variable also for scalar types */ namespace itk { /** Forward declaration of the Variable Length Vector class from ITK */ template class VariableLengthVector; } #define MITK_PIXEL_COMPONENT_TYPE(type, ctype, name) \ template <> \ struct mitk::MapPixelComponentType \ { \ static const int value = ctype; \ } template <> \ std::string mitk::PixelComponentTypeToString() \ { \ return name; \ } namespace mitk { static const int PixelUserType = static_cast(itk::IOPixelEnum::MATRIX) + 1; static const int PixelComponentUserType = static_cast(itk::IOComponentEnum::DOUBLE) + 1; /** * Maps pixel component types (primitive types like int, short, double, etc. and custom * types) to and integer constant. Specialize this template for custom types by using the * #MITK_PIXEL_COMPONENT_TYPE macro. */ template struct MapPixelComponentType { static const itk::IOComponentEnum value = itk::ImageIOBase::MapPixelType::CType; }; /** \brief This is an implementation of a type trait to provide a compile-time check for PixelType used in the instantiation of an itk::Image */ template struct isPrimitiveType { static const bool value = false; }; /** \brief Provides a partial specialization for the \sa isPrimitiveType object */ #define DEFINE_TYPE_PRIMITIVE(_TYPEIN) \ template <> \ struct isPrimitiveType<_TYPEIN> \ { \ static const bool value = true; \ } /** \brief Partial specialization (unsigned char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned char); /** \brief Partial specialization (char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(char); /** \brief Partial specialization (signed char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(signed char); /** \brief Partial specialization (unsigned short) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned short); /** \brief Partial specialization (short) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(short); /** \brief Partial specialization (unsigned int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned int); /** \brief Partial specialization (int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(int); /** \brief Partial specialization (long int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(long int); /** \brief Partial specialization (long unsigned int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(long unsigned int); /** \brief Partial specialization (float) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(float); /** \brief Partial specialization (double) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(double); template struct ImageTypeTrait { typedef itk::Image ImageType; static const bool IsVectorImage = false; }; template struct ImageTypeTrait, VDimension> { typedef itk::VectorImage ImageType; static const bool IsVectorImage = true; }; template struct ImageTypeTrait { typedef T ImageType; static const bool IsVectorImage = false; }; template struct ImageTypeTrait, 0> { typedef itk::VectorImage ImageType; static const bool IsVectorImage = true; }; /** \brief Compile-time trait for resolving the ValueType from an ItkImageType */ template struct PixelTypeTrait { typedef T ValueType; }; /** \brief Partial specialization for the PixelTypeTrait * * Specialization for the false value. Used to define the value type for non-primitive pixel types */ template struct PixelTypeTrait { typedef typename T::ValueType ValueType; }; /** \brief Compile time resolving of the type of a component */ template struct GetComponentType { typedef typename PixelTypeTrait::value, T>::ValueType ComponentType; }; /** \brief Object for compile-time resolving of the number of components for given type. * * Default value for the component number is 1 */ template struct ComponentsTrait { static const size_t Size = 1; }; /** \brief Partial specialization for the ComponentsTraits in case of compound types */ template struct ComponentsTrait { static const size_t Size = T::ValueType::Length; }; typedef itk::IOPixelEnum itkIOPixelType; typedef itk::IOComponentEnum itkIOComponentType; /** \brief Object for compile-time translation of a composite pixel type into an itk::ImageIOBase::IOPixelType * information * * The default value of the IOCompositeType is the UNKNOWNPIXELTYPE, the default value will be used for all but the * types below with own partial specialization. The values of the IOCompositeType member in the specializations * correspond * to the values of the itk::ImageIOBase::IOPixelType enum values. */ template struct MapCompositePixelType { static const itkIOPixelType IOCompositeType = itkIOPixelType::UNKNOWNPIXELTYPE; }; //------------------------ // Partial template specialization for fixed-length types //------------------------ template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::RGB; }; template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::RGBA; }; template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::DIFFUSIONTENSOR3D; }; template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR; }; //------------------------ // Partial template specialization for variable-length types //------------------------ template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR; }; template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR; }; template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR; }; template struct MapCompositePixelType> { static const itkIOPixelType IOCompositeType = itkIOPixelType::MATRIX; }; /** \brief Object for compile-time translation of a pixel type into an itk::ImageIOBase::IOPixelType information * * The first template parameter is the pixel type to be translated, the second parameter determines the processing * way. For non-primitive types the first template parameter is passed to the MapCompositePixelType object to be * resolved there * for primitive types the value is set to SCALAR. * - * To initalize the flag correctly in compile-time use the \sa isPrimitiveType trait. + * To initialize the flag correctly in compile-time use the \sa isPrimitiveType trait. */ template struct MapPixelType { static const itkIOPixelType IOPixelType = MapCompositePixelType::IOCompositeType; static const itkIOComponentType IOComponentType = MapPixelComponentType::ComponentType>::value; }; /** \brief Partial specialization for setting the IOPixelType for primitive types to SCALAR */ template struct MapPixelType { static const itkIOPixelType IOPixelType = itkIOPixelType::SCALAR; static const itkIOComponentType IOComponentType = MapPixelComponentType::value; }; } // end namespace mitk #endif // PIXELTYPETRAITS_H diff --git a/Modules/Core/include/mitkPlaneGeometryDataVtkMapper3D.h b/Modules/Core/include/mitkPlaneGeometryDataVtkMapper3D.h index fa7ee89fcd..1e4d578a9e 100644 --- a/Modules/Core/include/mitkPlaneGeometryDataVtkMapper3D.h +++ b/Modules/Core/include/mitkPlaneGeometryDataVtkMapper3D.h @@ -1,215 +1,215 @@ /*============================================================================ 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 MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #define MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #include "mitkDataStorage.h" #include "mitkPlaneGeometryDataToSurfaceFilter.h" #include "mitkVtkMapper.h" #include "mitkWeakPointer.h" #include #include #include class vtkActor; class vtkPolyDataMapper; class vtkAssembly; class vtkFeatureEdges; class vtkTubeFilter; class vtkTransformPolyDataFilter; class vtkHedgeHog; namespace mitk { class PlaneGeometryData; class BaseRenderer; class ImageVtkMapper2D; class DataStorage; class PlaneGeometryDataVtkMapper3D; /** \deprecatedSince{2014_10} This class is deprecated. Please use PlaneGeometryDataVTKMapper3D instead. */ DEPRECATED(typedef PlaneGeometryDataVtkMapper3D Geometry2DDataVtkMapper3D); /** * \brief Vtk-based mapper to display a PlaneGeometry in a 3D window * \ingroup Mapper * * Uses a PlaneGeometryDataToSurfaceFilter object to create a vtkPolyData representation of a given PlaneGeometry * instance. * PlaneGeometry may either contain a common flat plane or a curved plane (ThinPlateSplineCurvedGeometry). * * The vtkPolyData object is then decorated by a colored tube on the edges and by image textures if possible * (currently this requires that there is a 2D render window rendering the same geometry as this mapper). * * Properties that influence rendering are: * * - \b "draw edges": (Bool) Toggle display of the tubed frame * - \b "color": (ColorProperty) Color of the tubed frame. * - \b "xresolution": (FloatProperty) Resolution (=number of tiles) in x direction. Only relevant for * ThinPlateSplineCurvedGeometry * - \b "yresolution": (FloatProperty) Resolution (=number of tiles) in y direction. Only relevant for * ThinPlateSplineCurvedGeometry * - \b "draw normals 3D": (BoolProperty) If true, a vtkHedgeHog is used to display normals for the generated surface * object. Useful to distinguish front and back of a plane. Hedgehogs are colored according to "front color" and "back * color" * - \b "color two sides": (BoolProperty) If true, front and back side of the plane are colored differently ("front * color" and "back color") * - \b "invert normals": (BoolProperty) Inverts front/back for display. * - \b "front color": (ColorProperty) Color for front side of the plane * - \b "back color": (ColorProperty) Color for back side of the plane * - \b "material.representation": (BoolProperty) Choose the representation to draw the mesh in (Surface, Wireframe, * Point Cloud) * - \b "surfacegeometry": TODO: Add documentation * - \b "LookupTable": (LookupTableProperty) Set the lookuptable to render with. * * Note: The following properties are set for each image individually, and thus, also influence the rendering of this * mapper: * * - \b "texture interpolation": (BoolProperty) Turn on/off the texture interpolation of each image * - \b "use color": (BoolProperty) Decide whether we want to use the color property or a lookuptable. * - \b "binary": (BoolProperty) Binary image handling: Color the value=1.0 with the color property and make the * background (value=0.0) of the image translucent. * - \b "layer": (IntProperty) Controls what image is considered "on top" of another. In the case that two should * inhabit the same space, higher layer occludes lower layer. * - \b "opacity": (FloatProperty) Set the opacity for each rendered image. * - \b "color": (FloatProperty) Set the color for each rendered image. */ class MITKCORE_EXPORT PlaneGeometryDataVtkMapper3D : public VtkMapper { public: mitkClassMacro(PlaneGeometryDataVtkMapper3D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Overloaded since the displayed color-frame of the image mustn't be * transformed after generation of poly data, but before (vertex coordinates * only) */ vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; void UpdateVtkTransform(mitk::BaseRenderer *renderer) override; /** * \brief Get the PlaneGeometryData to map */ virtual const PlaneGeometryData *GetInput(); /** * \brief All images found when traversing the (sub-) tree starting at * \a iterator which are resliced by an ImageVtkMapper2D will be mapped. * This method is used to set the data storage to traverse. This offers * the possibility to use this mapper for other data storages (not only * the default data storage). */ virtual void SetDataStorageForTexture(mitk::DataStorage *storage); protected: typedef std::multimap LayerSortedActorList; PlaneGeometryDataVtkMapper3D(); ~PlaneGeometryDataVtkMapper3D() override; void GenerateDataForRenderer(BaseRenderer *renderer) override; void ProcessNode(DataNode *node, BaseRenderer *renderer, Surface *surface, LayerSortedActorList &layerSortedActors); void ImageMapperDeletedCallback(itk::Object *caller, const itk::EventObject &event); /** \brief general PropAssembly to hold the entire scene */ vtkAssembly *m_Prop3DAssembly; /** \brief PropAssembly to hold the planes */ vtkAssembly *m_ImageAssembly; PlaneGeometryDataToSurfaceFilter::Pointer m_SurfaceCreator; BoundingBox::Pointer m_SurfaceCreatorBoundingBox; BoundingBox::PointsContainer::Pointer m_SurfaceCreatorPointsContainer; /** \brief Edge extractor for tube-shaped frame */ vtkFeatureEdges *m_Edges; /** \brief Filter to apply object transform to the extracted edges */ vtkTransformPolyDataFilter *m_EdgeTransformer; /** \brief Source to create the tube-shaped frame */ vtkTubeFilter *m_EdgeTuber; /** \brief Mapper for the tube-shaped frame */ vtkPolyDataMapper *m_EdgeMapper; /** \brief Actor for the tube-shaped frame */ vtkActor *m_EdgeActor; /** \brief Mapper for black plane background */ vtkPolyDataMapper *m_BackgroundMapper; /** \brief Actor for black plane background */ vtkActor *m_BackgroundActor; - /** \brief Transforms the suface before applying the glyph filter */ + /** \brief Transforms the surface before applying the glyph filter */ vtkTransformPolyDataFilter *m_NormalsTransformer; /** \brief Mapper for normals representation (thin lines) */ vtkPolyDataMapper *m_FrontNormalsMapper; vtkPolyDataMapper *m_BackNormalsMapper; /** \brief Generates lines for surface normals */ vtkHedgeHog *m_FrontHedgeHog; vtkHedgeHog *m_BackHedgeHog; /** \brief Actor to hold the normals arrows */ vtkActor *m_FrontNormalsActor; vtkActor *m_BackNormalsActor; /** Cleans the polyline in order to avoid phantom boundaries */ vtkCleanPolyData *m_Cleaner; /** Internal flag, if actors for normals are already added to m_Prop3DAssembly*/ bool m_NormalsActorAdded; - /** \brief The DataStorage defines which part of the data tree is traversed for renderering. */ + /** \brief The DataStorage defines which part of the data tree is traversed for rendering. */ mitk::WeakPointer m_DataStorage; class MITKCORE_EXPORT ActorInfo { public: vtkActor *m_Actor; // we do not need a smart-pointer, because we delete our // connection, when the referenced mapper is destroyed itk::Object *m_Sender; unsigned long m_ObserverID; void Initialize(vtkActor *actor, itk::Object *sender, itk::Command *command); ActorInfo(); ~ActorInfo(); }; /** \brief List holding the vtkActor to map the image into 3D for each * ImageMapper */ typedef std::map ActorList; ActorList m_ImageActors; - // responsiblity to remove the observer upon its destruction + // responsibility to remove the observer upon its destruction typedef itk::MemberCommand MemberCommandType; MemberCommandType::Pointer m_ImageMapperDeletedCommand; }; } // namespace mitk #endif /* MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F */ diff --git a/Modules/Core/include/mitkPointSet.h b/Modules/Core/include/mitkPointSet.h index d58c5bfe77..77b0b744de 100755 --- a/Modules/Core/include/mitkPointSet.h +++ b/Modules/Core/include/mitkPointSet.h @@ -1,348 +1,348 @@ /*============================================================================ 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 MITKPointSet_H_HEADER_INCLUDED #define MITKPointSet_H_HEADER_INCLUDED #include "mitkBaseData.h" #include #include namespace mitk { /** * \brief Data structure which stores a set of points. * * 3D points are grouped within a point set; for time resolved usage, one point * set is created and maintained per time step. A point entry consists of the * point coordinates and point data. * * The point data includes a point ID (unique identifier to address this point * within the point set), the selection state of the point and the type of * the point. * * For further information about different point types see * mitk::PointSpecificationType in mitkVector.h. * * Inserting a point is accompanied by an event, containing an index. The new * point is inserted into the list at the specified position. At the same time * an internal ID is generated and stored for the point. Points at specific time * steps are accessed by specifying the time step number (which defaults to 0). * * The points of itk::PointSet stores the points in a pointContainer * (MapContainer). The points are best accessed by using a ConstIterator (as * defined in MapContainer); avoid access via index. * * The class internally uses an itk::Mesh for each time step. * * \section mitkPointSetDisplayOptions * * The default mappers for this data structure are mitk::PointSetGLMapper2D and * mitk::PointSetVtkMapper3D. See these classes for display options which can * can be set via properties. * * \section Events * * PointSet issues the following events, for which observers can register * (the below events are grouped into a class hierarchy as indicated by - * identation level; e.g. PointSetSizeChangeEvent comprises PointSetAddEvent + * indentation level; e.g. PointSetSizeChangeEvent comprises PointSetAddEvent * and PointSetRemoveEvent): * * * PointSetEvent subsumes all PointSet events * PointSetMoveEvent issued when a point of the PointSet is moved * PointSetSizeChangeEvent subsumes add and remove events * PointSetAddEvent issued when a point is added to the PointSet * PointSetRemoveEvent issued when a point is removed from the PointSet * * \ingroup PSIO * \ingroup Data */ class MITKCORE_EXPORT PointSet : public BaseData { public: mitkClassMacro(PointSet, BaseData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef mitk::ScalarType CoordinateType; typedef mitk::ScalarType InterpolationWeightType; static const unsigned int PointDimension = 3; static const unsigned int MaxTopologicalDimension = 3; /** * \brief struct for data of a point */ struct MITKCORE_EXPORT PointDataType { unsigned int id; // to give the point a special ID bool selected; // information about if the point is selected mitk::PointSpecificationType pointSpec; // specifies the type of the point bool operator==(const PointDataType &other) const; }; /** * \brief cellDataType, that stores all indexes of the lines, that are * selected e.g.: points A,B and C.Between A and B there is a line with * index 0. If vector of cellData contains 1 and 2, then the lines between * B and C and C and A is selected. */ typedef std::vector SelectedLinesType; typedef SelectedLinesType::iterator SelectedLinesIter; struct CellDataType { // used to set the whole cell on selected bool selected; // indexes of selected lines. 0 is between pointId 0 and 1 SelectedLinesType selectedLines; // is the polygon already finished and closed bool closed; }; typedef itk::DefaultDynamicMeshTraits MeshTraits; typedef itk::Mesh MeshType; typedef MeshType DataType; typedef Point3D PointType; typedef DataType::PointIdentifier PointIdentifier; typedef DataType::PointsContainer PointsContainer; typedef DataType::PointsContainerIterator PointsIterator; typedef DataType::PointsContainer::ConstIterator PointsConstIterator; typedef DataType::PointDataContainer PointDataContainer; typedef DataType::PointDataContainerIterator PointDataIterator; typedef DataType::PointDataContainerIterator PointDataConstIterator; void Expand(unsigned int timeSteps) override; /** \brief executes the given Operation */ void ExecuteOperation(Operation *operation) override; /** \brief returns the current size of the point-list */ virtual int GetSize(unsigned int t = 0) const; virtual unsigned int GetPointSetSeriesSize() const; /** \brief returns the pointset */ virtual DataType::Pointer GetPointSet(int t = 0) const; PointsIterator Begin(int t = 0); PointsConstIterator Begin(int t = 0) const; PointsIterator End(int t = 0); PointsConstIterator End(int t = 0) const; /** * \brief Get an iterator to the max ID element if existent. Return End() otherwise. */ PointsIterator GetMaxId(int t = 0); /** * \brief Get the point with ID id in world coordinates * * check if the ID exists. If it doesn't exist, then return 0,0,0 */ PointType GetPoint(PointIdentifier id, int t = 0) const; /** * \brief Get the point with ID id in world coordinates * * If a point exists for the ID id, the point is returned in the parameter point * and the method returns true. If the ID does not exist, the method returns false */ bool GetPointIfExists(PointIdentifier id, PointType *point, int t = 0) const; /** * \brief Set the given point in world coordinate system into the itkPointSet. */ void SetPoint(PointIdentifier id, PointType point, int t = 0); /** * \brief Set the given point in world coordinate system with the given PointSpecificationType */ void SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t = 0); /** * \brief Set the given point in world coordinate system into the itkPointSet. */ void InsertPoint(PointIdentifier id, PointType point, int t = 0); /** * \brief Set the given point in world coordinate system with given PointSpecificationType */ void InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t); /** * \brief Insert the given point in world coordinate system with incremented max id at time step t. */ PointIdentifier InsertPoint(PointType point, int t = 0); /** * \brief Remove point with given id at timestep t, if existent */ bool RemovePointIfExists(PointIdentifier id, int t = 0); /** * \brief Remove max id point at timestep t and return iterator to precedent point */ PointsIterator RemovePointAtEnd(int t = 0); /** * \brief Swap a point at the given position (id) with the upper point (moveUpwards=true) or with the lower point * (moveUpwards=false). * If upper or lower index does not exist false is returned, if swap was successful true. */ bool SwapPointPosition(PointIdentifier id, bool moveUpwards, int t = 0); /** * \brief searches a selected point and returns the id of that point. * If no point is found, then -1 is returned */ virtual int SearchSelectedPoint(int t = 0) const; /** \brief returns true if a point exists at this position */ virtual bool IndexExists(int position, int t = 0) const; /** \brief to get the state selected/unselected of the point on the * position */ virtual bool GetSelectInfo(int position, int t = 0) const; virtual void SetSelectInfo(int position, bool selected, int t = 0); /** \brief to get the type of the point at the position and the moment */ virtual PointSpecificationType GetSpecificationTypeInfo(int position, int t) const; /** \brief returns the number of selected points */ virtual int GetNumberOfSelected(int t = 0) const; /** * \brief searches a point in the list == point +/- distance * * \param point is in world coordinates. * \param distance is in mm. * \param t * returns -1 if no point is found * or the position in the list of the first match */ int SearchPoint(Point3D point, ScalarType distance, int t = 0) const; bool IsEmptyTimeStep(unsigned int t) const override; // virtual methods, that need to be implemented void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; // Method for subclasses virtual void OnPointSetChange(){}; protected: mitkCloneMacro(Self); PointSet(); PointSet(const PointSet &other); ~PointSet() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; ///< print content of the object to os void ClearData() override; void InitializeEmpty() override; /** \brief swaps point coordinates and point data of the points with identifiers id1 and id2 */ bool SwapPointContents(PointIdentifier id1, PointIdentifier id2, int t = 0); typedef std::vector PointSetSeries; PointSetSeries m_PointSetSeries; DataType::PointsContainer::Pointer m_EmptyPointsContainer; /** * @brief flag to indicate the right time to call SetBounds **/ bool m_CalculateBoundingBox; }; /** - * @brief Equal A function comparing two pointsets for beeing identical. + * @brief Equal A function comparing two pointsets for being identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const * mitk::PointSet& p1, const mitk::PointSet& p2) instead. * * @ingroup MITKTestingAPI * * The function compares the Geometry, the size and all points element-wise. - * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * The parameter eps is a tolarence value for all methods which are internally used for comparison. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @param checkGeometry if comparing point sets loaded from a file, the geometries might be different and must not be * compared. In all other cases, you should compare the geometries. * @return True, if all subsequent comparisons are true, false otherwise */ DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::PointSet *leftHandSide, const mitk::PointSet *rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry = true)); /** - * @brief Equal A function comparing two pointsets for beeing identical. + * @brief Equal A function comparing two pointsets for being identical. * * @ingroup MITKTestingAPI * * The function compares the Geometry, the size and all points element-wise. - * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * The parameter eps is a tolarence value for all methods which are internally used for comparison. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @param checkGeometry if comparing point sets loaded from a file, the geometries might be different and must not be * compared. In all other cases, you should compare the geometries. * @return True, if all subsequent comparisons are true, false otherwise */ MITKCORE_EXPORT bool Equal(const mitk::PointSet &leftHandSide, const mitk::PointSet &rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry = true); itkEventMacroDeclaration(PointSetEvent, itk::AnyEvent); itkEventMacroDeclaration(PointSetMoveEvent, PointSetEvent); itkEventMacroDeclaration(PointSetSizeChangeEvent, PointSetEvent); itkEventMacroDeclaration(PointSetAddEvent, PointSetSizeChangeEvent); itkEventMacroDeclaration(PointSetRemoveEvent, PointSetSizeChangeEvent); itkEventMacroDeclaration(PointSetExtendTimeRangeEvent, PointSetEvent); } // namespace mitk #endif /* MITKPointSet_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkPointSetDataInteractor.h b/Modules/Core/include/mitkPointSetDataInteractor.h index 98e156d2b2..9726e47a32 100644 --- a/Modules/Core/include/mitkPointSetDataInteractor.h +++ b/Modules/Core/include/mitkPointSetDataInteractor.h @@ -1,186 +1,186 @@ /*============================================================================ 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 mitkPointSetDataInteractor_h_ #define mitkPointSetDataInteractor_h_ #include "itkObject.h" #include "itkObjectFactory.h" #include "itkSmartPointer.h" #include "mitkCommon.h" #include "mitkDataInteractor.h" #include #include namespace mitk { /** * Class PointSetDataInteractor * \brief Implementation of the PointSetInteractor * * Interactor operates on a point set and supports to: * - add points * - remove points * - move single points * - move complete pointset * - select/unselect a point * * in 2d and 3d render windows. * * \warning If this Interactor is assigned (SetDataNode) an empty mitk::DataNode it creates a point set, * changing the point set of the assigned mitk::DataNode after this assignment will cause the mitk::PointSetDataInteractor * to not work properly. So the usage has follow this general scheme: * * \code // Set up interactor m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("PointSet.xml"); m_CurrentInteractor->SetEventConfig("PointSetConfig.xml"); //Create new PointSet which will receive the interaction input m_TestPointSet = mitk::PointSet::New(); // Add the point set to the mitk::DataNode *before* the DataNode is added to the mitk::PointSetDataInteractor m_TestPointSetNode->SetData(m_TestPointSet); // finally add the mitk::DataNode (which already is added to the mitk::DataStorage) to the mitk::PointSetDataInteractor m_CurrentInteractor->SetDataNode(m_TestPointSetNode); \endcode * * */ // Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. class MITKCORE_EXPORT PointSetDataInteractor : public DataInteractor { public: mitkClassMacro(PointSetDataInteractor, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Sets the maximum distance that is accepted when looking for a point at a certain position using the * GetPointIndexByPosition function. */ void SetAccuracy(float accuracy); /** * @brief SetMaxPoints Sets the maximal number of points for the pointset - * Default ist zero, which result in infinite number of allowed points + * Default is zero, which result in infinite number of allowed points * @param maxNumber */ void SetMaxPoints(unsigned int maxNumber = 0); protected: PointSetDataInteractor(); ~PointSetDataInteractor() override; /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; /** * This function is called when a DataNode has been set/changed. * It is used to initialize the DataNode, e.g. if no PointSet exists yet it is created * and added to the DataNode. */ void DataNodeChanged() override; /** * \brief Return index in PointSet of the point that is within given accuracy to the provided position. * * Assumes that the DataNode contains a PointSet, if so it iterates over all points * in the DataNode to check if it contains a point near the pointer position. * If a point is found its index-position is returned, else -1 is returned. */ virtual int GetPointIndexByPosition(Point3D position, unsigned int time = 0, float accuracy = -1); virtual bool CheckSelection(const InteractionEvent *interactionEvent); /** Adds a point at the given coordinates. * Every time a point is added it is also checked if the maximal number of points is reached, * and if so an InternalEvent with the signal name "MaxNumberOfPoints" is triggered. */ virtual void AddPoint(StateMachineAction *, InteractionEvent *event); /** Removes point that is selected */ virtual void RemovePoint(StateMachineAction *, InteractionEvent *interactionEvent); /** * Checks if new point is close enough to an old one, * if so, trigger the ClosedContour signal which can be caught by the state machine. */ virtual void IsClosedContour(StateMachineAction *, InteractionEvent *); /** - * Moves the currently selected point to the new coodinates. + * Moves the currently selected point to the new coordinates. */ virtual void MovePoint(StateMachineAction *, InteractionEvent *); /** * Initializes the movement, stores starting position. */ virtual void InitMove(StateMachineAction *, InteractionEvent *interactionEvent); /** * Is called when a movement is finished, changes back to regular color. */ virtual void FinishMove(StateMachineAction *, InteractionEvent *); /** * Selects a point from the PointSet as currently active. */ virtual void SelectPoint(StateMachineAction *, InteractionEvent *); /** * Unselects a point at the given coordinate. */ virtual void UnSelectPointAtPosition(StateMachineAction *, InteractionEvent *); /** * Unselects all points out of reach. */ virtual void UnSelectAll(StateMachineAction *, InteractionEvent *); /** * @brief UpdatePointSet Updates the member variable that holds the point set, evaluating the time step of the * sender. */ virtual void UpdatePointSet(StateMachineAction *stateMachineAction, InteractionEvent *); /** * Calls for inactivation of the DataInteractor */ virtual void Abort(StateMachineAction *, InteractionEvent *); /** \brief to calculate a direction vector from last point and actual * point */ Point3D m_LastPoint; /** \brief summ-vector for Movement */ Vector3D m_SumVec; // DATA PointSet::Pointer m_PointSet; int m_MaxNumberOfPoints; // maximum of allowed number of points float m_SelectionAccuracy; // accuracy that's needed to select a point // FUNCTIONS void UnselectAll(unsigned int timeStep, ScalarType timeInMs); void SelectPoint(int position, unsigned int timeStep, ScalarType timeInMS); }; } #endif diff --git a/Modules/Core/include/mitkPointSetSource.h b/Modules/Core/include/mitkPointSetSource.h index a393f8e6db..7068e54079 100644 --- a/Modules/Core/include/mitkPointSetSource.h +++ b/Modules/Core/include/mitkPointSetSource.h @@ -1,67 +1,67 @@ /*============================================================================ 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 _MITK_POINT_SET_SOURCE_H #define _MITK_POINT_SET_SOURCE_H #include "mitkBaseDataSource.h" #include "mitkPointSet.h" namespace mitk { /** * @brief Superclass of all classes generating point sets (instances of class * mitk::PointSet) as output. * * In itk and vtk the generated result of a ProcessObject is only guaranteed * to be up-to-date, when Update() of the ProcessObject or the generated * DataObject is called immediately before access of the data stored in the * DataObject. * @ingroup Process */ class MITKCORE_EXPORT PointSetSource : public BaseDataSource { public: mitkClassMacro(PointSetSource, BaseDataSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef PointSet OutputType; typedef OutputType::Pointer OutputTypePointer; mitkBaseDataSourceGetOutputDeclarations /** * Allocates a new output object and returns it. Currently the * index idx is not evaluated. * @param idx the index of the output for which an object should be created * @returns the new object */ itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override; /** * This is a default implementation to make sure we have something. - * Once all the subclasses of ProcessObject provide an appopriate + * Once all the subclasses of ProcessObject provide an appropriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; protected: PointSetSource(); ~PointSetSource() override; }; } #endif // #define _MITK_POINT_SET_SOURCE_H diff --git a/Modules/Core/include/mitkPointSetVtkMapper2D.h b/Modules/Core/include/mitkPointSetVtkMapper2D.h index def499c1c9..267cf0b204 100644 --- a/Modules/Core/include/mitkPointSetVtkMapper2D.h +++ b/Modules/Core/include/mitkPointSetVtkMapper2D.h @@ -1,239 +1,239 @@ /*============================================================================ 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 mitkPointSetVtkMapper2D_h #define mitkPointSetVtkMapper2D_h #include "mitkBaseRenderer.h" #include "mitkLocalStorageHandler.h" #include "mitkVtkMapper.h" #include #include // VTK #include class vtkActor; class vtkPropAssembly; class vtkPolyData; class vtkPolyDataMapper; class vtkGlyphSource2D; class vtkGlyph3D; class vtkFloatArray; class vtkCellArray; namespace mitk { class PointSet; /** * @brief Vtk-based 2D mapper for PointSet * * Due to the need of different colors for selected * and unselected points and the facts, that we also have a contour and * labels for the points, the vtk structure is build up the following way: * * We have three PolyData, one selected, and one unselected and one * for a contour between the points. Each one is connected to an own * PolyDataMapper and an Actor. The different color for the unselected and * selected state and for the contour is read from properties. * * This mapper has several additional functionalities, such as rendering * a contour between points, calculating and displaying distances or angles * between points. * * @section mitkPointSetVtkMapper2D_point_rep Point Representation * * The points are displayed as small glyphs of configurable shape * (see property "PointSet.2D.shape"). The size of these glyphs * is given in world units. That means, the size or shape of those * glyphs is independent of the BaseGeometry object that you assign * to the PointSet. As for all other objects, _positions_ of points * will be transformed into the world via the Geometry's index-to-world * transform. * * Then the three Actors are combined inside a vtkPropAssembly and this * object is returned in GetProp() and so hooked up into the rendering * pipeline. * * @section mitkPointSetVtkMapper2D_propertires Applicable Properties * * Properties that can be set for point sets and influence the PointSetVTKMapper2D are: * * - \b "line width": (IntProperty 2) // line width of the line from one point to another * - \b "point line width": (IntProperty 1) // line width of the cross marking a point * - \b "point 2D size": (FloatProperty 6) // size of the glyph marking a point (diameter, in world * units!) * - \b "show contour": (BoolProperty false) // enable contour rendering between points (lines) * - \b "close contour": (BoolProperty false) // if enabled, the open strip is closed (first point * connected with last point) * - \b "show points": (BoolProperty true) // show or hide points * - \b "show distances": (BoolProperty false) // show or hide distance measure * - \b "distance decimal digits": (IntProperty 2) // set the number of decimal digits to be shown when * rendering the distance information * - \b "show angles": (BoolProperty false) // show or hide angle measurement * - \b "show distant lines": (BoolProperty false) // show the line between to points from a distant view * (equals "always on top" option) * - \b "layer": (IntProperty 1) // default is drawing pointset above images (they have a * default layer of 0) * - \b "PointSet.2D.shape" (EnumerationProperty Cross) // provides different shapes marking a point * 0 = "None", 1 = "Vertex", 2 = "Dash", 3 = "Cross", 4 = "ThickCross", 5 = "Triangle", 6 = "Square", 7 = * "Circle", * 8 = "Diamond", 9 = "Arrow", 10 = "ThickArrow", 11 = "HookedArrow", 12 = "Cross" * - \b "PointSet.2D.fill shape": (BoolProperty false) // fill or do not fill the glyph shape * - \b "Pointset.2D.distance to plane": (FloatProperty 4.0) //In the 2D render window, points are rendered which lie * within a certain distance * to the current plane. They are projected on the current * plane and scaled according to their distance. * Point markers appear smaller as the plane moves away * from * their true location. * The distance threshold can be adjusted by this float * property, which ables the user to delineate the points * that lie exactly on the plane. (+/- rounding error) * * Other Properties used here but not defined in this class: * * - \b "selectedcolor": (ColorProperty (1.0f, 0.0f, 0.0f)) // default color of the selected pointset e.g. the * current * point is red * - \b "contourcolor" : (ColorProperty (1.0f, 0.0f, 0.0f)) // default color for the contour is red * - \b "color": (ColorProperty (1.0f, 1.0f, 0.0f)) // default color of the (unselected) pointset is yellow * - \b "opacity": (FloatProperty 1.0) // opacity of point set, contours * - \b "label": (StringProperty nullptr) // a label can be defined for each point, which is rendered in proximity * to * the point * * @ingroup Mapper */ class MITKCORE_EXPORT PointSetVtkMapper2D : public VtkMapper { public: mitkClassMacro(PointSetVtkMapper2D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); virtual const mitk::PointSet *GetInput() const; /** \brief returns the a prop assembly */ vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; /** \brief set the default properties for this mapper */ static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ class LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /* constructor */ LocalStorage(); /* destructor */ ~LocalStorage() override; // points vtkSmartPointer m_UnselectedPoints; vtkSmartPointer m_SelectedPoints; vtkSmartPointer m_ContourPoints; // scales vtkSmartPointer m_UnselectedScales; vtkSmartPointer m_SelectedScales; // distances vtkSmartPointer m_DistancesBetweenPoints; // lines vtkSmartPointer m_ContourLines; // glyph source (provides different shapes for the points) vtkSmartPointer m_UnselectedGlyphSource2D; vtkSmartPointer m_SelectedGlyphSource2D; // glyph vtkSmartPointer m_UnselectedGlyph3D; vtkSmartPointer m_SelectedGlyph3D; // polydata vtkSmartPointer m_VtkUnselectedPointListPolyData; vtkSmartPointer m_VtkSelectedPointListPolyData; vtkSmartPointer m_VtkContourPolyData; // actor vtkSmartPointer m_UnselectedActor; vtkSmartPointer m_SelectedActor; vtkSmartPointer m_ContourActor; vtkSmartPointer m_VtkTextActor; std::vector> m_VtkTextLabelActors; std::vector> m_VtkTextDistanceActors; std::vector> m_VtkTextAngleActors; // mappers vtkSmartPointer m_VtkUnselectedPolyDataMapper; vtkSmartPointer m_VtkSelectedPolyDataMapper; vtkSmartPointer m_VtkContourPolyDataMapper; // propassembly vtkSmartPointer m_PropAssembly; }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; protected: /* constructor */ PointSetVtkMapper2D(); /* destructor */ ~PointSetVtkMapper2D() override; /* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */ void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; /* \brief Called in mitk::Mapper::Update * If TimeGeometry or time step is not valid of point set: reset mapper so that nothing is - * displayed e.g. toggle visiblity of the propassembly */ + * displayed e.g. toggle visibility of the propassembly */ void ResetMapper(BaseRenderer *renderer) override; /* \brief Fills the vtk objects, thus it is only called when the point set has been changed. * This function iterates over the input point set and determines the glyphs which lie in a specific * range around the current slice. Those glyphs are rendered using a specific shape defined in vtk glyph source * to mark each point. The shape can be changed in MITK using the property "PointSet.2D.shape". * * There were issues when rendering vtk glyphs in the 2D-render windows. By default, the glyphs are * rendered within the x-y plane in each 2D-render window, so you would only see them from the * side in the sagittal and coronal 2D-render window. The solution to this is to rotate the glyphs in order * to be ortogonal to the current view vector. To achieve this, the rotation (vtktransform) of the current - * PlaneGeometry is applied to the orienation of the glyphs. */ + * PlaneGeometry is applied to the orientation of the glyphs. */ virtual void CreateVTKRenderObjects(mitk::BaseRenderer *renderer); // member variables holding the current value of the properties used in this mapper bool m_ShowContour; // "show contour" property bool m_CloseContour; // "close contour" property bool m_ShowPoints; // "show points" property bool m_ShowDistances; // "show distances" property int m_DistancesDecimalDigits; // "distance decimal digits" property bool m_ShowAngles; // "show angles" property bool m_ShowDistantLines; // "show distant lines" property int m_LineWidth; // "line width" property int m_PointLineWidth; // "point line width" property float m_Point2DSize; // "point 2D size" property int m_IDShapeProperty; // ID for mitkPointSetShape Enumeration Property "Pointset.2D.shape" bool m_FillShape; // "Pointset.2D.fill shape" property float m_DistanceToPlane; // "Pointset.2D.distance to plane" property bool m_FixedSizeOnScreen; // "Pointset.2D.fixed size on screen" property }; } // namespace mitk #endif /* mitkPointSetVtkMapper2D_h */ diff --git a/Modules/Core/include/mitkProgressBarImplementation.h b/Modules/Core/include/mitkProgressBarImplementation.h index 8d7cb4f570..44ad914e72 100644 --- a/Modules/Core/include/mitkProgressBarImplementation.h +++ b/Modules/Core/include/mitkProgressBarImplementation.h @@ -1,52 +1,52 @@ /*============================================================================ 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 MITKPROGRESSBARIMPLEMENTATION_H #define MITKPROGRESSBARIMPLEMENTATION_H #include namespace mitk { //##Documentation - //## @brief GUI indepentent Interface for all Gui depentent implementations of a ProgressBar. + //## @brief GUI independent Interface for all Gui depentent implementations of a ProgressBar. class MITKCORE_EXPORT ProgressBarImplementation { public: //##Documentation //## @brief Constructor ProgressBarImplementation(){}; //##Documentation //## @brief Destructor virtual ~ProgressBarImplementation(){}; //##Documentation //## @brief Sets whether the current progress value is displayed. virtual void SetPercentageVisible(bool visible) = 0; //##Documentation //## @brief Explicitly reset progress bar. virtual void Reset() = 0; //##Documentation //## @brief Adds steps to totalSteps. virtual void AddStepsToDo(unsigned int steps) = 0; //##Documentation //## @brief Sets the current amount of progress to current progress + steps. //## @param steps the number of steps done since last Progress(int steps) call. virtual void Progress(unsigned int steps) = 0; }; } // end namespace mitk #endif /* define MITKPROGRESSBARIMPLEMENTATION_H */ diff --git a/Modules/Core/include/mitkPropertyKeyPath.h b/Modules/Core/include/mitkPropertyKeyPath.h index 134bb8e9ff..bf0c37101e 100644 --- a/Modules/Core/include/mitkPropertyKeyPath.h +++ b/Modules/Core/include/mitkPropertyKeyPath.h @@ -1,216 +1,216 @@ /*============================================================================ 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 mitkPropertyKeyPath_h #define mitkPropertyKeyPath_h #include #include #include #include namespace mitk { /** @brief Class that can be used to specify nested or wild carded property keys. E.g. * for the use in context of the property persistence service or the property relation service.\n * Following assumptions are made /preconditions are defined: * - A property key is partitioned by "." into nodes (c.f. visualization of property keys in the PropertyView). * - A node can either be an element or a selection. * - An element has a name (alphanumric, - and space; "A-Za-z0-9- ") or is wildcarded ("*") * - A selection is either an index (e.g. "[1]") or a wildcard ("[*]"). * - * Selections are used to indicate that the preceding element has multiple occurences and which occurence is meant. + * Selections are used to indicate that the preceding element has multiple occurrences and which occurence is meant. * Example property keys would be: * - prop : A simple property key - * - prop.subprop1 : A property key consting of two nodes + * - prop.subprop1 : A property key consisting of two nodes * - prop.* : Any property key that starts with a node "prop" * - prop.sub.[2] : A property key that starts with a node "prop" and a has a second node that is selection and has * the index 2. * - prop.sub.[*] : Any property key that starts with a node "prop" and a has a second node that is selection (with * any index). * * To build a path one may use the Add* method to build up the PropertyKeyPath element by element.\n * "first.*.third.[3]" would be equivalent to * propKeyPath.AddElement("first"); * propKeyPath.AddAnyElement(); * propKeyPath.AddSelection("third",3);\n * or the inline version * propKeyPath.AddElement("first").AddAnyElement().AddSelection("third",3); */ class MITKCORE_EXPORT PropertyKeyPath final { public: using ItemSelectionIndex = std::size_t; using ElementNameType = std::string; struct MITKCORE_EXPORT NodeInfo { enum class NodeType { Invalid = 0, //*< Node does not exist or is invalid. Element, //*< Selects an specific element given the node name. ElementSelection, //*< Selects an specific item in a sequence of items and has a item selector ("[n]"). AnySelection, //*< Selects all items of a specific element ("[*]"). AnyElement //*< Selects any element/item. Node name is wildcarded ("*"); item selection as well implictily. }; NodeType type; ElementNameType name; ItemSelectionIndex selection; NodeInfo(); NodeInfo(const ElementNameType &name, NodeType type = NodeType::Element, ItemSelectionIndex index = 0); bool Matches(const NodeInfo &right) const; bool operator==(const NodeInfo &right) const; }; using NodeInfoVectorType = std::vector; using PathIndexType = NodeInfoVectorType::size_type; /** Returns if the PropertyKeyPath is empty.*/ bool IsEmpty() const; /** Returns if the path is explicit (has no wildcards).*/ bool IsExplicit() const; /** Returns if the path has any nodes with item selection wild cards ([*]).*/ bool HasItemSelectionWildcardsOnly() const; /** Number of path nodes the PropertyKeyPath contains.*/ PathIndexType GetSize() const; /** Adds a new node to the end of the path. \param [in] newNode Reference to the node that should be added. \return Returns the index of the newly added node.*/ PathIndexType AddNode(const NodeInfo &newNode); /** Function returns the node info of a path node specified by the index * within the PropertyKeyPath. * \pre Passed index must not be out of bounds. * \param [in] index Index of the node whose info should be retrieved. * \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be * thrown.*/ const NodeInfo &GetNode(const PathIndexType &index) const; /** Function returns the node info of a path node specified by the index * within the PropertyKeyPath. * \pre Passed index must not be out of bounds. * \param [in] index Index of the node whose info should be retrieved. * \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be * thrown.*/ NodeInfo &GetNode(const PathIndexType &index); /** Function returns the node info of the first path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ NodeInfo &GetFirstNode(); /** Function returns the node info of the first path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ const NodeInfo &GetFirstNode() const; /** Function returns the node info of the last path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ NodeInfo &GetLastNode(); /** Function returns the node info of the last path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ const NodeInfo &GetLastNode() const; const NodeInfoVectorType &GetNodes() const; /**Compares two PropertyKeyPaths for real equality. So it is a string comparison of their string conversion.*/ bool operator==(const PropertyKeyPath &path) const; /**Operation equals like comparing the ToStr() results with operator <.*/ bool operator<(const PropertyKeyPath &right) const; /**Operation equals like comparing the ToStr() results with operator <=.*/ bool operator<=(const PropertyKeyPath &right) const; /**Operation equals like comparing the ToStr() results with operator >=.*/ bool operator>=(const PropertyKeyPath &right) const; /**Operation equals like comparing the ToStr() results with operator >.*/ bool operator>(const PropertyKeyPath &right) const; /**Checks if two PropertyKeyPaths specify the same node. Hence all wildcards will be processed.\n * E.G.: "item1.child1.grandChild2" == "item1.*.grandChild2" is true. * \remark If you want to check if two paths are "truly" equal and not only equal in terms of * pointing to the same node, use the member function operator ==().*/ bool Equals(const PropertyKeyPath &path) const; PropertyKeyPath &operator=(const PropertyKeyPath &path); /** Appends an "any element" to the path instance.*/ PropertyKeyPath &AddAnyElement(); /** Appends an element with the passed name to the path instance.*/ PropertyKeyPath &AddElement(const ElementNameType &name); /** Appends an element with the passed name and any selection to the path instance.*/ PropertyKeyPath &AddAnySelection(const ElementNameType &name); /** Appends an element with the passed name and selection index to the path instance.*/ PropertyKeyPath &AddSelection(const ElementNameType &name, ItemSelectionIndex index); PropertyKeyPath(); PropertyKeyPath(const PropertyKeyPath &path); - /** overload constructor that supports simple key pathes consisting only of elements.*/ + /** overload constructor that supports simple key paths consisting only of elements.*/ PropertyKeyPath(const std::initializer_list< ElementNameType >& list); ~PropertyKeyPath(); void Reset(); protected: NodeInfoVectorType m_NodeInfos; static bool PropertyKeyPathsMatch(const PropertyKeyPath &left, const PropertyKeyPath &right); }; class MITKCORE_EXPORT InvalidPathNodeException : public mitk::Exception { public: mitkExceptionClassMacro(InvalidPathNodeException, mitk::Exception); }; MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const PropertyKeyPath &path); /**Helper function that converts a path PropertyKeyPath into a regex string that can be used to search for property keys (using std::regex) that are matched by the PropertyKeyPath. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPropertyRegEx(const PropertyKeyPath &tagPath); /**Helper function that converts a path PropertyKeyPath into a regex string that can be used to search for property persistence keys (using std::regex) that are matched by the PropertyKeyPath. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyRegEx(const PropertyKeyPath &tagPath); /**Helper function that converts a path PropertyKeyPath into a regex that can be used as key template in a PropertyPersistanceInfo. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyTemplate(const PropertyKeyPath &tagPath); /**Helper function that converts a path PropertyKeyPath into a regex that can be used as name template in a PropertyPersistanceInfo. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceNameTemplate(const PropertyKeyPath &tagPath); /** Converts the passed property name into a tag path. If the property name cannot be converted into a valid path, the returned path is empty.*/ MITKCORE_EXPORT PropertyKeyPath PropertyNameToPropertyKeyPath(const std::string &propertyName); /** returns the correct property name for a given PropertyKeyPath instance. */ MITKCORE_EXPORT std::string PropertyKeyPathToPropertyName(const PropertyKeyPath &tagPath); } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index c5832a5888..35e29262a5 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -1,254 +1,254 @@ /*============================================================================ 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 PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D #define PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D #include "mitkBaseProperty.h" #include "mitkGenericProperty.h" #include "mitkUIDGenerator.h" #include "mitkIPropertyOwner.h" #include #include #include #include namespace mitk { class XMLWriter; /** * @brief Key-value list holding instances of BaseProperty * * This list is meant to hold an arbitrary list of "properties", * which should describe the object associated with this list. * * Usually you will use PropertyList as part of a DataNode * object - in this context the properties describe the data object * held by the DataNode (e.g. whether the object is rendered at * all, which color is used for rendering, what name should be * displayed for the object, etc.) * * The values in the list are not fixed, you may introduce any kind * of property that seems useful - all you have to do is inherit * from BaseProperty. * * The list is organized as a key-value pairs, i.e. * * \li "name" : pointer to a StringProperty * \li "visible" : pointer to a BoolProperty * \li "color" : pointer to a ColorProperty * \li "volume" : pointer to a FloatProperty * * Please see the documentation of SetProperty and ReplaceProperty for two * quite different semantics. Normally SetProperty is what you want - this * method will try to change the value of an existing property and will * not allow you to replace e.g. a ColorProperty with an IntProperty. * * Please also regard, that the key of a property must be a none empty string. * This is a precondition. Setting properties with empty keys will raise an exception. * * @ingroup DataManagement */ class MITKCORE_EXPORT PropertyList : public itk::Object, public IPropertyOwner { public: mitkClassMacroItkParent(PropertyList, itk::Object); /** * Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Map structure to hold the properties: the map key is a string, * the value consists of the actual property object (BaseProperty). */ typedef std::map PropertyMap; typedef std::pair PropertyMapElementType; // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * @brief Get a property by its name. */ mitk::BaseProperty *GetProperty(const std::string &propertyKey) const; /** * @brief Set a property object in the list/map by reference. * * The actual OBJECT holding the value of the property is replaced by this function. * This is useful if you want to change the type of the property, like from BoolProperty to StringProperty. - * Another use is to share one and the same property object among several ProperyList/DataNode objects, which + * Another use is to share one and the same property object among several PropertyList/DataNode objects, which * makes them appear synchronized. */ void ReplaceProperty(const std::string &propertyKey, BaseProperty *property); /** * @brief Set a property object in the list/map by reference. */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); //##Documentation //## @brief Convenience access method for GenericProperty properties //## (T being the type of the second parameter) //## @return @a true property was found template bool GetPropertyValue(const char *propertyKey, T &value) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /** * @brief Convenience method to access the value of a BoolProperty */ bool GetBoolProperty(const char *propertyKey, bool &boolValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, bool &boolValue) const; /** * @brief Convenience method to set the value of a BoolProperty */ void SetBoolProperty(const char *propertyKey, bool boolValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, bool boolValue); /** * @brief Convenience method to access the value of an IntProperty */ bool GetIntProperty(const char *propertyKey, int &intValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, int &intValue) const; /** * @brief Convenience method to set the value of an IntProperty */ void SetIntProperty(const char *propertyKey, int intValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, int intValue); /** * @brief Convenience method to access the value of a FloatProperty */ bool GetFloatProperty(const char *propertyKey, float &floatValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, float &floatValue) const; /** * @brief Convenience method to set the value of a FloatProperty */ void SetFloatProperty(const char *propertyKey, float floatValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, float floatValue); /** * @brief Convenience method to access the value of a DoubleProperty */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, double &doubleValue) const; /** * @brief Convenience method to set the value of a DoubleProperty */ void SetDoubleProperty(const char *propertyKey, double doubleValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, double doubleValue); /** * @brief Convenience method to access the value of a StringProperty */ bool GetStringProperty(const char *propertyKey, std::string &stringValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, std::string &stringValue) const; /** * @brief Convenience method to set the value of a StringProperty */ void SetStringProperty(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const std::string &stringValue); /** * @brief Get the timestamp of the last change of the map or the last change of one of * the properties store in the list (whichever is later). */ itk::ModifiedTimeType GetMTime() const override; /** * @brief Remove a property from the list/map. */ bool DeleteProperty(const std::string &propertyKey); const PropertyMap *GetMap() const { return &m_Properties; } bool IsEmpty() const { return m_Properties.empty(); } virtual void Clear(); protected: PropertyList(); PropertyList(const PropertyList &other); ~PropertyList() override; /** * @brief Map of properties. */ PropertyMap m_Properties; private: itk::LightObject::Pointer InternalClone() const override; }; } // namespace mitk #endif /* PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D */ diff --git a/Modules/Core/include/mitkPropertyObserver.h b/Modules/Core/include/mitkPropertyObserver.h index 62f1ae6ab6..04903c7e71 100644 --- a/Modules/Core/include/mitkPropertyObserver.h +++ b/Modules/Core/include/mitkPropertyObserver.h @@ -1,81 +1,81 @@ /*============================================================================ 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 MITK_BASEPROPERTYOBSERVER_H_INCLUDED #define MITK_BASEPROPERTYOBSERVER_H_INCLUDED #include "MitkCoreExports.h" #include "mitkCommon.h" #include namespace mitk { /** \brief Convenience class to observe changes of a mitk::BaseProperty. This class registers itself as an ITK observer to a BaseProperty and gets - informed of changes to the property. Whenever such a change occurrs, the virtual + informed of changes to the property. Whenever such a change occurs, the virtual method PropertyChanged() or PropertyRemoved() is called. This way, derived classes can implement behaviour for more specific properties (e.g. ColorProperty) without the need to reimplement the Subject-Observer handling. */ class BaseProperty; class MITKCORE_EXPORT PropertyObserver { public: PropertyObserver(); virtual ~PropertyObserver(); virtual void PropertyChanged() = 0; virtual void PropertyRemoved() = 0; protected: void BeginModifyProperty(); void EndModifyProperty(); unsigned long m_ModifiedTag; unsigned long m_DeleteTag; bool m_SelfCall; }; class MITKCORE_EXPORT PropertyView : public PropertyObserver { public: PropertyView(const mitk::BaseProperty *); ~PropertyView() override; void OnModified(const itk::EventObject &e); void OnDelete(const itk::EventObject &e); protected: const mitk::BaseProperty *m_Property; }; class MITKCORE_EXPORT PropertyEditor : public PropertyObserver { public: PropertyEditor(mitk::BaseProperty *); ~PropertyEditor() override; void OnModified(const itk::EventObject &e); void OnDelete(const itk::EventObject &e); protected: mitk::BaseProperty *m_Property; }; } #endif diff --git a/Modules/Core/include/mitkPropertyPersistenceInfo.h b/Modules/Core/include/mitkPropertyPersistenceInfo.h index 3f3a429149..1325f3953a 100644 --- a/Modules/Core/include/mitkPropertyPersistenceInfo.h +++ b/Modules/Core/include/mitkPropertyPersistenceInfo.h @@ -1,124 +1,124 @@ /*============================================================================ 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 mitkPropertyPersistenceInfo_h #define mitkPropertyPersistenceInfo_h #include #include #include #include #include namespace mitk { /** \brief Property persistence info. This class is used to specify the way the persistance of a property of BaseData derived instances is handled. The info specifies the key for property, as well as the mime type the info is defined for and should be used. Additionally the functions for deserialization and serialization of the property can be defined. As default */ class MITKCORE_EXPORT PropertyPersistenceInfo : public itk::LightObject { public: /** Signature specification for functions that can be provided for deserialization of the property. @post The function returns a valid instance derived from mitk::BaseProperty.*/ using DeserializationFunctionType = std::function; using SerializationFunctionType = std::function; using MimeTypeNameType = std::string; mitkClassMacroItkParent(PropertyPersistenceInfo, itk::LightObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(Self, const std::string &); mitkNewMacro2Param(Self, const std::string &, const std::string &); std::string GetName() const; std::string GetKey() const; void SetName(const std::string &name); void SetNameAndKey(const std::string &name, const std::string &key); bool IsRegEx() const; /** Sets the name and the key identifier as a regular expression that describes valid names and keys. - * @pre nameRegEx must be a valid regular expression, otherweis a regex_error esception + * @pre nameRegEx must be a valid regular expression, otherwise a regex_error esception * is thrown and the info object is not changed. - * @pre keyRegEx must be a valid regular expression, otherweis a regex_error esception + * @pre keyRegEx must be a valid regular expression, otherwise a regex_error esception * is thrown and the info object is not changed.*/ void UseRegEx(const std::string &nameRegEx, const std::string &nameTemplate); void UseRegEx(const std::string &nameRegEx, const std::string &nameTemplate, const std::string &keyRegEx, const std::string keyTemplate); const std::string &GetKeyTemplate() const; const std::string &GetNameTemplate() const; const MimeTypeNameType &GetMimeTypeName() const; void SetMimeTypeName(const MimeTypeNameType &mimeTypeName); const DeserializationFunctionType GetDeserializationFunction() const; void SetDeserializationFunction(const DeserializationFunctionType &fnc); const SerializationFunctionType GetSerializationFunction() const; void SetSerializationFunction(const SerializationFunctionType &fnc); PropertyPersistenceInfo::Pointer UnRegExByName(const std::string &propertyName) const; PropertyPersistenceInfo::Pointer UnRegExByKey(const std::string &key) const; /** This mime type name indicates that a info can be used for any mime type as long as another info with a more specific mime type is not available.*/ static MimeTypeNameType ANY_MIMETYPE_NAME(); protected: /** \brief Constructor. * * \param[in] name Name is the name of the property that described by the info. Key will be the same. */ PropertyPersistenceInfo(const std::string &name = ""); /** \brief Constructor. * * \param[in] name Name is the name of the property that described by the info. Key will be the same. * \param[in] mimeTypeName mime type the info is defined for. */ PropertyPersistenceInfo(const std::string &name, const std::string &mimeTypeName); ~PropertyPersistenceInfo() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; private: PropertyPersistenceInfo(const Self &other); Self &operator=(const Self &other); struct Impl; Impl *m_Impl; }; MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const PropertyPersistenceInfo &info); namespace PropertyPersistenceSerialization { /** Simple default serialization that uses prop->GetValueAsString for the serialization.*/ MITKCORE_EXPORT::std::string serializeByGetValueAsString(const mitk::BaseProperty *prop); } namespace PropertyPersistenceDeserialization { /** Simple default functions that puts the passed string into a string property.*/ MITKCORE_EXPORT mitk::BaseProperty::Pointer deserializeToStringProperty(const std::string &value); } } #endif diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h index 2ea5eadefa..1a77b47f64 100644 --- a/Modules/Core/include/mitkPropertyRelationRuleBase.h +++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h @@ -1,403 +1,403 @@ /*============================================================================ 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 mitkPropertyRelationRuleBase_h #define mitkPropertyRelationRuleBase_h #include "mitkIPropertyOwner.h" #include "mitkIdentifiable.h" #include "mitkException.h" #include "mitkNodePredicateBase.h" #include "mitkPropertyKeyPath.h" #include #include namespace mitk { /**Base class to standardize/abstract/encapsulate rules and business logic to detect and define (property/data based) relations in MITK. - Following important definitions must be regarded when using/implementing/specifing rule classes: - - Releations represented by rules are directed relations that point from a source IPropertyOwner (Source) + Following important definitions must be regarded when using/implementing/specifying rule classes: + - Relations represented by rules are directed relations that point from a source IPropertyOwner (Source) to a destination IPropertyOwner (Destination). - Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations. Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules, abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule: in its abstract state it cannot connect but be used to detect any type of generic ID relation. - A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the way to express the source image relation to an input image and to a mask would be nearly the same and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for "DICOM source mask" and both rules must have distinct rule IDs. - Source may have several relations of a rule to different Destinations. Destination may have several relations of a rule from different Sources. But a specific source destination pair may have only one relation of a specific rule id (concrete rule). A specific source destination pair may however have multiple relations for an abstract rule. - The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation but it should still be documented, even if the destination is unknown. One has to explicitly disconnect a zombie relation to get rid of it. - Each relation has its own UID (relationUID) that can be used to address it. The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer which uses the properties of Source and Destination to deduce if there is a relation of the rule type. The ID-layer is completely implemented by this base class. The base class falls back to the Data-layer (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated to (only) look at the data layer. Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction. - Rules use relation instance identifing (RII) properties in order to manage their relations that are stored in the + Rules use relation instance identifying (RII) properties in order to manage their relations that are stored in the Source. The RII-properties follow the following naming schema: "MITK.Relations.\.[relationUID|destinationUID|ruleID|\]" - \: The unique index of the relation for the Source. Used to assign/group the properties to their relation. In the default implementation of this class the instance id is an positive integer (i>0). - relationUID: The UID of the relation. Set by the ID-layer (so by this class) - destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements IIdentifiable. - ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set - automaticaly be this base class. + automatically be this base class. - : Information needed by the Data-layer (so derived classes) to find the relationUID */ class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object { public: mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); using RuleIDType = std::string; using RelationUIDType = Identifiable::UIDType; using RelationUIDVectorType = std::vector; /** Enum class for different types of relations. */ enum class RelationType { None = 0, /**< Two IPropertyOwner have no relation under the rule.*/ Data = 1, /**< Two IPropertyOwner have a relation, but it is "only" deduced from the Data-layer (so a bit "weaker" as ID). Reasons for the missing ID connection could be that Destintination has not IIdentifiable implemented.*/ - ID = 2, /**< Two IPropertyOwner have a relation and are explictly connected via the ID of IIdentifiable of the Destination.*/ - Complete = 3 /**< Two IPropertyOwner have a relation and are fully explictly connected (via data layer and ID layer).*/ + ID = 2, /**< Two IPropertyOwner have a relation and are explicitly connected via the ID of IIdentifiable of the Destination.*/ + Complete = 3 /**< Two IPropertyOwner have a relation and are fully explicitly connected (via data layer and ID layer).*/ }; using RelationVectorType = std::vector; /** Returns the generic root path for relation rules ("MITK.Relations").*/ static PropertyKeyPath GetRootKeyPath(); using InstanceIDType = std::string; /** Returns the property key path for a RII property. * @param propName If not empty a PropertyPath element will added (with the passed value) after the \ element. * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, * it is wildcarded and will match RIIs property of any instance.*/ static PropertyKeyPath GetRIIPropertyKeyPath(const std::string propName, const InstanceIDType& instanceID); /** Returns the property key path for RII RelationUID properties. * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, * it is wildcarded and will match RII RelationUIDs property of any instance.*/ static PropertyKeyPath GetRIIRelationUIDPropertyKeyPath(const InstanceIDType& instanceID = ""); /** Returns the property key path for RII RuleID properties. * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, * it is wildcarded and will match RII RuleIDs property of any instance.*/ static PropertyKeyPath GetRIIRuleIDPropertyKeyPath(const InstanceIDType& instanceID = ""); /** Returns the property key path for RII DestinationUID properties. * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, * it is wildcarded and will match RII DestinationUIDs property of any instance.*/ static PropertyKeyPath GetRIIDestinationUIDPropertyKeyPath(const InstanceIDType& instanceID = ""); /** Returns an ID string that identifies the rule class. @post The returned rule ID must met the preconditions of a PropertyKeyPath element name (see mitk::PropertyKeyPath*/ virtual RuleIDType GetRuleID() const = 0; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ virtual std::string GetDisplayName() const = 0; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ virtual std::string GetSourceRoleName() const = 0; - /** Returns a human readable string that can be used to describe the role of a destionation in context of the rule + /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ virtual std::string GetDestinationRoleName() const = 0; /** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement if another behavior is needed.*/ virtual bool IsAbstract() const; /** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsSourceCandidate(const IPropertyProvider *owner) const; /** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const; /** Returns true if the passed owner is a Source of a relation defined by the rule. @pre owner must be a pointer to a valid IPropertyProvider instance.*/ bool IsSource(const IPropertyProvider *owner) const; /** Returns all relation types of the passed IPropertyOwner instances. @remark Abstract rules may have several relationtypes between the instances (from different supported concrete rules), that cover both ID and Data relations; thus it returns a vector of RelationTypes. @result Vector of all relation types that exist between the given instances. Empty vector equals none relation at all. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ RelationVectorType GetRelationTypes(const IPropertyProvider* source, const IPropertyProvider* destination) const; /** Indicates if passed IPropertyOwner instances have a relation of a certain type. @remark Abstract rules may also indicate RelationType::Complete if there are multiple relations (from different supported concrete rules), that cover both ID and Data relations. @param source @param destination @param requiredRelation Defines the type of relation that should be present. None: does not matter which one, as long as at least one is present. Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ bool HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination, RelationType requiredRelation = RelationType::None) const; /** Returns a vector of relation UIDs for all relations of this rule instance that are defined for the passed source. @pre source must be a pointer to a valid IPropertyOwner instance. @param source @param layer Defines the layer the relations must be reflected. None: does not matter which one, as long as at least one is present. Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.*/ RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source, RelationType layer = RelationType::None) const; /** Returns the relation UID(s) for the passed source and destination of this rule instance. If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only one relation UID. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance.*/ RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Returns the relation UID for the passed source and destination of this rule instance. If the passed instances have no relation, no ID can be deduced and an exception will be thrown. If more than one relation is found, also an exception will be thrown. Thus only use this convenience method, if you are sure that one(!) relation UID can exist. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance. @pre Source and destination have one relation; otherwise if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists - a default MITK expception is thrown.*/ + a default MITK exception is thrown.*/ RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const; /**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet). Thus all nodes where IsSourceCandidate() returns true. */ NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const; /**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination yet). Thus all nodes where IsDestinationCandidate() returns true. */ NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const; /**Predicate that can be used to find nodes that are Sources of that rule and connected. Thus all nodes where IsSource() returns true.*/ NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const; /**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule @param destination Pointer to the Destination instance that should be used for detection. @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetSourcesDetector( const IPropertyProvider *destination, RelationType exclusiveRelation = RelationType::None) const; /**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule @param source Pointer to the Source instance that should be used for detection. @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Source must be a valid instance.*/ NodePredicateBase::ConstPointer GetDestinationsDetector( const IPropertyProvider *source, RelationType exclusiveRelation = RelationType::None) const; /**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID. @param source Pointer to the Source instance that should be used for detection. @param relationUID @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source, RelationUIDType relationUID) const; - /**Disconnect the passed instances by modifing source. One can specify which layer should be disconnected + /**Disconnect the passed instances by modifying source. One can specify which layer should be disconnected via the argument "layer". Default is the complete disconnection. All RII-properties or properties that define the connection on the data layer in the source for the passed destination will be removed. @pre source must be a valid instance. @pre destination must be a valid instance. @param source @param destination @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer = RelationType::Complete) const; - /**Disconnect the source from the passed relationUID (usefull for "zombie relations"). + /**Disconnect the source from the passed relationUID (useful for "zombie relations"). One can specify which layer should be disconnected via the argument "layer". Default is the complete disconnection. All RII-properties or properties that define the connection on the data layer in the source for the passed destination will be removed. If the relationUID is not part of the source. Nothing will be changed. @pre source must be a valid instance. @param source @param relationUID @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ void Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer = RelationType::Complete) const; /**Returns the list of PropertyKeyPaths of all properties that are relevant for a given relation. - @param source Pointer to the Source instance that containes the potential properties. + @param source Pointer to the Source instance that contains the potential properties. @param relationUID UID of the relation that is relevant for the requested properties. @param layer Indicates which layer is requested. ID: returns all RII properties that belong to the relation. Data: returns all properties that are relevant/belong to the data layer of the relation. Complete: returns all properties (ID+Data) @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ - std::vector GetReleationPropertyPaths(const IPropertyProvider* source, + std::vector GetRelationPropertyPaths(const IPropertyProvider* source, RelationUIDType relationUID, RelationType layer = RelationType::Data) const; protected: PropertyRelationRuleBase() = default; ~PropertyRelationRuleBase() override = default; using InstanceIDVectorType = std::vector; static InstanceIDType NULL_INSTANCE_ID(); /** Returns the instance IDs for the passed source and destination for this rule instance. If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID), an empty vector will be returned. @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider pair and concrete rule. But there might be more then one instanceID because either 1) the rule is abstract and supports multiple rule IDs or 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance.*/ InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source, const IPropertyProvider *destination) const; using DataRelationUIDVectorType = std::vector< std::pair >; - /** Returns the ReleationUIDs of all relations that are defined by the data layer of source for + /** Returns the RelationUIDs of all relations that are defined by the data layer of source for this rule instance and, if defined, destination. If the passed source (and destination) instance has no relation on the data layer, an empty vector will be returned. @remark Per definition for property relation rules only 0 or 1 instance should be found for one provider pair and concrete rule. But there might be more then one instance because either 1) the rule is abstract and supports multiple rule IDs or 2) the data layer may be ambiguous (e.g. because the destination was not specified) and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @param source @param destination Destination the find relations should point to. If destination is NULL any relation on the data layer for this rule and source are wanted. - @param instances_IDLayer List of releation instances that are already defined by the ID layer. The implementation of this - function should only cover releations that are not already resembled in the passed relarions_IDLayer. + @param instances_IDLayer List of relation instances that are already defined by the ID layer. The implementation of this + function should only cover relations that are not already resembled in the passed relarions_IDLayer. @pre source must be a pointer to a valid IPropertyProvider instance.*/ virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider * source, const IPropertyProvider * destination, const InstanceIDVectorType& instances_IDLayer) const = 0; /**Helper function that deduces the relation UID of the given relation instance. If it cannot be deduced an NoPropertyRelationException is thrown.*/ RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; /**Helper function that deduces the relation instance ID given the relation UID. If it cannot be deduced an NoPropertyRelationException is thrown.*/ InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source, const RelationUIDType &relationUID) const; /**Explicitly connects the passed instances. Afterwards they have a relation of Data (if data layer is supported), ID (if a destination implements IIdentifiable) or Complete (if Data and ID could be connected). If the passed instance are already connected the old connection will be overwritten (and raised to the highest possible connection level). @remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations. @pre source must be a valid instance. @pre destination must be a valid instance. @pre the rule instance must not be abstract. @return Return the relation uid of the relation connected by this method call*/ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; /**Is called by Connect() to ensure that source has correctly set properties to resemble the relation on the data layer. This means that the method should set the properties that describe and encode the relation on the data layer (data-layer-specific relation properties). If the passed instance are already connected, the old settings should be overwritten. Connect() will ensure that source and destination are valid pointers. @param source @param destination - @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance + @param instanceID is the ID for the relation instance that should be connected. Existence of the relation instance is ensured. @pre source must be a valid instance. @pre destination must be a valid instance.*/ virtual void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const = 0; /**This method is called by Disconnect() to remove all properties of the relation from the source that are set by Connect_datalayer(). @remark This method should remove all properties that are set for a specific relation by Connect_datalayer(...). If the relationUID is not part of the source, nothing will be changed. Disconnect() ensures that source is a valid pointer if called. @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ virtual void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const = 0; /** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because it is an abstract rule which also supports the rule ID. @return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/ virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const; /** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance. * @param propName If not empty a PropertyPath element will be added (with the passed value) after the \ element. * @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/ std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const; /**Helper function that deduces the instance ID out of a property name. If it cannot be deduced an MITK exception is thrown.*/ static InstanceIDType GetInstanceIDByPropertyName(const std::string propName); - /**Helper function that retrives the rule ID of a relation instance of a passed source. + /**Helper function that retrieves the rule ID of a relation instance of a passed source. @pre source must be valid. @pre source must have a relation instance with this ID*/ RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; - /**Helper function that retrives the destination UID of a relation instance of a passed + /**Helper function that retrieves the destination UID of a relation instance of a passed source. If the relation has no destination UID, an empty string will be returned. @pre source must be valid.*/ std::string GetDestinationUIDByInstanceID(const IPropertyProvider * source, const InstanceIDType & instanceID) const; itk::LightObject::Pointer InternalClone() const override; /** helper method that serves as a workaround until T24729 is done. Please remove if T24728 is done then could directly use owner->GetPropertyKeys() again.*/ static std::vector GetPropertyKeys(const IPropertyProvider *owner); /** Helper method that tries to cast the provider to the Identifiable interface.*/ const Identifiable* CastProviderAsIdentifiable(const mitk::IPropertyProvider* provider) const; private: - /** Creats a relation UID*/ + /** Creates a relation UID*/ static RelationUIDType CreateRelationUID(); - /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be genarated + /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be generated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const; }; /**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/ class NoPropertyRelationException : public Exception { public: mitkExceptionClassMacro(NoPropertyRelationException, Exception) }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkProportionalTimeGeometry.h b/Modules/Core/include/mitkProportionalTimeGeometry.h index 699ed702a9..48ff6563d0 100644 --- a/Modules/Core/include/mitkProportionalTimeGeometry.h +++ b/Modules/Core/include/mitkProportionalTimeGeometry.h @@ -1,260 +1,260 @@ /*============================================================================ 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 ProportionalTimeGeometry_h #define ProportionalTimeGeometry_h // MITK #include #include #include #include namespace mitk { /** * \brief Organizes geometries over proportional time steps * * For this TimeGeometry implementation it is assumed that * the durations of the time steps are equidistant, e.g. * the durations of the time steps in one ProportionalTimeGeometry * are the same. The geometries of the time steps are independent, * and not linked to each other. Since the timeBounds of the * geometries are different for each time step it is not possible * to set the same geometry to different time steps. Instead * copies should be used. * * \addtogroup geometry */ class MITKCORE_EXPORT ProportionalTimeGeometry : public TimeGeometry { public: mitkClassMacro(ProportionalTimeGeometry, TimeGeometry); ProportionalTimeGeometry(); typedef ProportionalTimeGeometry self; itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ TimeStepType CountTimeSteps() const override; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for this geometry. If only one * time steps available it usually goes from -max to +max. The time point * is given in ms. */ TimePointType GetMinimumTimePoint() const override; /** * \brief Returns the last time point for which the object is valid * - * Gives the last time point for which a valid geometrie is saved in + * Gives the last time point for which a valid geometry is saved in * this time geometry. The time point is given in ms. */ TimePointType GetMaximumTimePoint() const override; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ TimePointType GetMinimumTimePoint(TimeStepType step) const override; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ TimePointType GetMaximumTimePoint(TimeStepType step) const override; /** * \brief Get the time bounds (in ms) */ TimeBounds GetTimeBounds() const override; /** * \brief Get the time bounds for the given TimeStep (in ms) */ TimeBounds GetTimeBounds(TimeStepType step) const override; /** * \brief Tests if a given time point is covered by this object * * Returns true if a geometry can be returned for the given time * point and falls if not. The time point must be given in ms. */ bool IsValidTimePoint(TimePointType timePoint) const override; /** - * \brief Test for the given time step if a geometry is availible + * \brief Test for the given time step if a geometry is available * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. - * The time step is defined as positiv number. + * The time step is defined as positive number. */ bool IsValidTimeStep(TimeStepType timeStep) const override; /** * \brief Converts a time step to a time point * * Converts a time step to a time point in a way that * the new time point indicates the same geometry as the time step. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ TimePointType TimeStepToTimePoint(TimeStepType timeStep) const override; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. - * If a negativ invalid time point is given always time step 0 is - * returned. If an positiv invalid time step is given an invalid + * If a negative invalid time point is given always time step 0 is + * returned. If an positive invalid time step is given an invalid * time step will be returned. */ TimeStepType TimePointToTimeStep(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns a clone of the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. */ BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const override; /** * \brief Returns the geometry which corresponds to the given time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const override; /** - * \brief Tests if all necessary informations are set and the object is valid + * \brief Tests if all necessary information are set and the object is valid */ bool IsValid() const override; /** - * \brief Initilizes a new object with one time steps which contains an empty geometry. + * \brief Initializes a new object with one time steps which contains an empty geometry. */ void Initialize() override; /** * \brief Expands the time geometry to the given number of time steps. * * Initializes the new time steps with empty geometries if no timesteps * in the geometry so far. Otherwise fills the new times steps with * clones of the first time step. * Shrinking is not supported. */ void Expand(TimeStepType size) override; /** * \brief Sets the geometry for the given time step * * This method does not afflict other time steps, since the geometry for * each time step is saved individually. */ void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) override; /** * \brief Replaces the geometry instances with clones of the passed geometry. * * Replaces the geometries of all time steps with clones of the passed * geometry. Replacement strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ void ReplaceTimeStepGeometries(const BaseGeometry *geometry) override; /** * \brief Makes a deep copy of the current object */ itk::LightObject::Pointer InternalClone() const override; itkGetConstMacro(FirstTimePoint, TimePointType); itkSetMacro(FirstTimePoint, TimePointType); itkGetConstMacro(StepDuration, TimePointType); itkSetMacro(StepDuration, TimePointType); // void SetGeometryForTimeStep(TimeStepType timeStep, BaseGeometry& geometry); void ClearAllGeometries(); // void AddGeometry(BaseGeometry geometry); void ReserveSpaceForGeometries(TimeStepType numberOfGeometries); /** * \brief Initializes the TimeGeometry with equally time Step geometries * * Saves a copy for each time step. */ void Initialize(const BaseGeometry *geometry, TimeStepType timeSteps); /** * \brief Initialize the TimeGeometry with empty BaseGeometry */ void Initialize(TimeStepType timeSteps); void PrintSelf(std::ostream &os, itk::Indent indent) const override; protected: ~ProportionalTimeGeometry() override; std::vector m_GeometryVector; TimePointType m_FirstTimePoint; TimePointType m_StepDuration; }; // end class ProportialTimeGeometry /** * @brief Equal A function comparing two ProportionalTimeGeometries for being identical. * * @ingroup MITKTestingAPI * * The function compares two instances of ProportionalTimeGeometries in all their aspects. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolerance for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::ProportionalTimeGeometry &leftHandSide, const mitk::ProportionalTimeGeometry &rightHandSide, ScalarType eps, bool verbose); } // end namespace MITK #endif // ProportionalTimeGeometry_h diff --git a/Modules/Core/include/mitkRenderingManager.h b/Modules/Core/include/mitkRenderingManager.h index 6518d232d8..2691261279 100644 --- a/Modules/Core/include/mitkRenderingManager.h +++ b/Modules/Core/include/mitkRenderingManager.h @@ -1,487 +1,487 @@ /*============================================================================ 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 MITKRENDERINGMANAGER_H #define MITKRENDERINGMANAGER_H #include #include #include #include #include #include #include #include class vtkRenderWindow; class vtkObject; namespace mitk { class RenderingManagerFactory; class BaseGeometry; class SliceNavigationController; class BaseRenderer; class DataStorage; /** * \brief Manager for coordinating the rendering process. * * RenderingManager is a central instance retrieving and executing * RenderWindow update requests. Its main purpose is to coordinate * distributed requests which cannot be aware of each other - lacking the * knowledge of whether they are really necessary or not. For example, two * objects might determine that a specific RenderWindow needs to be updated. * This would result in one unnecessary update, if both executed the update * on their own. * * The RenderingManager addresses this by letting each such object * request an update, and waiting for other objects to possibly * issue the same request. The actual update will then only be executed at a * well-defined point in the main event loop (this may be each time after * event processing is done). * - * Convinience methods for updating all RenderWindows which have been - * registered with the RenderingManager exist. If theses methods are not + * Convenience methods for updating all RenderWindows which have been + * registered with the RenderingManager exist. If these methods are not * used, it is not required to register (add) RenderWindows prior to using * the RenderingManager. * * The methods #ForceImmediateUpdate() and #ForceImmediateUpdateAll() can * be used to force the RenderWindow update execution without any delay, * bypassing the request functionality. * * The interface of RenderingManager is platform independent. Platform * specific subclasses have to be implemented, though, to supply an - * appropriate event issueing for controlling the update execution process. + * appropriate event issuing for controlling the update execution process. * See method documentation for a description of how this can be done. * * \sa TestingRenderingManager An "empty" RenderingManager implementation which * can be used in tests etc. * */ class MITKCORE_EXPORT RenderingManager : public itk::Object { public: mitkClassMacroItkParent(RenderingManager, itk::Object); typedef std::vector RenderWindowVector; typedef std::vector FloatVector; typedef std::vector BoolVector; typedef itk::SmartPointer DataStoragePointer; enum RequestType { REQUEST_UPDATE_ALL = 0, REQUEST_UPDATE_2DWINDOWS, REQUEST_UPDATE_3DWINDOWS }; static Pointer New(); /** Set the object factory which produces the desired platform specific * RenderingManager singleton instance. */ static void SetFactory(RenderingManagerFactory *factory); /** Get the object factory which produces the platform specific * RenderingManager instances. */ static const RenderingManagerFactory *GetFactory(); /** Returns true if a factory has already been set. */ static bool HasFactory(); /** Get the RenderingManager singleton instance. */ static RenderingManager *GetInstance(); /** Returns true if the singleton instance does already exist. */ static bool IsInstantiated(); /** Adds a RenderWindow. This is required if the methods #RequestUpdateAll * or #ForceImmediateUpdate are to be used. */ void AddRenderWindow(vtkRenderWindow *renderWindow); /** Removes a RenderWindow. */ void RemoveRenderWindow(vtkRenderWindow *renderWindow); /** Get a list of all registered RenderWindows */ const RenderWindowVector &GetAllRegisteredRenderWindows(); /** Requests an update for the specified RenderWindow, to be executed as * soon as the main loop is ready for rendering. */ void RequestUpdate(vtkRenderWindow *renderWindow); /** Immediately executes an update of the specified RenderWindow. */ void ForceImmediateUpdate(vtkRenderWindow *renderWindow); /** Requests all currently registered RenderWindows to be updated. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void RequestUpdateAll(RequestType type = REQUEST_UPDATE_ALL); /** Immediately executes an update of all registered RenderWindows. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void ForceImmediateUpdateAll(RequestType type = REQUEST_UPDATE_ALL); /** * @brief Initialize the render windows by the aggregated geometry of all objects that are held in * the data storage. * * @param dataStorage The data storage from which the bounding object can be retrieved */ virtual void InitializeViewsByBoundingObjects(const DataStorage* dataStorage); /** * @brief Initialize the render windows specified by "type" to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param geometry The geometry to be used to initialize / update a * render window's time and slice navigation controller * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize / update the * time and slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize / update the * time and slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize / update the * time and slice navigation controller of 3D render windows * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeViews(const BaseGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool resetCamera = true); /** * @brief Initialize the render windows specified by "type" to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param geometry The geometry to be used to initialize / update a * render window's time- and slice navigation controller * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize / update the * time- and slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize / update the * time- and slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize / update the * time- and slice navigation controller of 3D render windows * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeViews(const TimeGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool resetCamera = true); /** * @brief Initialize the render windows specified by "type" to the default viewing direction * without updating the geometry information. * * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize the * slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize the * slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize the * slice navigation controller of 3D render windows */ virtual bool InitializeViews(RequestType type = REQUEST_UPDATE_ALL); /** * @brief Initialize the specified render window to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * - * @param renderWindow The specifid render window to update + * @param renderWindow The specific render window to update * @param geometry The geometry to be used to initialize / update the * render window's time- and slice navigation controller * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool resetCamera = true); /** * @brief Initialize the specified render window to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * - * @param renderWindow The specifid render window to update + * @param renderWindow The specific render window to update * @param geometry The geometry to be used to initialize / update the * render window's time- and slice navigation controller * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool resetCamera = true); /** * @brief Initialize the specified render window to the default viewing direction * without updating the geometry information. * - * @param renderWindow The specifid render window to update + * @param renderWindow The specific render window to update */ virtual bool InitializeView(vtkRenderWindow *renderWindow); /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ const SliceNavigationController *GetTimeNavigationController() const; /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ SliceNavigationController *GetTimeNavigationController(); ~RenderingManager() override; /** Executes all pending requests. This method has to be called by the * system whenever a RenderingManager induced request event occurs in * the system pipeline (see concrete RenderingManager implementations). */ virtual void ExecutePendingRequests(); bool IsRendering() const; void AbortRendering(); /** En-/Disable LOD increase globally. */ itkSetMacro(LODIncreaseBlocked, bool); /** En-/Disable LOD increase globally. */ itkGetMacro(LODIncreaseBlocked, bool); /** En-/Disable LOD increase globally. */ itkBooleanMacro(LODIncreaseBlocked); /** En-/Disable LOD abort mechanism. */ itkSetMacro(LODAbortMechanismEnabled, bool); /** En-/Disable LOD abort mechanism. */ itkGetMacro(LODAbortMechanismEnabled, bool); /** En-/Disable LOD abort mechanism. */ itkBooleanMacro(LODAbortMechanismEnabled); /** Force a sub-class to start a timer for a pending hires-rendering request */ virtual void StartOrResetTimer(){}; /** To be called by a sub-class from a timer callback */ void ExecutePendingHighResRenderingRequest(); virtual void DoStartRendering(){}; virtual void DoMonitorRendering(){}; virtual void DoFinishAbortRendering(){}; int GetNextLOD(BaseRenderer *renderer); /** Set current LOD (nullptr means all renderers)*/ void SetMaximumLOD(unsigned int max); void SetShading(bool state, unsigned int lod); bool GetShading(unsigned int lod); void SetClippingPlaneStatus(bool status); bool GetClippingPlaneStatus(); void SetShadingValues(float ambient, float diffuse, float specular, float specpower); FloatVector &GetShadingValues(); /** Returns a property list */ PropertyList::Pointer GetPropertyList() const; /** Returns a property from m_PropertyList */ BaseProperty *GetProperty(const char *propertyKey) const; /** Sets or adds (if not present) a property in m_PropertyList */ void SetProperty(const char *propertyKey, BaseProperty *propertyValue); /** * \brief Setter for internal DataStorage * * Sets the DataStorage that is used internally. This instance holds all DataNodes that are * rendered by the registered BaseRenderers. * * If this DataStorage is changed at runtime by calling SetDataStorage(), * all currently registered BaseRenderers are automatically given the correct instance. * When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage. */ void SetDataStorage(DataStorage *storage); /** * \brief Getter for internal DataStorage * * Returns the DataStorage that is used internally. This instance holds all DataNodes that are * rendered by the registered BaseRenderers. */ itkGetMacro(DataStorage, DataStorage*); itkGetConstMacro(DataStorage, DataStorage*); /** * @brief Sets a flag to the given renderwindow to indicated that it has the focus e.g. has been clicked recently. * @param focusWindow */ void SetRenderWindowFocus(vtkRenderWindow *focusWindow); itkGetMacro(FocusedRenderWindow, vtkRenderWindow *); itkSetMacro(ConstrainedPanningZooming, bool); itkGetConstMacro(ConstrainedPanningZooming, bool); void SetAntiAliasing(AntiAliasing antiAliasing); itkGetConstMacro(AntiAliasing, AntiAliasing); protected: enum { RENDERING_INACTIVE = 0, RENDERING_REQUESTED, RENDERING_INPROGRESS }; RenderingManager(); /** Abstract method for generating a system specific event for rendering * request. This method is called whenever an update is requested */ virtual void GenerateRenderingRequestEvent() = 0; virtual void InitializePropertyList(); bool m_UpdatePending; typedef std::map RendererIntMap; typedef std::map RendererBoolMap; RendererBoolMap m_RenderingAbortedMap; RendererIntMap m_NextLODMap; unsigned int m_MaxLOD; bool m_LODIncreaseBlocked; bool m_LODAbortMechanismEnabled; BoolVector m_ShadingEnabled; bool m_ClippingPlaneEnabled; FloatVector m_ShadingValues; static void RenderingStartCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); static void RenderingProgressCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); static void RenderingEndCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); typedef std::map RenderWindowList; RenderWindowList m_RenderWindowList; RenderWindowVector m_AllRenderWindows; struct RenderWindowCallbacks { vtkCallbackCommand *commands[3u]; }; typedef std::map RenderWindowCallbacksList; RenderWindowCallbacksList m_RenderWindowCallbacksList; itk::SmartPointer m_TimeNavigationController; static RenderingManager::Pointer s_Instance; static RenderingManagerFactory *s_RenderingManagerFactory; PropertyList::Pointer m_PropertyList; DataStoragePointer m_DataStorage; bool m_ConstrainedPanningZooming; private: /** * @brief Initialize the specified renderer to the given geometry. * - * @param baseRenderer The specifid renderer to update + * @param baseRenderer The specific renderer to update * @param geometry The geometry to be used to initialize / update the * render window's slice navigation controller * @param boundingBoxInitialized If this parameter is set to true, the slice navigation controller will be * initialized / updated with the given geometry. If set to false, the geometry * of the slice navigation controller is not updated. * @param mapperID The mapper ID is used to define if the given renderer is a 2D or a 3D renderer. * In case of a 2D renderer and if "boundingBoxInitialized" is set to true (slice * navigation controller will be updated with a new geometry), the position of the * slice navigation controller is set to the center slice. * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ void InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera); /** * @brief Extend the bounding box of the given geometry to make sure the bounding box has an extent bigger than * zero in any direction. * * @param originalGeometry The original geometry to be extended * @param modifiedGeometry The modified geometry where the new bounds (extended bounding box) are used / set */ bool ExtendGeometryForBoundingBox(const TimeGeometry* originalGeometry, TimeGeometry::Pointer& modifiedGeometry); vtkRenderWindow *m_FocusedRenderWindow; AntiAliasing m_AntiAliasing; }; #pragma GCC visibility push(default) itkEventMacroDeclaration(RenderingManagerEvent, itk::AnyEvent); itkEventMacroDeclaration(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); #pragma GCC visibility pop itkEventMacroDeclaration(FocusChangedEvent, itk::AnyEvent); /** - * Generic RenderingManager implementation for "non-rendering-plattform", + * Generic RenderingManager implementation for "non-rendering-platform", * e.g. for tests. Its factory (TestingRenderingManagerFactory) is * automatically on start-up and is used by default if not other * RenderingManagerFactory is instantiated explicitly thereafter. * (see mitkRenderingManager.cpp) */ class MITKCORE_EXPORT TestingRenderingManager : public RenderingManager { public: mitkClassMacro(TestingRenderingManager, RenderingManager); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected: void GenerateRenderingRequestEvent() override {}; }; } // namespace mitk #endif // MITKRENDERINGMANAGER_H diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h index 019e3aa184..c45673095e 100644 --- a/Modules/Core/include/mitkSliceNavigationController.h +++ b/Modules/Core/include/mitkSliceNavigationController.h @@ -1,439 +1,439 @@ /*============================================================================ 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 BaseGeometry or a TimeGeometry as input world geometry * (TODO what are the exact requirements?) and generates a TimeGeometry * as output. 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. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // BaseGeometry::ConstPointer) * sliceCtrl->SetInputWorldTimeGeometry(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator 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.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator 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 navigator 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(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \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. * QmitkSliderNavigator (for Qt): * * \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 Possible view directions, \a Original will use * the PlaneGeometry instances in a SlicedGeometry3D provided * as input world geometry (by SetInputWorldTimeGeometry). */ enum ViewDirection { Axial, Sagittal, Coronal, Original }; /** * \brief Set the input world time geometry out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in 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(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \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(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); 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(ViewDirection 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(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); 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(GeometryTimeEvent, 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); } template void ConnectGeometryTimeEvent(T* receiver) { auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryEvents(T* receiver) { // connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // 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(); /** \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 SliceNavigationController. * If the time geometry is not yet set, this function will always return 0.0.*/ TimePointType GetSelectedTimePoint() const; protected: SliceNavigationController(); ~SliceNavigationController() override; void CreateWorldGeometry(bool top, bool frontside, bool rotated); TimeGeometry::ConstPointer m_InputWorldTimeGeometry; TimeGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection 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 // MITKSLICENAVIGATIONCONTROLLER_H diff --git a/Modules/Core/include/mitkSlicedGeometry3D.h b/Modules/Core/include/mitkSlicedGeometry3D.h index 573541b549..da2c611722 100644 --- a/Modules/Core/include/mitkSlicedGeometry3D.h +++ b/Modules/Core/include/mitkSlicedGeometry3D.h @@ -1,328 +1,328 @@ /*============================================================================ 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 MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include "mitkBaseGeometry.h" #include "mitkPlaneGeometry.h" namespace mitk { class SliceNavigationController; class NavigationController; /** \brief Describes the geometry of a data object consisting of slices. * * A PlaneGeometry can be requested for each slice. In the case of * \em evenly-spaced, \em plane geometries (m_EvenlySpaced==true), * only the 2D-geometry of the first slice has to be set (to an instance of * PlaneGeometry). The 2D geometries of the other slices are calculated * by shifting the first slice in the direction m_DirectionVector by * m_Spacing.z * sliceNumber. The m_Spacing member (which is only - * relevant in the case m_EvenlySpaced==true) descibes the size of a voxel + * relevant in the case m_EvenlySpaced==true) describes the size of a voxel * (in mm), i.e., m_Spacing.x is the voxel width in the x-direction of the * plane. It is derived from the reference geometry of this SlicedGeometry3D, * which usually would be the global geometry describing how datasets are to * be resliced. * * By default, slices are oriented in the direction of one of the main axes * (x, y, z). However, by means of rotation, it is possible to realign the * slices in any possible direction. In case of an inclined plane, the spacing * is derived as a product of the (regular) geometry spacing and the direction * vector of the plane. * * SlicedGeometry3D and the associated PlaneGeometries have to be initialized in * the method GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing pic * tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \a itk::DataObject::UpdateOutputInformation(). * * Rule: everything is in mm (or ms for temporal information) if not * stated otherwise. * * \warning The hull (i.e., transform, bounding-box and * time-bounds) is only guaranteed to be up-to-date after calling * UpdateInformation(). * * \ingroup Geometry */ class MITKCORE_EXPORT SlicedGeometry3D : public mitk::BaseGeometry { public: mitkClassMacro(SlicedGeometry3D, BaseGeometry); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Returns the PlaneGeometry of the slice (\a 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[3]*s * in the direction of m_DirectionVector. * * \warning The PlaneGeometries are not necessarily up-to-date and not even * initialized. * * The PlaneGeometries have to be initialized in the method * GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing * pic tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \sa itk::DataObject::UpdateOutputInformation(). */ virtual mitk::PlaneGeometry *GetPlaneGeometry(int s) const; /** * \deprecatedSince{2014_10} Please use GetPlaneGeometry */ DEPRECATED(const PlaneGeometry *GetGeometry2D(int s)) { return GetPlaneGeometry(s); } /** * \deprecatedSince{2014_10} Please use SetPlaneGeometry */ DEPRECATED(void SetGeometry2D(PlaneGeometry *geo, int s)) { SetPlaneGeometry(geo, s); } //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to //change // the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and // changes the origin respectively. void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) override; // virtual void SetTimeBounds( const mitk::TimeBounds& timebounds ); const mitk::BoundingBox *GetBoundingBox() const override; /** * \brief Get the number of slices */ itkGetConstMacro(Slices, unsigned int); /** * \brief Set PlaneGeometry of slice \a s. */ virtual bool SetPlaneGeometry(mitk::PlaneGeometry *geometry2D, int s); /** * \brief Check whether a slice exists */ virtual bool IsValidSlice(int s = 0) const; virtual const BaseGeometry* GetReferenceGeometry() const; virtual void SetReferenceGeometry(const BaseGeometry *referenceGeometry); bool HasReferenceGeometry() const; /** * \brief Set the SliceNavigationController corresponding to this sliced * geometry. * * The SNC needs to be informed when the number of slices in the geometry * changes, which can occur whenthe slices are re-oriented by rotation. */ virtual void SetSliceNavigationController(mitk::SliceNavigationController *snc); mitk::SliceNavigationController *GetSliceNavigationController(); /** * \brief Set/Get whether the SlicedGeometry3D is evenly-spaced * (m_EvenlySpaced) * * 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.z * s in the direction of * m_DirectionVector. * * \sa GetPlaneGeometry */ itkGetConstMacro(EvenlySpaced, bool); virtual void SetEvenlySpaced(bool on = true); /** * \brief Set/Get the vector between slices for the evenly-spaced case * (m_EvenlySpaced==true). * * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. * * \sa m_DirectionVector */ virtual void SetDirectionVector(const mitk::Vector3D &directionVector); itkGetConstMacro(DirectionVector, const mitk::Vector3D &); itk::LightObject::Pointer InternalClone() const override; #ifndef SWIG static const std::string SLICES; const static std::string DIRECTION_VECTOR; const static std::string EVENLY_SPACED; #endif // !SWIG /** * \brief Tell this instance how many PlaneGeometries it shall manage. Bounding * box and the PlaneGeometries must be set additionally by calling the respective * methods! * * \warning Bounding box and the 2D-geometries must be set additionally: use * SetBounds(), SetGeometry(). */ virtual void InitializeSlicedGeometry(unsigned int slices); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided PlaneGeometry that is used as the first slice and * for spacing calculation. * * Initializes the bounding box according to the width/height of the * PlaneGeometry and \a slices. The spacing is calculated from the PlaneGeometry. */ virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided PlaneGeometry that is used as the first slice and * for spacing calculation (except z-spacing). * * Initializes the bounding box according to the width/height of the * PlaneGeometry and \a slices. The x-/y-spacing is calculated from the * PlaneGeometry. */ virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, mitk::ScalarType zSpacing, unsigned int slices); /** * \brief Completely initialize this instance as evenly-spaced plane slices * parallel to a side of the provided BaseGeometry and using its spacing * information. * * Initializes the bounding box according to the width/height of the * BaseGeometry and the number of slices according to * BaseGeometry::GetExtent(2). * * \param geometry3D * \param planeorientation side parallel to which the slices will be oriented * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Axial, for other plane locations respectively) * \param frontside defines the side of the plane (the definition of * front/back is somewhat arbitrary) * * \param rotated rotates the plane by 180 degree around its normal (the * definition of rotated vs not rotated is somewhat arbitrary) */ virtual void InitializePlanes(const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top = true, bool frontside = true, bool rotated = false); void SetImageGeometry(const bool isAnImageGeometry) override; void ExecuteOperation(Operation *operation) override; static double CalculateSpacing(const mitk::Vector3D &spacing, const mitk::Vector3D &d); protected: SlicedGeometry3D(); SlicedGeometry3D(const SlicedGeometry3D &other); ~SlicedGeometry3D() override; /** * Reinitialize plane stack after rotation. More precisely, the first plane * of the stack needs to spatially aligned, in two respects: * * 1. Re-alignment with respect to the dataset center; this is necessary * since the distance from the first plane to the center could otherwise * continuously decrease or increase. * 2. Re-alignment with respect to a given reference point; the reference * point is a location which the user wants to be exactly touched by one * plane of the plane stack. The first plane is minimally shifted to * ensure this touching. Usually, the reference point would be the * point around which the geometry is rotated. */ virtual void ReinitializePlanes(const Point3D ¢er, const Point3D &referencePoint); ScalarType GetLargestExtent(const BaseGeometry *geometry); void PrintSelf(std::ostream &os, itk::Indent indent) const override; /** Calculate "directed spacing", i.e. the spacing in directions * non-orthogonal to the coordinate axes. This is done via the * ellipsoid equation. */ double CalculateSpacing(const mitk::Vector3D &direction) const; /** The extent of the slice stack, i.e. the number of slices, depends on the * plane normal. For rotated geometries, the geometry's transform needs to * be accounted in this calculation. */ mitk::Vector3D AdjustNormal(const mitk::Vector3D &normal) const; /** * Container for the 2D-geometries contained within this SliceGeometry3D. */ mutable std::vector m_PlaneGeometries; /** * 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.z*s * in the direction of m_DirectionVector. * * \sa GetPlaneGeometry */ bool m_EvenlySpaced; /** * Vector between slices for the evenly-spaced case (m_EvenlySpaced==true). * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. */ mutable mitk::Vector3D m_DirectionVector; - /** Number of slices this SliceGeometry3D is descibing. */ + /** Number of slices this SliceGeometry3D is describing. */ unsigned int m_Slices; /** Underlying BaseGeometry for this SlicedGeometry */ const mitk::BaseGeometry *m_ReferenceGeometry; /** SNC correcsponding to this geometry; used to reflect changes in the * number of slices due to rotation. */ // mitk::NavigationController *m_NavigationController; mitk::SliceNavigationController *m_SliceNavigationController; //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; void PreSetSpacing(const mitk::Vector3D &aSpacing) override; }; } // namespace mitk #endif /* MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Modules/Core/include/mitkSourceImageRelationRule.h b/Modules/Core/include/mitkSourceImageRelationRule.h index b6647a871f..bdb0cd6dc2 100644 --- a/Modules/Core/include/mitkSourceImageRelationRule.h +++ b/Modules/Core/include/mitkSourceImageRelationRule.h @@ -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. ============================================================================*/ #ifndef MITK_SOURCE_IMAGE_RELATION_RULE_H #define MITK_SOURCE_IMAGE_RELATION_RULE_H #include "mitkPropertyRelationRuleBase.h" #include "mitkImage.h" namespace mitk { /**This rule class can be used for relations that reference an image as source for a destination entity. (e.g. an image that is used to generate the relation source). - The ID-layer is supported like for GenericIDReleations. + The ID-layer is supported like for GenericIDRelations. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In addition the rule uses the data-layer to deduce/define relations. For this layer it uses properties compliant to DICOM. Thus (1) the information is stored in a DICOM Source Image Sequence item (0x0008,0x2112) and (2) the destination must have properties DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016). If the destination does not have this properties, no connection can be made on the data-layer. @remark Please note that PropertyRelationRules and DICOM use the term "source" differently. The DICOM source (image) equals the PropertyRelationRule destination. This is due to an inverted relation direction. So in the context of the SourceImageRelationRule interface a derived data is the source and points to the original image, it derives from. In the context of DICOM this referenced original image would be called source image (as the name of this class). In order to be able to use this class for different relation types (DICOM would call it purposes), the purposeTag is used. It must be specified when creating a rule instance. The purposeTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. One may also specify the display name and the role names of the instance. If not specified the default values are used (display name: " relation", source role name: "derived data", destination role name: "source image") */ class MITKCORE_EXPORT SourceImageRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(SourceImageRelationRule, PropertyRelationRuleBase); itkNewMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; bool IsDestinationCandidate(const IPropertyProvider *owner) const override; /** Connects to passed images. - @remark destination must specifiy DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016) + @remark destination must specify DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016) in order to establish a connection on the data layer.*/ RelationUIDType Connect(Image *source, const Image *destination) const; protected: SourceImageRelationRule(); SourceImageRelationRule(const RuleIDType &purposeTag); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~SourceImageRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 (and its associated ruleID) that refer to destination or all (if no destination is passed) with a supported RuleID and which are not already covered by the ignoreInstances.*/ std::vector > GetReferenceSequenceIndices(const IPropertyProvider * source, const IPropertyProvider* destination = nullptr, InstanceIDVectorType ignoreInstances = {}) const; using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; /**Prepares a new reference to an image on the data layer. Therefore an unused and valid sequence item index - for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The + for the passed source will be generated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const; std::string GenerateRuleID(const std::string& purpose) const; private: RuleIDType m_PurposeTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkStandardFileLocations.h b/Modules/Core/include/mitkStandardFileLocations.h index d49dc2405e..1bc4bb5f0d 100644 --- a/Modules/Core/include/mitkStandardFileLocations.h +++ b/Modules/Core/include/mitkStandardFileLocations.h @@ -1,112 +1,112 @@ /*============================================================================ 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 MITK_STANDARD_FILE_LOCATIONS_H_INCLUDED_SWDG #define MITK_STANDARD_FILE_LOCATIONS_H_INCLUDED_SWDG #include #include #include #include namespace mitk { /*! \brief Provides a method to look for configuration and option files etc. Call mitk::StandardFileLocations::FindFile(filename) to look for configuration files. Call mitk::StandardFileLocations::GetOptionDirectory() to look for/save option files. */ class MITKCORE_EXPORT StandardFileLocations : public itk::Object { public: typedef StandardFileLocations Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; /*! \brief Adds a directory into the search queue: \ Use this function in combination with FindFile(), after adding some \ directories, they will also be searched for the requested file \param dir directory you want to be searched in - \param insertInFrontOfSearchList wheather this search request shall be processed first + \param insertInFrontOfSearchList whether this search request shall be processed first */ void AddDirectoryForSearch(const char *dir, bool insertInFrontOfSearchList = true); /*! \brief Remove a directory from the search queue: \ Use this function in combination with FindFile(). \param dir directory you want to be searched in */ void RemoveDirectoryForSearch(const char *dir); /*! \brief looks for a file in several standard locations \param filename The file you want to fine, without any path \param pathInSourceDir Where in the source tree hierarchy would that file be? \return The absolute path to the file including the filename This method appends several standard locations to the end of the searchqueue (if they not already exist) and then searches for the file within all directories contained in the search queue:
  1. Add the directory specified in the environment variable MITKCONF
  2. Add the .mitk directory in the home folder of the user
  3. Add the current working directory
  4. Add the (current working directory)/bin directory
  5. Add the directory specified in pathInSourceDir, that is relative to the source code directory root (which is determined at compile time)
Already added directories in the searchqueue by using AddDirectoryForSearch before calling FindFile are still searched first, because above mentioned standard locations are always appended at the end of the list. */ std::string FindFile(const char *filename, const char *pathInSourceDir = nullptr); /*! \brief Return directory of/for option files \return The absolute path to the directory for option files. This method looks for the directory of/for option files in two ways. The logic is as follows 1. If there is an environment variable MITKOPTIONS, then use that directory. 2. Use .mitk-subdirectory in home directory of the user The directory will be created if it does not exist. */ std::string GetOptionDirectory(); static StandardFileLocations *GetInstance(); protected: itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef std::vector FileSearchVectorType; FileSearchVectorType m_SearchDirectories; StandardFileLocations(); ~StandardFileLocations() override; std::string SearchDirectoriesForFile(const char *filename); private: // Private Copy Constructor StandardFileLocations(const StandardFileLocations &); }; } // namespace #endif diff --git a/Modules/Core/include/mitkStatusBarImplementation.h b/Modules/Core/include/mitkStatusBarImplementation.h index 17cf30fb96..f8121f2ff6 100644 --- a/Modules/Core/include/mitkStatusBarImplementation.h +++ b/Modules/Core/include/mitkStatusBarImplementation.h @@ -1,60 +1,60 @@ /*============================================================================ 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 MITKSTATUSBARIMPLEMENTATION_H #define MITKSTATUSBARIMPLEMENTATION_H #include #include namespace mitk { //##Documentation - //## @brief GUI indepentent Interface for all Gui depentent implementations of a StatusBar. + //## @brief GUI independent Interface for all Gui depentent implementations of a StatusBar. class MITKCORE_EXPORT StatusBarImplementation { public: mitkClassMacroNoParent(StatusBarImplementation) //##Documentation //## @brief Constructor StatusBarImplementation(){}; //##Documentation //## @brief Destructor virtual ~StatusBarImplementation(){}; //##Documentation //## @brief Send a string to the applications StatusBar virtual void DisplayText(const char *t) = 0; //##Documentation //## @brief Send a string with a time delay to the applications StatusBar virtual void DisplayText(const char *t, int ms) = 0; virtual void DisplayErrorText(const char *t) = 0; virtual void DisplayWarningText(const char *t) = 0; virtual void DisplayWarningText(const char *t, int ms) = 0; virtual void DisplayGenericOutputText(const char *t) = 0; virtual void DisplayDebugText(const char *t) = 0; virtual void DisplayGreyValueText(const char *t) = 0; //##Documentation //## @brief removes any temporary message being shown. virtual void Clear() = 0; //##Documentation //## @brief Set the SizeGrip of the window //## (the triangle in the lower right Windowcorner for changing the size) //## to enabled or disabled virtual void SetSizeGripEnabled(bool enable) = 0; }; } // end namespace mitk #endif /* define MITKSTATUSBARIMPLEMENTATION_H */ diff --git a/Modules/Core/include/mitkSurfaceSource.h b/Modules/Core/include/mitkSurfaceSource.h index 6c43c1ba09..c903d6cbcc 100644 --- a/Modules/Core/include/mitkSurfaceSource.h +++ b/Modules/Core/include/mitkSurfaceSource.h @@ -1,69 +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 MITKSURFACEDATASOURCE_H_HEADER_INCLUDED_C10B4740 #define MITKSURFACEDATASOURCE_H_HEADER_INCLUDED_C10B4740 #include "mitkBaseDataSource.h" namespace mitk { class Surface; /** * @brief Superclass of all classes generating surfaces (instances of class * Surface) as output. * * In itk and vtk the generated result of a ProcessObject is only guaranteed * to be up-to-date, when Update() of the ProcessObject or the generated * DataObject is called immediately before access of the data stored in the * DataObject. This is also true for subclasses of mitk::BaseProcess and thus * for mitk::SurfaceSource. * @ingroup Process */ class MITKCORE_EXPORT SurfaceSource : public BaseDataSource { public: mitkClassMacro(SurfaceSource, BaseDataSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef Surface OutputType; mitkBaseDataSourceGetOutputDeclarations /** * Allocates a new output object and returns it. Currently the * index idx is not evaluated. * @param idx the index of the output for which an object should be created * @returns the new object */ itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override; /** * This is a default implementation to make sure we have something. - * Once all the subclasses of ProcessObject provide an appopriate + * Once all the subclasses of ProcessObject provide an appropriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; protected: SurfaceSource(); ~SurfaceSource() override; }; } // namespace mitk #endif /* MITKSURFACEDATASOURCE_H_HEADER_INCLUDED_C10B4740 */ diff --git a/Modules/Core/include/mitkSurfaceVtkMapper2D.h b/Modules/Core/include/mitkSurfaceVtkMapper2D.h index 9e52cbc226..eda0c82186 100644 --- a/Modules/Core/include/mitkSurfaceVtkMapper2D.h +++ b/Modules/Core/include/mitkSurfaceVtkMapper2D.h @@ -1,215 +1,215 @@ /*============================================================================ 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 mitkSurfaceVtkMapper2D_h #define mitkSurfaceVtkMapper2D_h #include "mitkBaseRenderer.h" #include "mitkLocalStorageHandler.h" #include "mitkVtkMapper.h" #include // VTK #include class vtkAssembly; class vtkCutter; class vtkPlane; class vtkLookupTable; class vtkGlyph3D; class vtkArrowSource; class vtkReverseSense; namespace mitk { class Surface; /** * @brief Vtk-based mapper for cutting 2D slices out of Surfaces. * * The mapper uses a vtkCutter filter to cut out slices (contours) of the 3D * volume and render these slices as vtkPolyData. The data is transformed * according to its geometry before cutting, to support the geometry concept * of MITK. * * Properties: * \b Surface.2D.Line Width: Thickness of the rendered lines in 2D. * \b Surface.2D.Normals.Draw Normals: enables drawing of normals as 3D arrows * in the 2D render window. The normals are created with a vtkGlyph3D from * the vtkPolyData. * \b Surface.2D.Normals.Draw Inverse Normals: same as normals, but in the * other direction. The inverse normals are computed with a vtkReverseSense * filter. * \b Surface.2D.Normals.(Inverse) Normals Color: Color of the (inverse) normals. * \b Surface.2D.Normals.(Inverse) Normals Scale Factor: Regulates the size of the normals. * * @ingroup Mapper */ class MITKCORE_EXPORT SurfaceVtkMapper2D : public VtkMapper { public: mitkClassMacro(SurfaceVtkMapper2D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); virtual const mitk::Surface *GetInput() const; /** \brief returns the prop assembly */ vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; /** \brief set the default properties for this mapper */ static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ class LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastUpdateTime; /** * @brief m_PropAssembly Contains all vtkProps for the final rendering. * * Consists of 3 actors: * The surface cut (the slice from the 3D surface). * The normals and the inverse normals. */ vtkSmartPointer m_PropAssembly; /** * @brief m_Actor actor for the surface cut. */ vtkSmartPointer m_Actor; /** * @brief m_NormalActor actor for the normals. */ vtkSmartPointer m_NormalActor; /** * @brief m_InverseNormalActor actor for the inverse normals. */ vtkSmartPointer m_InverseNormalActor; /** * @brief m_Mapper VTK mapper for all types of 2D polydata e.g. werewolves. */ vtkSmartPointer m_Mapper; /** * @brief m_Cutter Filter to cut out the 2D slice. */ vtkSmartPointer m_Cutter; /** * @brief m_CuttingPlane The plane where to cut off the 2D slice. */ vtkSmartPointer m_CuttingPlane; /** * @brief m_NormalMapper Mapper for the normals. */ vtkSmartPointer m_NormalMapper; /** * @brief m_InverseNormalMapper Mapper for the inverse normals. */ vtkSmartPointer m_InverseNormalMapper; /** * @brief m_NormalGlyph Glyph for creating normals. */ vtkSmartPointer m_NormalGlyph; /** * @brief m_InverseNormalGlyph Glyph for creating inverse normals. */ vtkSmartPointer m_InverseNormalGlyph; /** * @brief m_ArrowSource Arrow representation of the normals. */ vtkSmartPointer m_ArrowSource; /** * @brief m_ReverseSense Filter to invert the normals. */ vtkSmartPointer m_ReverseSense; /** \brief Default constructor of the local storage. */ LocalStorage(); /** \brief Default deconstructor of the local storage. */ ~LocalStorage() override; }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; /** * @brief UpdateVtkTransform Overwrite the method of the base class. * * The base class transforms the actor according to the respective * geometry which is correct for most cases. This mapper, however, * uses a vtkCutter to cut out a contour. To cut out the correct * contour, the data has to be transformed beforehand. Else the * current plane geometry will point the cutter to en empty location * (if the surface does have a geometry, which is a rather rare case). */ void UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) override {} protected: /** * @brief SurfaceVtkMapper2D default constructor. */ SurfaceVtkMapper2D(); /** * @brief ~SurfaceVtkMapper2D default destructor. */ ~SurfaceVtkMapper2D() override; /** * @brief GenerateDataForRenderer produces all the data. * @param renderer The respective renderer of the mitkRenderWindow. */ void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; /** * @brief ResetMapper Called in mitk::Mapper::Update to hide objects. * If TimeGeometry or time step is not valid, reset the mapper. - * so that nothing is displayed e.g. toggle visiblity of the propassembly. + * so that nothing is displayed e.g. toggle visibility of the propassembly. * * @param renderer The respective renderer of the mitkRenderWindow. */ void ResetMapper(BaseRenderer *renderer) override; /** * @brief Updates legacy properties to current behavior/interpretation. * @param properties The property list which should be adapted to new behaviour. * * Whenever a mapper decides to change its property types or its * interpretation of certain values, it should add something to this * method and call it before methods like ApplyProperties(); * * This is particularly helpful when dealing with data from * archive/scene files that were created before changes. */ virtual void FixupLegacyProperties(PropertyList *properties); /** * @brief ApplyAllProperties Pass all the properties to VTK. * @param renderer The respective renderer of the mitkRenderWindow. */ void ApplyAllProperties(BaseRenderer *renderer); /** * @brief Update Check if data should be generated. * @param renderer The respective renderer of the mitkRenderWindow. */ void Update(BaseRenderer *renderer) override; }; } // namespace mitk #endif /* mitkSurfaceVtkMapper2D_h */ diff --git a/Modules/Core/include/mitkTemporoSpatialStringProperty.h b/Modules/Core/include/mitkTemporoSpatialStringProperty.h index 99a145cc8d..510958cd02 100644 --- a/Modules/Core/include/mitkTemporoSpatialStringProperty.h +++ b/Modules/Core/include/mitkTemporoSpatialStringProperty.h @@ -1,136 +1,136 @@ /*============================================================================ 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 MITKTEMPOROSPATIALSTRINGPROPERTY_H_HEADER #define MITKTEMPOROSPATIALSTRINGPROPERTY_H_HEADER #include #include "mitkBaseProperty.h" #include #include "mitkTimeGeometry.h" #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Property for time and space resolved string values * @ingroup DataManagement */ class MITKCORE_EXPORT TemporoSpatialStringProperty : public BaseProperty { public: typedef ::itk::IndexValueType IndexValueType; typedef std::string ValueType; mitkClassMacro(TemporoSpatialStringProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(TemporoSpatialStringProperty, const char*); mitkNewMacro1Param(TemporoSpatialStringProperty, const std::string &); /**Returns the value of the first time point in the first slice. * If now value is set it returns an empty string.*/ ValueType GetValue() const; /**Returns the value of the passed time step and slice. If it does not exist and allowedClosed is true * it will look for the closest value. If nothing could be found an empty string will be returned.*/ ValueType GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; ValueType GetValueBySlice(const IndexValueType &zSlice, bool allowClose = false) const; ValueType GetValueByTimeStep(const TimeStepType &timeStep, bool allowClose = false) const; bool HasValue() const; bool HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; bool HasValueBySlice(const IndexValueType &zSlice, bool allowClose = false) const; bool HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose = false) const; /** return all slices stored for the specified timestep.*/ std::vector GetAvailableSlices(const TimeStepType& timeStep) const; /** return all time steps stored for the specified slice.*/ std::vector GetAvailableTimeSteps(const IndexValueType& slice) const; /** return all time steps stored in the property.*/ std::vector GetAvailableTimeSteps() const; /** return all slices stored in the property. @remark not all time steps may contain all slices.*/ std::vector GetAvailableSlices() const; void SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value); void SetValue(const ValueType &value); std::string GetValueAsString() const override; - /** Inidicates of all values (all time steps, all slices) are the same, or if at least one value stored + /** Indicates of all values (all time steps, all slices) are the same, or if at least one value stored in the property is different. If IsUniform==true one can i.a. use GetValueAsString() without the loss of information to retrieve the stored value.*/ bool IsUniform() const; using BaseProperty::operator=; protected: typedef std::map SliceMapType; typedef std::map TimeMapType; TimeMapType m_Values; TemporoSpatialStringProperty(const char *string = nullptr); TemporoSpatialStringProperty(const std::string &s); TemporoSpatialStringProperty(const TemporoSpatialStringProperty &); std::pair CheckValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; private: // purposely not implemented TemporoSpatialStringProperty &operator=(const TemporoSpatialStringProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; namespace PropertyPersistenceSerialization { /** Serialization of a TemporoSpatialStringProperty into a JSON string.*/ MITKCORE_EXPORT::std::string serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop); } namespace PropertyPersistenceDeserialization { /**Deserialize a passed JSON string into a TemporoSpatialStringProperty.*/ MITKCORE_EXPORT mitk::BaseProperty::Pointer deserializeJSONToTemporoSpatialStringProperty(const std::string &value); } #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTimeGeometry.h b/Modules/Core/include/mitkTimeGeometry.h index 48f6414e22..b1c0070ed1 100644 --- a/Modules/Core/include/mitkTimeGeometry.h +++ b/Modules/Core/include/mitkTimeGeometry.h @@ -1,349 +1,349 @@ /*============================================================================ 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 TimeGeometry_h #define TimeGeometry_h // ITK #include // MITK #include "mitkOperationActor.h" #include #include #include namespace mitk { typedef mitk::ScalarType TimePointType; typedef std::size_t TimeStepType; /** * \brief Manages the geometries of a data object for each time step * * This class is an abstract class. The concrete implementation * depends on the way the different time steps are managed. * * The time is defined either by a time step or a time point. Time steps - * are non-negativ integers starting from 0. A time point is is a ScalarType value + * are non-negative integers starting from 0. A time point is a ScalarType value * which gives the passed time since start in ms. Be aware that the starting * point is not fixed so it is possible that the same time point defines two * different time depending on the start time of the used time geometry. * * \addtogroup geometry */ class MITKCORE_EXPORT TimeGeometry : public itk::Object, public OperationActor { protected: TimeGeometry(); ~TimeGeometry() override; /** * \brief Contains a bounding box which includes all time steps */ BoundingBox::Pointer m_BoundingBox; /** * \brief Makes a deep copy of the current object */ LightObject::Pointer InternalClone() const override; public: mitkClassMacroItkParent(TimeGeometry, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ virtual TimeStepType CountTimeSteps() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for this geometry. If only one * time steps available it usually goes from -max to +max. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint() const = 0; /** * \brief Returns the last time point for which the object is valid * - * Gives the last time point for which a valid geometrie is saved in + * Gives the last time point for which a valid geometry is saved in * this time geometry. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint(TimeStepType step) const = 0; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint(TimeStepType step) const = 0; /** * \brief Get the time bounds (in ms) */ virtual TimeBounds GetTimeBounds() const = 0; /** * \brief Get the time bounds for the given TimeStep (in ms) */ virtual TimeBounds GetTimeBounds(TimeStepType step) const = 0; /** * \brief Tests if a given time point is covered by this object * * Returns true if a geometry can be returned for the given time * point and falls if not. The time point must be given in ms. */ virtual bool IsValidTimePoint(TimePointType timePoint) const = 0; /** - * \brief Test for the given time step if a geometry is availible + * \brief Test for the given time step if a geometry is available * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. - * The time step is defined as positiv number. + * The time step is defined as positive number. */ virtual bool IsValidTimeStep(TimeStepType timeStep) const = 0; /** * \brief Converts a time step to a time point * * Converts a time step to a time point in a way that * the new time point indicates the same geometry as the time step. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ virtual TimePointType TimeStepToTimePoint(TimeStepType timeStep) const = 0; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. - * If a negativ invalid time point is given always time step 0 is - * returned. If an positiv invalid time step is given an invalid + * If a negative invalid time point is given always time step 0 is + * returned. If an positive invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const = 0; /** * \brief Returns the geometry of a specific time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time point or all time points depending on the used implementation * of TimeGeometry. */ virtual BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const = 0; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time step or all time steps depending on the used implementation * of TimeGeometry. */ virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const = 0; /** * \brief Returns a clone of the geometry of a specific time point * * If an invalid time step is given (e.g. no geometry is defined for this time step) * a null-pointer will be returned. */ virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const = 0; /** * \brief Sets the geometry for a given time step * * Sets the geometry for the given time steps. This may also afflects other * time steps, depending on the implementation of TimeGeometry. */ virtual void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) = 0; /** * \brief Expands to the given number of time steps * * Expands to the given number of time steps. Each new created time * step is filled with an empty geometry. * Shrinking is not supported! */ virtual void Expand(TimeStepType size) = 0; /** * \brief Replaces the geometry instances with clones ot the passed geometry. * * Replaces the geometries of all time steps with clones of the passed - * geometry. Replacment strategy depends on the implementation of TimeGeometry + * geometry. Replacement strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ virtual void ReplaceTimeStepGeometries(const BaseGeometry *geometry) = 0; /** - * \brief Tests if all necessary informations are set and the object is valid + * \brief Tests if all necessary information are set and the object is valid */ virtual bool IsValid() const = 0; /** * \brief Get the position of the corner number \a id (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(int id) const; /** * \brief Get the position of a corner (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(bool xFront = true, bool yFront = true, bool zFront = true) const; /** * \brief Get the center of the bounding-box in mm */ Point3D GetCenterInWorld() const; /** * \brief Get the squared length of the diagonal of the bounding-box in mm */ double GetDiagonalLength2InWorld() const; /** * \brief Get the length of the diagonal of the bounding-box in mm */ double GetDiagonalLengthInWorld() const; /** * \brief Test whether the point \a p (world coordinates in mm) is inside the bounding box */ bool IsWorldPointInside(const mitk::Point3D &p) const; /** * \brief Updates the bounding box to cover the area used in all time steps * * The bounding box is updated by this method. The new bounding box * covers an area which includes all bounding boxes during * all times steps. */ void UpdateBoundingBox(); /** * \brief Returns a bounding box that covers all time steps */ BoundingBox *GetBoundingBoxInWorld() const { return m_BoundingBox; } /** * \brief Returns the world bounds of the object that cover all time steps */ BoundingBox::BoundsArrayType GetBoundsInWorld() const { return m_BoundingBox->GetBounds(); } /** * \brief Returns the Extend of the bounding in the given direction */ ScalarType GetExtentInWorld(unsigned int direction) const; /** * \brief Initializes the TimeGeometry */ virtual void Initialize(); /** * \brief Updates the geometry */ void Update(); /** * \brief Updates everything except the Bounding box * * This class should be overwritten by child classes. * The method is called when Update() is required. */ virtual void UpdateWithoutBoundingBox(){}; /** * \brief Executes the given operation on all time steps */ void ExecuteOperation(Operation *op) override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; // end class TimeGeometry /** * @brief Equal A function comparing two instances of TimeGeometry for being identical. * * @ingroup MITKTestingAPI * * The function compares two instances of TimeGeometries in all their aspects. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolerance for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::TimeGeometry &leftHandSide, const mitk::TimeGeometry &rightHandSide, ScalarType eps, bool verbose); /** * @brief Compare two instances of TimeGeometry * * @ingroup MITKTestingAPI * * The function compares two instances of TimeGeometries in all their aspects. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * * @param leftHandSide Compare this against rightHandSide. * @param rightHandSide Compare this against leftHandSide. * @param coordinateEps Tolerance for comparison of all spatial and temporal aspects (spacing, origin and grid alignment, time points). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * * @return True, if all comparisons are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::TimeGeometry& leftHandSide, const mitk::TimeGeometry& rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose); } // end namespace MITK #endif // TimeGeometry_h diff --git a/Modules/Core/include/mitkUIDGenerator.h b/Modules/Core/include/mitkUIDGenerator.h index 0b7931f039..ac261e108c 100644 --- a/Modules/Core/include/mitkUIDGenerator.h +++ b/Modules/Core/include/mitkUIDGenerator.h @@ -1,44 +1,44 @@ /*============================================================================ 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 MITK_UID_GENERATOR_INDCLUDED_FASAWE #define MITK_UID_GENERATOR_INDCLUDED_FASAWE #include #include namespace mitk { /*! \brief Generated unique IDs Creates unique IDs. The current implementation uses the UUID specification (https://www.ietf.org/rfc/rfc4122.txt) and random generator. - One may define a prefix for the UID string. But it is not needed to guarantee uniquness. It is + One may define a prefix for the UID string. But it is not needed to guarantee uniqueness. It is just a human readable addition, e.g. to see for which purpose the UID was generated. */ class MITKCORE_EXPORT UIDGenerator { public: explicit UIDGenerator(const char * prefix = ""); /** @return Returns a unique ID as string. You will get another unique ID each time you call GetUID. */ std::string GetUID(); private: std::string m_Prefix; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkVtkInteractorStyle.h b/Modules/Core/include/mitkVtkInteractorStyle.h index e9f5a494cb..4ebd6a8bde 100644 --- a/Modules/Core/include/mitkVtkInteractorStyle.h +++ b/Modules/Core/include/mitkVtkInteractorStyle.h @@ -1,57 +1,57 @@ /*============================================================================ 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. ============================================================================*/ /** * \brief Implements the handling of events that are missing for MITK interaction. * * This class inherits from vtkInteractorStyleUser, a class that handles * VTK-Events and invokes callbacks by means of an observer pattern. * * Most event-types needed for typical MITK interaction have already * been implemented in vtkInteractorStyleUser (Mouse-Buttons + Keyboard). -* However, wheel-events and widgetModifed-events (whatever these are) +* However, wheel-events and widgetModified-events (whatever these are) * have not been handled so far. This is the purpose of this class. */ #ifndef __mitkVtkInteractorStyle_h #define __mitkVtkInteractorStyle_h #include "MitkCoreExports.h" #include class MITKCORE_EXPORT mitkVtkInteractorStyle : public vtkInteractorStyleUser { public: // default VTK c'tor static mitkVtkInteractorStyle *New(); vtkTypeMacro(mitkVtkInteractorStyle, vtkInteractorStyleUser); /** * \brief Called when scrolling forwards with the mouse-wheel. */ void OnMouseWheelForward() override; /** * \brief Called when scrolling backwards with the mouse-wheel. */ void OnMouseWheelBackward() override; protected: mitkVtkInteractorStyle(); ~mitkVtkInteractorStyle() override; private: mitkVtkInteractorStyle(const mitkVtkInteractorStyle &); // Not implemented. void operator=(const mitkVtkInteractorStyle &); // Not implemented. }; #endif diff --git a/Modules/Core/include/mitkVtkLayerController.h b/Modules/Core/include/mitkVtkLayerController.h index 089491086c..b6825c8fa2 100644 --- a/Modules/Core/include/mitkVtkLayerController.h +++ b/Modules/Core/include/mitkVtkLayerController.h @@ -1,114 +1,114 @@ /*============================================================================ 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 mitkVtkLayerController_h #define mitkVtkLayerController_h #include #include #include #include class vtkRenderWindow; class vtkRenderer; namespace mitk { /** * Manages the VTK layer hierarchy * of a vtkRenderWindow. * For simple access the layers are divided into three * main groups: background, scene and foreground layers. * Renderers can be registered via the insert... functions and * removed via the RemoveRenderer function. */ class MITKCORE_EXPORT VtkLayerController { public: static VtkLayerController *GetInstance(vtkSmartPointer renWin); static void AddInstance(vtkSmartPointer renWin, vtkSmartPointer mitkSceneRenderer); static void RemoveInstance(vtkSmartPointer renWin); VtkLayerController(vtkSmartPointer renderWindow); virtual ~VtkLayerController(); /** * Returns the current vtkRenderer of the Scene */ vtkSmartPointer GetSceneRenderer(); /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the background. * With forceAbsoluteBackground set true a renderer can be placed at the absolute background of the scene. * Multiple calls with forceAbsoluteBackground set true will set the latest registered renderer as background. */ void InsertBackgroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteBackground); /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the foreground. * With forceAbsoluteBackground set true a renderer can be placed at the absolute foreground of the scene. * Multiple calls with forceAbsoluteForeground set true will set the latest registered renderer as foreground. */ void InsertForegroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteForeground); /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered between background renderers and * foreground renderers. */ void InsertSceneRenderer(vtkSmartPointer renderer); /** * Connects a VtkRenderWindow with the layer controller. */ void SetRenderWindow(vtkSmartPointer renwin); /** * A renderer which has been inserted via a insert... function can be removed from the vtkRenderWindow with * RemoveRenderer. */ void RemoveRenderer(vtkSmartPointer renderer); /** * Returns true if a renderer has been inserted */ bool IsRendererInserted(vtkSmartPointer renderer); /** * Returns the number of renderers in the renderwindow. */ unsigned int GetNumberOfRenderers(); void SetEraseForAllRenderers(int i); protected: vtkSmartPointer m_RenderWindow; private: /** * Internally used to sort all registered renderers and to connect the with the vtkRenderWindow. * Mention that VTK Version 5 and above is rendering higher numbers in the background and VTK - * Verison < 5 in the foreground. + * Version < 5 in the foreground. */ void UpdateLayers(); // Layer Management typedef std::vector> RendererVectorType; RendererVectorType m_BackgroundRenderers; RendererVectorType m_SceneRenderers; RendererVectorType m_ForegroundRenderers; typedef std::map, mitk::VtkLayerController *> vtkLayerControllerMapType; static vtkLayerControllerMapType s_LayerControllerMap; }; } // Namespace MITK #endif /* mitkVtkLayerController_h */ diff --git a/Modules/Core/include/mitkVtkMapper.h b/Modules/Core/include/mitkVtkMapper.h index e42a8df801..e6c6dbd967 100644 --- a/Modules/Core/include/mitkVtkMapper.h +++ b/Modules/Core/include/mitkVtkMapper.h @@ -1,151 +1,151 @@ /*============================================================================ 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. ============================================================================*/ // change number #ifndef VTKMAPPER_H_HEADER_INCLUDED_C1C5453B #define VTKMAPPER_H_HEADER_INCLUDED_C1C5453B #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkLocalStorageHandler.h" #include "mitkMapper.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include class vtkProp; class vtkProp3D; class vtkActor; namespace mitk { /** \brief Base class of all Vtk Mappers in order to display primitives * by exploiting Vtk functionality. * * Rendering of opaque, translucent or volumetric geometry and overlays * is done in consecutive render passes. * * \ingroup Mapper */ class MITKCORE_EXPORT VtkMapper : public Mapper { public: mitkClassMacro(VtkMapper, Mapper); virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) = 0; /** * \brief Returns whether this is an vtk-based mapper * \deprecatedSince{2013_03} All mappers of superclass VTKMapper are vtk based, use a dynamic_cast instead */ DEPRECATED(virtual bool IsVtkBased() const override); /** \brief Determines which geometry should be rendered * (opaque, translucent, volumetric, overlay) * and calls the appropriate function. * * Called by mitk::VtkPropRenderer::Render */ void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override; /** \brief Checks visibility and renders the overlay */ virtual void MitkRenderOverlay(BaseRenderer *renderer); /** \brief Checks visibility and renders untransparent geometry */ virtual void MitkRenderOpaqueGeometry(BaseRenderer *renderer); - /** \brief Checks visiblity and renders transparent geometry */ + /** \brief Checks visibility and renders transparent geometry */ virtual void MitkRenderTranslucentGeometry(BaseRenderer *renderer); /** \brief Checks visibility and renders volumes */ virtual void MitkRenderVolumetricGeometry(BaseRenderer *renderer); /** \brief Returns true if this mapper owns the specified vtkProp for * the given BaseRenderer. * * Note: returns false by default; should be implemented for VTK-based * Mapper subclasses. */ virtual bool HasVtkProp(const vtkProp *prop, BaseRenderer *renderer); /** \brief Set the vtkTransform of the m_Prop3D for * the current time step of \a renderer * * Called by mitk::VtkPropRenderer::Update before rendering. This * method will transform all actors (e.g. of an vtkAssembly) according * the geometry. * * \warning This method transforms only props which derive * from vtkProp3D. Make sure to use vtkAssembly, if you have * multiple props. vtkPropAssembly does not work, since it derives * from vtkProp. */ virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** * \brief Apply color and opacity properties read from the PropertyList * \deprecatedSince{2013_03} Use ApplyColorAndOpacityProperties(mitk::BaseRenderer* renderer, vtkActor * actor) * instead */ DEPRECATED(inline virtual void ApplyProperties(vtkActor *actor, mitk::BaseRenderer *renderer)) { ApplyColorAndOpacityProperties(renderer, actor); } /** * \deprecatedSince{2018_04} */ DEPRECATED(void ApplyShaderProperties(mitk::BaseRenderer *)){} /** * \brief Apply color and opacity properties read from the PropertyList. * Called by mapper subclasses. */ void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor *actor) override; /** * \brief Release vtk-based graphics resources that are being consumed by this mapper. * * Method called by mitk::VtkPropRenderer. The parameter renderer could be used to * determine which graphic resources to release. The local storage is accessible * by the parameter renderer. Should be overwritten in subclasses. */ virtual void ReleaseGraphicsResources(mitk::BaseRenderer * /*renderer*/) {} class LocalStorage : public mitk::Mapper::BaseLocalStorage { }; protected: /** constructor */ VtkMapper(); /** virtual destructor in order to derive from this class */ ~VtkMapper() override; private: /** copy constructor */ VtkMapper(const VtkMapper &); /** assignment operator */ VtkMapper &operator=(const VtkMapper &); }; } // namespace mitk #endif /* VTKMAPPER_H_HEADER_INCLUDED_C1C5453B */ diff --git a/Modules/Core/include/mitkXML2EventParser.h b/Modules/Core/include/mitkXML2EventParser.h index c86dec4fa4..c95356fc66 100755 --- a/Modules/Core/include/mitkXML2EventParser.h +++ b/Modules/Core/include/mitkXML2EventParser.h @@ -1,91 +1,91 @@ /*============================================================================ 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 mitkXML2EventParser_h #define mitkXML2EventParser_h #include #include "mitkPropertyList.h" #include #include "mitkInteractionEvent.h" namespace us { class Module; } namespace mitk { /** * \class InteractionEventList * \brief Generates a list of InteractionEvents based on an XML file- * * @sa EventRecorder * @ingroup Interaction **/ class MITKCORE_EXPORT XML2EventParser : public vtkXMLParser { public: /** * @brief Construct an InteractionEventList object based on a XML configuration file. * * Uses the specified resource file containing an XML event configuration to * construct an EventConfig object. If the resource is invalid, the created * EventConfig object will also be invalid. * * @param filename The resource name relative to the Interactions resource folder. * @param module */ XML2EventParser(const std::string &filename, const us::Module *module = nullptr); /** * @brief Construct an InteractionEventList object based on a XML configuration file. * - * Uses the specified istream refering to a file containing an XML event configuration to + * Uses the specified istream referring to a file containing an XML event configuration to * construct an EventConfig object. If the resource is invalid, the created * EventConfig object will also be invalid. * * @param inputStream std::ifstream to XML configuration file */ XML2EventParser(std::istream &inputStream); typedef std::vector EventContainerType; EventContainerType GetInteractions() { return m_InteractionList; } ~XML2EventParser() override{}; protected: /** * @brief Derived from XMLReader **/ void StartElement(const char *elementName, const char **atts) override; /** * @brief Derived from XMLReader **/ void EndElement(const char *elementName) override; std::string ReadXMLStringAttribute(const std::string &name, const char **atts); bool ReadXMLBooleanAttribute(const std::string &name, const char **atts); private: PropertyList::Pointer m_EventPropertyList; EventContainerType m_InteractionList; }; } // namespace mitk #endif /* mitkStateMachineConfig_h */ diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp index 6daf8f416d..58e925235c 100644 --- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp @@ -1,504 +1,504 @@ /*============================================================================ 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 "mitkExtractSliceFilter.h" #include #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice *reslicer): m_XMin(0), m_XMax(0), m_YMin(0), m_YMax(0) { if (reslicer == nullptr) { m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = nullptr; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; m_BackgroundLevel = -32768.0; m_Component = 0; } mitk::ExtractSliceFilter::~ExtractSliceFilter() { m_ResliceTransform = nullptr; m_WorldGeometry = nullptr; delete[] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation() { Image::ConstPointer input = this->GetInput(); if (input.IsNull()) { return; } if (nullptr == m_WorldGeometry) { return; } Vector3D right, bottom; double widthInMM, heightInMM; Vector2D extent; // set the geometry from current worldgeometry for the resultimage // this is needed that the image has the correct mitk geometry // the sliceGeometry is the Geometry of the result slice PlaneGeometry::Pointer sliceGeometry = m_WorldGeometry->Clone(); sliceGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); // the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = sliceGeometry->GetOrigin(); auto abstractGeometry = dynamic_cast(m_WorldGeometry.GetPointer()); if (abstractGeometry != nullptr) { extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); right = abstractGeometry->GetPlane()->GetAxisVector(0); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); } else { // if the worldGeomatry is a PlaneGeometry everything is straight forward right = m_WorldGeometry->GetAxisVector(0); bottom = m_WorldGeometry->GetAxisVector(1); if (m_InPlaneResampleExtentByGeometry) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent(0); extent[1] = m_WorldGeometry->GetExtent(1); } else { const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() <= 0)) { itkWarningMacro(<< "Error reading input image TimeGeometry."); return; } // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. Vector3D rightInIndex, bottomInIndex; inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(right, rightInIndex); inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(bottom, bottomInIndex); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM(0); heightInMM = m_WorldGeometry->GetExtentInMM(1); } right.Normalize(); bottom.Normalize(); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; xMin = yMin = 0; xMax = static_cast(extent[0]); yMax = static_cast(extent[1]); if (m_WorldGeometry->GetReferenceGeometry()) { double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } if (this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), m_WorldGeometry, sliceBounds)) { // Calculate output extent (integer values) xMin = static_cast(sliceBounds[0] / m_OutPutSpacing[0] + 0.5); xMax = static_cast(sliceBounds[1] / m_OutPutSpacing[0] + 0.5); yMin = static_cast(sliceBounds[2] / m_OutPutSpacing[1] + 0.5); yMax = static_cast(sliceBounds[3] / m_OutPutSpacing[1] + 0.5); } // ELSE we use the default values } sliceOrigin += right * (m_OutPutSpacing[0] * 0.5); sliceOrigin += bottom * (m_OutPutSpacing[1] * 0.5); // a worldGeometry is no imageGeometry, thus it is manually set to true sliceGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transferring world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = sliceGeometry->GetAxisVector(0); Vector3D axis1 = sliceGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); // adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); sliceGeometry->SetOrigin(sliceOrigin); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calculated slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; boundsCopy[1] = std::max(xMax - xMin, 1); boundsCopy[3] = std::max(yMax - yMin, 1); sliceGeometry->SetBounds(boundsCopy); sliceGeometry->Modified(); Image::Pointer output = this->GetOutput(); output->Initialize(input->GetPixelType(), 1, *sliceGeometry); m_XMin = xMin; m_XMax = xMax; m_YMin = yMin; m_YMax = yMax; m_Right = right; m_Bottom = bottom; } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion() { // As we want all pixel information fo the image in our plane, the requested region // is set to the largest possible region in the image. // This is needed because an oblique plane has a larger extent then the image // and the in pipeline it is checked via PropagateResquestedRegion(). But the // extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = this->GetInput(); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType *mitk::ExtractSliceFilter::GetOutputSpacing() { return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData() { mitk::Image *input = this->GetInput(); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if (!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeGeometry *inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() <= 0)) { itkWarningMacro(<< "Error reading input image TimeGeometry."); return; } // is it a valid timeStep? if (inputTimeGeometry->IsValidTimeStep(m_TimeStep) == false) { itkWarningMacro(<< "This is not a valid timestep: " << m_TimeStep); return; } // check if there is something to display. if (!input->IsVolumeSet(m_TimeStep)) { itkWarningMacro(<< "No volume data existent at given timestep " << m_TimeStep); return; } /*================#BEGIN setup vtkImageReslice properties================*/ Point3D origin; Vector3D normal; const auto *planeGeometry = dynamic_cast(m_WorldGeometry.GetPointer()); // Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) const auto *abstractGeometry = dynamic_cast(m_WorldGeometry.GetPointer()); if (abstractGeometry != nullptr) { m_ResliceTransform = abstractGeometry; origin = abstractGeometry->GetPlane()->GetOrigin(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkSmartPointer::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->GetVtkTransform()->GetLinearInverse()); composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform()); m_Reslicer->SetResliceTransform(composedResliceTransform); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see PlaneGeometryDataVtkMapper3D) // Note: Backgroundlevel was hardcoded before to -1023 m_Reslicer->SetBackgroundLevel(m_BackgroundLevel); } else { if (planeGeometry != nullptr) { // if the worldGeomatry is a PlaneGeometry everything is straight forward origin = planeGeometry->GetOrigin(); normal = planeGeometry->GetNormal(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) - * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry + * and the worldGeometry surrounding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += m_Right * (m_OutPutSpacing[0] * 0.5); origin += m_Bottom * (m_OutPutSpacing[1] * 0.5); - // set the tranform for reslicing. + // set the transform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if (m_ResliceTransform.IsNotNull()) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D), // else the background of the image turns out gray // Note: Backgroundlevel was hardcoded to -32768 m_Reslicer->SetBackgroundLevel(m_BackgroundLevel); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if (m_ResliceTransform.IsNotNull()) { // if the resliceTransform is set the reslice axis are recalculated. // Thus the geometry information is not fitting. Therefor a unitSpacingFilter // is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New(); unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); unitSpacingImageFilter->SetInputData(input->GetVtkImageData(m_TimeStep)); m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort()); } else { // if no transform is set the image can be used directly m_Reslicer->SetInputData(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ // ResliceAxesOrigin is the anchor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); // the cosines define the plane: x and y are the direction vectors, n is the planes normal // this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(m_Right.GetVnlVector(), cosines); // x vnl2vtk(m_Bottom.GetVnlVector(), cosines + 3); // y vnl2vtk(normal.GetVnlVector(), cosines + 6); // n m_Reslicer->SetResliceAxesDirectionCosines(cosines); // we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); // set the interpolation mode for slicing switch (this->m_InterpolationMode) { case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); break; default: // the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ // Set the output extents! First included pixel index and last included pixel index // xMax and yMax are one after the last pixel. so they have to be decremented by 1. // In case we have a 2D image, xMax or yMax might be 0. in this case, do not decrement, but take 0. m_Reslicer->SetOutputExtent(m_XMin, std::max(0, m_XMax - 1), m_YMin, std::max(0, m_YMax - 1), m_ZMin, m_ZMax); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin(0.0, 0.0, 0.0); m_Reslicer->SetOutputSpacing(m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing); // TODO check the following lines, they are responsible whether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); // this produces a bad allocation error for 2D images // m_Reslicer->GetOutput()->UpdateInformation(); // m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); // start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageReslice properties================*/ if (m_VtkOutputRequested) { // no conversion to mitk // no mitk geometry will be set, as the output is vtkImageData only!!! // no image component will be extracted, as the caller might need the whole multi-component image as vtk output return; } else { auto reslicedImage = vtkSmartPointer::New(); reslicedImage = m_Reslicer->GetOutput(); if (nullptr == reslicedImage) { itkWarningMacro(<< "Reslicer returned empty image"); return; } /*================ #BEGIN Extract component from image slice ================*/ int numberOfScalarComponent = reslicedImage->GetNumberOfScalarComponents(); if (numberOfScalarComponent > 1 && static_cast(numberOfScalarComponent) >= m_Component) { // image has more than one component, extract the correct component information with the given 'component' parameter auto vectorComponentExtractor = vtkSmartPointer::New(); vectorComponentExtractor->SetInputData(reslicedImage); vectorComponentExtractor->SetComponents(m_Component); vectorComponentExtractor->Update(); reslicedImage = vectorComponentExtractor->GetOutput(); } /*================ #END Extract component from image slice ================*/ /*================ #BEGIN Convert the slice to an mitk::Image ================*/ mitk::Image::Pointer resultImage = GetOutput(); /*Temporary store the geometry that is already correct (set in GeneratOutputInformation()) but will be reset due to initialize.*/ mitk::BaseGeometry::Pointer resultGeometry = resultImage->GetGeometry(); // initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice if (reslicedImage->GetDataDimension() == 1) { // If original image was 2D, the slice might have an y extent of 0. // Still i want to ensure here that Image is 2D resultImage->Initialize(reslicedImage, 1, -1, -1, 1); } else { resultImage->Initialize(reslicedImage); } // transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); /*================ #END Convert the slice to an mitk::Image ================*/ resultImage->SetGeometry(resultGeometry); } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(double bounds[6]) { if (!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), m_WorldGeometry, bounds); } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds) { bool b = mitk::PlaneClipping::CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } diff --git a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp index 6802fb3c62..358aff4e36 100644 --- a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp +++ b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp @@ -1,122 +1,122 @@ /*============================================================================ 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. ============================================================================*/ #if (_MSC_VER == 1200) #include #endif #include "mitkHistogramGenerator.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" // // The new ITK Statistics framework has // a class with the same functionality as // MITKScalarImageToHistogramGenerator.h, but // no longer has the classis the MITK class depends on. #if !defined(ITK_USE_REVIEW_STATISTICS) #include "itkMITKScalarImageToHistogramGenerator.h" #else #include "itkScalarImageToHistogramGenerator.h" #endif mitk::HistogramGenerator::HistogramGenerator() : m_Image(nullptr), m_Size(256), m_Histogram(nullptr) { } mitk::HistogramGenerator::~HistogramGenerator() { } template void InternalCompute(itk::Image *itkImage, const mitk::HistogramGenerator *mitkHistoGenerator, mitk::HistogramGenerator::HistogramType::ConstPointer &histogram) { #if !defined(ITK_USE_REVIEW_STATISTICS) typedef itk::Statistics::MITKScalarImageToHistogramGenerator, double> HistogramGeneratorType; #else typedef itk::Statistics::ScalarImageToHistogramGenerator> HistogramGeneratorType; #endif typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New(); histogramGenerator->SetInput(itkImage); histogramGenerator->SetNumberOfBins(mitkHistoGenerator->GetSize()); // histogramGenerator->SetMarginalScale( 10.0 ); histogramGenerator->Compute(); histogram = histogramGenerator->GetOutput(); } void mitk::HistogramGenerator::ComputeHistogram() { if ((m_Histogram.IsNull()) || (m_Histogram->GetMTime() < m_Image->GetMTime())) { const_cast(m_Image.GetPointer())->SetRequestedRegionToLargestPossibleRegion(); //@todo without this, // Image::GetScalarMin // does not work for // dim==3 (including // sliceselector!) const_cast(m_Image.GetPointer())->Update(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(0); timeSelector->UpdateLargestPossibleRegion(); AccessByItk_n(timeSelector->GetOutput(), InternalCompute, (this, m_Histogram)); } // debug code /* - MITK_INFO << "Histogram modfied 1" << m_Histogram->GetMTime() << std::endl; + MITK_INFO << "Histogram modified 1" << m_Histogram->GetMTime() << std::endl; m_Histogram->Modified(); - MITK_INFO << "Histogram modfied 2" << m_Histogram->GetMTime() << std::endl; - MITK_INFO << "Image modfied" << m_Image->GetMTime() << std::endl; + MITK_INFO << "Histogram modified 2" << m_Histogram->GetMTime() << std::endl; + MITK_INFO << "Image modified" << m_Image->GetMTime() << std::endl; const unsigned int histogramSize = m_Histogram->Size(); MITK_INFO << "Histogram size " << histogramSize << std::endl; HistogramType::ConstIterator itr = GetHistogram()->Begin(); HistogramType::ConstIterator end = GetHistogram()->End(); int bin = 0; while( itr != end ) { MITK_INFO << "bin = " << GetHistogram()->GetBinMin(0,bin) << "--" << GetHistogram()->GetBinMax(0,bin) << " frequency = "; MITK_INFO << itr.GetFrequency() << std::endl; ++itr; ++bin; } */ } float mitk::HistogramGenerator::GetMaximumFrequency() const { return CalculateMaximumFrequency(this->m_Histogram); } float mitk::HistogramGenerator::CalculateMaximumFrequency(const HistogramType *histogram) { HistogramType::ConstIterator itr = histogram->Begin(); HistogramType::ConstIterator end = histogram->End(); float maxFreq = 0; while (itr != end) { maxFreq = std::max(maxFreq, static_cast(itr.GetFrequency())); ++itr; } return maxFreq; } diff --git a/Modules/Core/src/Algorithms/mitkImageSource.cpp b/Modules/Core/src/Algorithms/mitkImageSource.cpp index 7a4dc03182..19b1f22568 100644 --- a/Modules/Core/src/Algorithms/mitkImageSource.cpp +++ b/Modules/Core/src/Algorithms/mitkImageSource.cpp @@ -1,201 +1,201 @@ /*============================================================================ 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 "mitkImageSource.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" #include mitk::ImageSource::ImageSource() { // Create the output. We use static_cast<> here because we know the default // output must be of type TOutputImage OutputImageType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output.GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(DataObjectPointerArraySizeType /*idx*/) { return static_cast(mitk::Image::New().GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(const DataObjectIdentifierType &name) { itkDebugMacro("MakeOutput(" << name << ")"); if (this->IsIndexedOutputName(name)) { return this->MakeOutput(this->MakeIndexFromOutputName(name)); } return static_cast(mitk::Image::New().GetPointer()); } //---------------------------------------------------------------------------- unsigned int mitk::ImageSource::SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion) { // Get the output pointer OutputImageType *outputPtr = this->GetOutput(); const SlicedData::SizeType &requestedRegionSize = outputPtr->GetRequestedRegion().GetSize(); int splitAxis; SlicedData::IndexType splitIndex; SlicedData::SizeType splitSize; // Initialize the splitRegion to the output requested region splitRegion = outputPtr->GetRequestedRegion(); splitIndex = splitRegion.GetIndex(); splitSize = splitRegion.GetSize(); // split on the outermost dimension available splitAxis = outputPtr->GetDimension() - 1; while (requestedRegionSize[splitAxis] == 1) { --splitAxis; if (splitAxis < 0) { // cannot split itkDebugMacro(" Cannot Split"); return 1; } } // determine the actual number of pieces that will be generated SlicedData::SizeType::SizeValueType range = requestedRegionSize[splitAxis]; auto valuesPerThread = itk::Math::Ceil(range / (double)num); unsigned int maxThreadIdUsed = itk::Math::Ceil(range / (double)valuesPerThread) - 1; // Split the region if (i < maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; splitSize[splitAxis] = valuesPerThread; } if (i == maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; // last thread needs to process the "rest" dimension being split splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread; } // set the split region ivars splitRegion.SetIndex(splitIndex); splitRegion.SetSize(splitSize); itkDebugMacro(" Split Piece: " << splitRegion); return maxThreadIdUsed + 1; } //---------------------------------------------------------------------------- void mitk::ImageSource::AllocateOutputs() { OutputImagePointer outputPtr; // Allocate the output memory for (unsigned int i = 0; i < this->GetNumberOfOutputs(); i++) { outputPtr = this->GetOutput(i); // outputPtr->SetBufferedRegion(outputPtr->GetRequestedRegion()); @FIXME??? // outputPtr->Allocate(); @FIXME??? } } //---------------------------------------------------------------------------- void mitk::ImageSource::GenerateData() { - // Call a method that can be overriden by a subclass to allocate + // Call a method that can be overridden by a subclass to allocate // memory for the filter's outputs this->AllocateOutputs(); // Call a method that can be overridden by a subclass to perform // some calculations prior to splitting the main computations into // separate threads this->BeforeThreadedGenerateData(); // Set up the multithreaded processing ThreadStruct str; str.Filter = this; this->GetMultiThreader()->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str); // multithread the execution this->GetMultiThreader()->SingleMethodExecute(); // Call a method that can be overridden by a subclass to perform // some calculations after all the threads have completed this->AfterThreadedGenerateData(); } //---------------------------------------------------------------------------- // The execute method created by the subclass. void mitk::ImageSource::ThreadedGenerateData(const OutputImageRegionType &, itk::ThreadIdType) { itkExceptionMacro("subclass should override this method!!!"); } // Callback routine used by the threading library. This routine just calls // the ThreadedGenerateData method after setting the correct region for this // thread. itk::ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg) { ThreadStruct *str; itk::ThreadIdType total, threadId, threadCount; threadId = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->WorkUnitID; threadCount = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->NumberOfWorkUnits; str = (ThreadStruct *)(((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->UserData); // execute the actual method with appropriate output region // first find out how many pieces extent can be split into. SlicedData::RegionType splitRegion; total = str->Filter->SplitRequestedRegion(threadId, threadCount, splitRegion); if (threadId < total) { str->Filter->ThreadedGenerateData(splitRegion, threadId); } // else // { // otherwise don't use this thread. Sometimes the threads dont // break up very well and it is just as efficient to leave a // few threads idle. // } return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; } void mitk::ImageSource::PrepareOutputs() { Superclass::PrepareOutputs(); } vtkImageData *mitk::ImageSource::GetVtkImageData() { Update(); return GetOutput()->GetVtkImageData(); } const vtkImageData *mitk::ImageSource::GetVtkImageData() const { return GetOutput()->GetVtkImageData(); } mitkBaseDataSourceGetOutputDefinitions(mitk::ImageSource) diff --git a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp index 7f69c6ee80..65ec8c4623 100644 --- a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp +++ b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp @@ -1,247 +1,247 @@ /*============================================================================ 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 "mitkLimitedLinearUndo.h" #include namespace mitk { itkEventMacroDefinition(UndoStackEvent, itk::ModifiedEvent); itkEventMacroDefinition(UndoEmptyEvent, UndoStackEvent); itkEventMacroDefinition(RedoEmptyEvent, UndoStackEvent); itkEventMacroDefinition(UndoNotEmptyEvent, UndoStackEvent); itkEventMacroDefinition(RedoNotEmptyEvent, UndoStackEvent); itkEventMacroDefinition(UndoFullEvent, UndoStackEvent); itkEventMacroDefinition(RedoFullEvent, UndoStackEvent); } mitk::LimitedLinearUndo::LimitedLinearUndo() : m_UndoLimit(0) { // nothing to do } mitk::LimitedLinearUndo::~LimitedLinearUndo() { // delete undo and redo list this->ClearList(&m_UndoList); this->ClearList(&m_RedoList); } void mitk::LimitedLinearUndo::ClearList(UndoContainer *list) { while (!list->empty()) { UndoStackItem *item = list->back(); list->pop_back(); delete item; } } bool mitk::LimitedLinearUndo::SetOperationEvent(UndoStackItem *stackItem) { auto *operationEvent = dynamic_cast(stackItem); if (!operationEvent) return false; // clear the redolist, if a new operation is saved if (!m_RedoList.empty()) { this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } if (0 != m_UndoLimit && m_UndoList.size() == m_UndoLimit) { auto item = m_UndoList.front(); m_UndoList.pop_front(); delete item; } m_UndoList.push_back(operationEvent); InvokeEvent(UndoNotEmptyEvent()); return true; } bool mitk::LimitedLinearUndo::Undo(bool fine) { if (fine) { // undo one object event ID return Undo(); } else { // undo one group event ID int oeid = FirstObjectEventIdOfCurrentGroup( - m_UndoList); // get the Object Event ID of the first item with a differnt Group ID (as seen from the end of stack) + m_UndoList); // get the Object Event ID of the first item with a different Group ID (as seen from the end of stack) return Undo(oeid); } } bool mitk::LimitedLinearUndo::Undo() { if (m_UndoList.empty()) return false; int undoObjectEventId = m_UndoList.back()->GetObjectEventId(); return Undo(undoObjectEventId); } bool mitk::LimitedLinearUndo::Undo(int oeid) { if (m_UndoList.empty()) return false; bool rc = true; do { m_UndoList.back()->ReverseAndExecute(); m_RedoList.push_back(m_UndoList.back()); // move to redo stack m_UndoList.pop_back(); InvokeEvent(RedoNotEmptyEvent()); if (m_UndoList.empty()) { InvokeEvent(UndoEmptyEvent()); rc = false; break; } } while (m_UndoList.back()->GetObjectEventId() >= oeid); // Update. Check Rendering Mechanism where to request updates mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return rc; } bool mitk::LimitedLinearUndo::Redo(bool) { return Redo(); } bool mitk::LimitedLinearUndo::Redo() { if (m_RedoList.empty()) return false; int redoObjectEventId = m_RedoList.back()->GetObjectEventId(); return Redo(redoObjectEventId); } bool mitk::LimitedLinearUndo::Redo(int oeid) { if (m_RedoList.empty()) return false; do { m_RedoList.back()->ReverseAndExecute(); m_UndoList.push_back(m_RedoList.back()); m_RedoList.pop_back(); InvokeEvent(UndoNotEmptyEvent()); if (m_RedoList.empty()) { InvokeEvent(RedoEmptyEvent()); break; } } while (m_RedoList.back()->GetObjectEventId() <= oeid); // Update. This should belong into the ExecuteOperation() of OperationActors, but it seems not to be used everywhere mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void mitk::LimitedLinearUndo::Clear() { this->ClearList(&m_UndoList); InvokeEvent(UndoEmptyEvent()); this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } void mitk::LimitedLinearUndo::ClearRedoList() { this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } bool mitk::LimitedLinearUndo::RedoListEmpty() { return m_RedoList.empty(); } std::size_t mitk::LimitedLinearUndo::GetUndoLimit() const { return m_UndoLimit; } void mitk::LimitedLinearUndo::SetUndoLimit(std::size_t undoLimit) { if (undoLimit != m_UndoLimit) { if (m_UndoList.size() > undoLimit) { m_UndoList.erase(m_UndoList.begin(), m_UndoList.end() - undoLimit); } m_UndoLimit = undoLimit; } } int mitk::LimitedLinearUndo::GetLastObjectEventIdInList() { return m_UndoList.back()->GetObjectEventId(); } int mitk::LimitedLinearUndo::GetLastGroupEventIdInList() { return m_UndoList.back()->GetGroupEventId(); } mitk::OperationEvent *mitk::LimitedLinearUndo::GetLastOfType(OperationActor *destination, OperationType opType) { // When/where is this function needed? In CoordinateSupplier... for (auto iter = m_UndoList.rbegin(); iter != m_UndoList.rend(); ++iter) { auto *opEvent = dynamic_cast(*iter); if (!opEvent) continue; if (opEvent->GetOperation() != nullptr && opEvent->GetOperation()->GetOperationType() == opType && opEvent->IsValid() && opEvent->GetDestination() == destination) return opEvent; } return nullptr; } int mitk::LimitedLinearUndo::FirstObjectEventIdOfCurrentGroup(mitk::LimitedLinearUndo::UndoContainer &stack) { int currentGroupEventId = stack.back()->GetGroupEventId(); int firstObjectEventId = -1; for (auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { if ((*iter)->GetGroupEventId() == currentGroupEventId) { firstObjectEventId = (*iter)->GetObjectEventId(); } else break; } return firstObjectEventId; } diff --git a/Modules/Core/src/Controllers/mitkProgressBar.cpp b/Modules/Core/src/Controllers/mitkProgressBar.cpp index 6d68ab3425..e6cf66ecaf 100644 --- a/Modules/Core/src/Controllers/mitkProgressBar.cpp +++ b/Modules/Core/src/Controllers/mitkProgressBar.cpp @@ -1,138 +1,138 @@ /*============================================================================ 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 "mitkProgressBar.h" #include "mitkCallbackFromGUIThread.h" #include "mitkProgressBarImplementation.h" #include #include #include #include namespace mitk { ProgressBar *ProgressBar::m_Instance = nullptr; /** * Sets the current amount of progress to current progress + steps. * @param steps the number of steps done since last Progress(int steps) call. */ void ProgressBar::Progress(unsigned int steps) { if (!m_Implementations.empty()) { ProgressBarImplementationsListIterator iter; for (iter = m_Implementations.begin(); iter != m_Implementations.end(); iter++) { // update progress for all ProgressBarImplementations if ((*iter) != nullptr) { (*iter)->Progress(steps); } } } } /** - * Explicitely reset progress bar. + * Explicitly reset progress bar. */ void ProgressBar::Reset() { if (!m_Implementations.empty()) { ProgressBarImplementationsListIterator iter; for (iter = m_Implementations.begin(); iter != m_Implementations.end(); iter++) { // set steps to do for all ProgressBarImplementations if ((*iter) != nullptr) { (*iter)->Reset(); } } } } /** * Adds steps to totalSteps. */ void ProgressBar::AddStepsToDo(unsigned int steps) { if (!m_Implementations.empty()) { ProgressBarImplementationsListIterator iter; for (iter = m_Implementations.begin(); iter != m_Implementations.end(); iter++) { // set steps to do for all ProgressBarImplementations if ((*iter) != nullptr) { (*iter)->AddStepsToDo(steps); } } } } /** * Sets whether the current progress value is displayed. */ void ProgressBar::SetPercentageVisible(bool visible) { if (!m_Implementations.empty()) { ProgressBarImplementationsListIterator iter; for (iter = m_Implementations.begin(); iter != m_Implementations.end(); iter++) { // set percentage visible for all ProgressBarImplementations if ((*iter) != nullptr) { (*iter)->SetPercentageVisible(visible); } } } } /** * Get the instance of this ProgressBar */ ProgressBar *ProgressBar::GetInstance() { if (m_Instance == nullptr) { m_Instance = new ProgressBar(); } return m_Instance; } /** * Set an instance of this; application must do this!See Header! */ void ProgressBar::RegisterImplementationInstance(ProgressBarImplementation *implementation) { if (std::find(m_Implementations.begin(), m_Implementations.end(), implementation) == m_Implementations.end()) { m_Implementations.push_back(implementation); } } void ProgressBar::UnregisterImplementationInstance(ProgressBarImplementation *implementation) { auto iter = std::find(m_Implementations.begin(), m_Implementations.end(), implementation); if (iter != m_Implementations.end()) { m_Implementations.erase(iter); } } ProgressBar::ProgressBar() {} ProgressBar::~ProgressBar() {} } // end namespace mitk diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index db64173826..8b6071707e 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,770 +1,770 @@ /*============================================================================ 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 inadvertantly create + // 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 inadvertantly create + // 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); } 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(); // Re-initialize view direction 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); 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/mitkStatusBar.cpp b/Modules/Core/src/Controllers/mitkStatusBar.cpp index 4649a03464..adba4af8cd 100755 --- a/Modules/Core/src/Controllers/mitkStatusBar.cpp +++ b/Modules/Core/src/Controllers/mitkStatusBar.cpp @@ -1,164 +1,164 @@ /*============================================================================ 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 "mitkStatusBar.h" #include #include namespace mitk { StatusBarImplementation *StatusBar::m_Implementation = nullptr; StatusBar *StatusBar::m_Instance = nullptr; /** - * Display the text in the statusbar of the applikation + * Display the text in the statusbar of the application */ void StatusBar::DisplayText(const char *t) { if (m_Implementation != nullptr) m_Implementation->DisplayText(t); } /** - * Display the text in the statusbar of the applikation for ms seconds + * Display the text in the statusbar of the application for ms seconds */ void StatusBar::DisplayText(const char *t, int ms) { if (m_Implementation != nullptr) m_Implementation->DisplayText(t, ms); } void StatusBar::DisplayErrorText(const char *t) { if (m_Implementation != nullptr) m_Implementation->DisplayErrorText(t); } void StatusBar::DisplayWarningText(const char *t) { if (m_Implementation != nullptr) m_Implementation->DisplayWarningText(t); } void StatusBar::DisplayWarningText(const char *t, int ms) { if (m_Implementation != nullptr) m_Implementation->DisplayWarningText(t, ms); } void StatusBar::DisplayGenericOutputText(const char *t) { if (m_Implementation != nullptr) m_Implementation->DisplayGenericOutputText(t); } void StatusBar::DisplayDebugText(const char *t) { if (m_Implementation != nullptr) m_Implementation->DisplayDebugText(t); } void StatusBar::DisplayGreyValueText(const char *t) { if (m_Implementation != nullptr) m_Implementation->DisplayGreyValueText(t); } static void WriteCommonImageInfo( std::ostringstream &stream, Point3D point, itk::Index<3> index, ScalarType time) { stream << "Position: <" << std::fixed << point[0] << ", " << std::fixed << point[1] << ", " << std::fixed << point[2] << "> mm; "; stream << "Index: <" << index[0] << ", " << index[1] << ", " << index[2] << "> ; "; stream << "Time: " << time << " ms"; } void StatusBar::DisplayImageInfo(Point3D point, itk::Index<3> index, ScalarType time, ScalarType pixelValue) { if (m_Implementation == nullptr) return; std::ostringstream stream; stream.imbue(std::locale::classic()); stream.precision(2); WriteCommonImageInfo(stream, point, index, time); stream << "; Pixel value: "; if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) stream << std::scientific; stream << pixelValue; m_Implementation->DisplayGreyValueText(stream.str().c_str()); } void StatusBar::DisplayImageInfo(Point3D point, itk::Index<3> index, ScalarType time, const char *pixelValue) { if (m_Implementation == nullptr) return; std::ostringstream stream; stream.imbue(std::locale::classic()); stream.precision(2); WriteCommonImageInfo(stream, point, index, time); stream << "; " << pixelValue; m_Implementation->DisplayGreyValueText(stream.str().c_str()); } void StatusBar::DisplayImageInfoInvalid() { if (m_Implementation != nullptr) m_Implementation->DisplayGreyValueText("No image information at this position!"); } void StatusBar::Clear() { if (m_Implementation != nullptr) m_Implementation->Clear(); } void StatusBar::SetSizeGripEnabled(bool enable) { if (m_Implementation != nullptr) { m_Implementation->SetSizeGripEnabled(enable); } } /** * Get the instance of this StatusBar */ StatusBar *StatusBar::GetInstance() { if (m_Instance == nullptr) // if not set, then send a errormessage on OutputWindow { m_Instance = new StatusBar(); } return m_Instance; } /** * Set an instance of this; application must do this!See Header! */ void StatusBar::SetImplementation(StatusBarImplementation *implementation) { if (m_Implementation == implementation) { return; } m_Implementation = implementation; } StatusBar::StatusBar() {} StatusBar::~StatusBar() {} } // end namespace mitk diff --git a/Modules/Core/src/Controllers/mitkUndoController.cpp b/Modules/Core/src/Controllers/mitkUndoController.cpp index e3e2debc9c..7b20a95ac9 100644 --- a/Modules/Core/src/Controllers/mitkUndoController.cpp +++ b/Modules/Core/src/Controllers/mitkUndoController.cpp @@ -1,213 +1,213 @@ /*============================================================================ 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 "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkLimitedLinearUndo.h" #include "mitkRenderingManager.h" #include "mitkVerboseLimitedLinearUndo.h" // static member-variables init. mitk::UndoModel::Pointer mitk::UndoController::m_CurUndoModel; mitk::UndoController::UndoModelMap mitk::UndoController::m_UndoModelList; mitk::UndoController::UndoType mitk::UndoController::m_CurUndoType; // const mitk::UndoController::UndoType mitk::UndoController::DEFAULTUNDOMODEL = LIMITEDLINEARUNDO; const mitk::UndoController::UndoType mitk::UndoController::DEFAULTUNDOMODEL = VERBOSE_LIMITEDLINEARUNDO; mitk::UndoController::UndoController(UndoType undoType) { if (SwitchUndoModel(undoType) == false) // existiert noch nicht in static-Liste { switch (undoType) { case LIMITEDLINEARUNDO: m_CurUndoModel = mitk::LimitedLinearUndo::New(); m_CurUndoType = undoType; m_UndoModelList.insert(UndoModelMap::value_type(undoType, m_CurUndoModel)); break; case VERBOSE_LIMITEDLINEARUNDO: m_CurUndoModel = mitk::VerboseLimitedLinearUndo::New(); m_CurUndoType = undoType; m_UndoModelList.insert(UndoModelMap::value_type(undoType, m_CurUndoModel)); break; // case ### // insert here, in add- and RemoveUndoModel new sets of UndoModels! // break; default: m_CurUndoModel = VerboseLimitedLinearUndo::New(); m_CurUndoType = undoType; m_UndoModelList.insert(UndoModelMap::value_type(undoType, m_CurUndoModel)); } } } mitk::UndoController::~UndoController() { } bool mitk::UndoController::SetOperationEvent(UndoStackItem *operationEvent) { m_CurUndoModel->SetOperationEvent(operationEvent); return true; } bool mitk::UndoController::Undo() { return this->Undo(true); } bool mitk::UndoController::Undo(bool fine) { bool ret = m_CurUndoModel->Undo(fine); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return ret; } bool mitk::UndoController::Redo() { return this->Redo(true); } bool mitk::UndoController::Redo(bool fine) { bool ret = m_CurUndoModel->Redo(fine); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return ret; } void mitk::UndoController::Clear() { m_CurUndoModel->Clear(); } void mitk::UndoController::ClearRedoList() { m_CurUndoModel->ClearRedoList(); } bool mitk::UndoController::RedoListEmpty() { return m_CurUndoModel->RedoListEmpty(); } //##Documentation //##Switches the UndoModel to the given Type //##if there is no equal Type in List, then return false bool mitk::UndoController::SwitchUndoModel(UndoType undoType) { if (m_CurUndoType == undoType) { return true; // already switched, don't need to be switched! } auto undoModelIter = m_UndoModelList.find(undoType); if (undoModelIter == m_UndoModelList.end()) { // undoType not found in List return false; } // found-> switch to UndoModel m_CurUndoModel = (undoModelIter)->second; m_CurUndoType = (undoModelIter)->first; return true; } //##Documentation //##adds a new kind of UndoModel to the set of UndoModels //##and switches to that UndoModel //##if the UndoModel exists already in the List, then nothing is done bool mitk::UndoController::AddUndoModel(UndoType undoType) { if (m_UndoModelList.find(undoType) != m_UndoModelList.end()) { // UndoModel already exists return false; } // doesn't already exist in list switch (undoType) { case LIMITEDLINEARUNDO: m_CurUndoModel = LimitedLinearUndo::New(); m_CurUndoType = undoType; m_UndoModelList.insert(UndoModelMap::value_type(undoType, m_CurUndoModel)); break; case VERBOSE_LIMITEDLINEARUNDO: m_CurUndoModel = VerboseLimitedLinearUndo::New(); m_CurUndoType = undoType; m_UndoModelList.insert(UndoModelMap::value_type(undoType, m_CurUndoModel)); break; default: // that undoType is not implemented! return false; } return true; } //##Documentation //##Removes an UndoModel from the set of UndoModels //##If that UndoModel is currently selected, then the DefaultUndoModel(const) is set. //##If the default is not in List, then the first UndoModel is set. //##UndoList may not be empty, so if the UndoType is the last, then return false; bool mitk::UndoController::RemoveUndoModel(UndoType undoType) { if (m_UndoModelList.size() < 2) { // for no empty m_UndoModelList return false; } // try deleting Element int ok = m_UndoModelList.erase(undoType); if (ok == 0) - { // delete unsucessful; Element of undoType not found + { // delete unsuccessful; Element of undoType not found return false; } // if m_CurUndoModel is the one removed, then change it to default or to the next or first if (m_CurUndoType == undoType) { // we have to change m_CurUndoModel and m_CurUndoType to an existing Model // if defaultUndoModel exists, then set to default auto undoModelIter = m_UndoModelList.find(DEFAULTUNDOMODEL); if (undoModelIter == m_UndoModelList.end()) { // DefaultUndoModel does not exists in m_CurUndoModelList undoModelIter = m_UndoModelList.begin(); } m_CurUndoModel = (undoModelIter)->second; m_CurUndoType = (undoModelIter)->first; return true; } // m_CurUndoType was not undoType and is not changed return true; } int mitk::UndoController::GetLastObjectEventIdInList() { return m_CurUndoModel->GetLastObjectEventIdInList(); } int mitk::UndoController::GetLastGroupEventIdInList() { return m_CurUndoModel->GetLastGroupEventIdInList(); } mitk::OperationEvent *mitk::UndoController::GetLastOfType(OperationActor *destination, OperationType opType) { return m_CurUndoModel->GetLastOfType(destination, opType); } mitk::UndoModel *mitk::UndoController::GetCurrentUndoModel() { return m_CurUndoModel; } diff --git a/Modules/Core/src/Controllers/mitkVtkLayerController.cpp b/Modules/Core/src/Controllers/mitkVtkLayerController.cpp index c0b9bc1b7d..94b6fef2f4 100644 --- a/Modules/Core/src/Controllers/mitkVtkLayerController.cpp +++ b/Modules/Core/src/Controllers/mitkVtkLayerController.cpp @@ -1,333 +1,333 @@ /*============================================================================ 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 "mitkVtkLayerController.h" #include #include #include #include #include mitk::VtkLayerController::vtkLayerControllerMapType mitk::VtkLayerController::s_LayerControllerMap; mitk::VtkLayerController *mitk::VtkLayerController::GetInstance(vtkSmartPointer renWin) { for (auto mapit = s_LayerControllerMap.begin(); mapit != s_LayerControllerMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::VtkLayerController::AddInstance(vtkSmartPointer renWin, vtkSmartPointer mitkSceneRenderer) { // ensure that no vtkRenderWindow is managed twice mitk::VtkLayerController::RemoveInstance(renWin); - // instanciate controller, add it to the map + // instantiate controller, add it to the map mitk::VtkLayerController *ControllerInstance = new mitk::VtkLayerController(renWin); ControllerInstance->InsertSceneRenderer(mitkSceneRenderer); s_LayerControllerMap.insert(vtkLayerControllerMapType::value_type(renWin, ControllerInstance)); } void mitk::VtkLayerController::RemoveInstance(vtkSmartPointer renWin) { auto mapit = s_LayerControllerMap.find(renWin); if (mapit != s_LayerControllerMap.end()) { delete mapit->second; s_LayerControllerMap.erase(mapit); } } mitk::VtkLayerController::VtkLayerController(vtkSmartPointer renderWindow) { m_RenderWindow = renderWindow; m_RenderWindow->Register(nullptr); m_BackgroundRenderers.clear(); m_ForegroundRenderers.clear(); m_SceneRenderers.clear(); } mitk::VtkLayerController::~VtkLayerController() { if (m_RenderWindow != nullptr) { m_RenderWindow->UnRegister(nullptr); } } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the background. * With forceAbsoluteBackground set true a renderer can be placed at the absolute background of the scene. * Multiple calls with forceAbsoluteBackground set true will set the latest registered renderer as background. */ void mitk::VtkLayerController::InsertBackgroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteBackground) { if (renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); if (forceAbsoluteBackground) { auto it = m_BackgroundRenderers.begin(); m_BackgroundRenderers.insert(it, renderer); } else m_BackgroundRenderers.push_back(renderer); UpdateLayers(); } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the foreground. * With forceAbsoluteBackground set true a renderer can be placed at the absolute foreground of the scene. * Multiple calls with forceAbsoluteForeground set true will set the latest registered renderer as foreground. */ void mitk::VtkLayerController::InsertForegroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteForeground) { if (renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); if (forceAbsoluteForeground) { auto it = m_ForegroundRenderers.begin(); m_ForegroundRenderers.insert(it, renderer); } else m_ForegroundRenderers.push_back(renderer); renderer->PreserveDepthBufferOn(); UpdateLayers(); } /** * Returns the Scene Renderer */ vtkSmartPointer mitk::VtkLayerController::GetSceneRenderer() { if (m_SceneRenderers.size() > 0) { auto it = m_SceneRenderers.begin(); return (*it); } else return nullptr; } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered between background renderers and * foreground renderers. */ void mitk::VtkLayerController::InsertSceneRenderer(vtkSmartPointer renderer) { if (renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); m_SceneRenderers.push_back(renderer); UpdateLayers(); } /** * A renderer which has been inserted via a insert... function can be removed from the vtkRenderWindow with * RemoveRenderer. */ void mitk::VtkLayerController::RemoveRenderer(vtkSmartPointer renderer) { RendererVectorType::iterator it; // background layers if (m_BackgroundRenderers.size() > 0) { it = std::find(m_BackgroundRenderers.begin(), m_BackgroundRenderers.end(), renderer); if (it != m_BackgroundRenderers.end()) { m_BackgroundRenderers.erase(it); UpdateLayers(); return; } } // scene layers if (m_SceneRenderers.size() > 0) { it = std::find(m_SceneRenderers.begin(), m_SceneRenderers.end(), renderer); if (it != m_SceneRenderers.end()) { m_SceneRenderers.erase(it); UpdateLayers(); return; } } // foreground layers if (m_ForegroundRenderers.size() > 0) { it = std::find(m_ForegroundRenderers.begin(), m_ForegroundRenderers.end(), renderer); if (it != m_ForegroundRenderers.end()) { m_ForegroundRenderers.erase(it); UpdateLayers(); return; } } } /** * Connects a VtkRenderWindow with the layer controller. */ void mitk::VtkLayerController::SetRenderWindow(vtkSmartPointer renwin) { if (renwin != nullptr) { RendererVectorType::iterator it; // Tell all renderers that there is a new renderwindow for (it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } for (it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } for (it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } // Set the new RenderWindow m_RenderWindow = renwin; } // Now sort renderers and add them to the renderwindow UpdateLayers(); } /** * Returns true if a renderer has been inserted */ bool mitk::VtkLayerController::IsRendererInserted(vtkSmartPointer renderer) { RendererVectorType::iterator it; // background layers if (m_BackgroundRenderers.size() > 0) { it = std::find(m_BackgroundRenderers.begin(), m_BackgroundRenderers.end(), renderer); if (it != m_BackgroundRenderers.end()) { return true; } } // scene layers if (m_SceneRenderers.size() > 0) { it = std::find(m_SceneRenderers.begin(), m_SceneRenderers.end(), renderer); if (it != m_SceneRenderers.end()) { return true; } } // foreground layers if (m_ForegroundRenderers.size() > 0) { it = std::find(m_ForegroundRenderers.begin(), m_ForegroundRenderers.end(), renderer); if (it != m_ForegroundRenderers.end()) { return true; } } return false; } /** * Internally used to sort all registered renderers and to connect the with the vtkRenderWindow. * Mention that VTK Version 5 and above is rendering higher numbers in the background and VTK - * Verison < 5 in the foreground. + * Version < 5 in the foreground. */ void mitk::VtkLayerController::UpdateLayers() { // Remove all Renderers from renderwindow vtkSmartPointer v = m_RenderWindow->GetRenderers(); v->RemoveAllItems(); auto numberOfLayers = static_cast(m_BackgroundRenderers.size() + m_SceneRenderers.size() + m_ForegroundRenderers.size()); int currentLayerNumber; bool traverseUpwards; currentLayerNumber = 0; traverseUpwards = true; m_RenderWindow->SetNumberOfLayers(numberOfLayers); RendererVectorType::iterator it; - // assign a layer number for the backround renderers + // assign a layer number for the background renderers for (it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if (traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } // assign a layer number for the scene renderers for (it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if (traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } // assign a layer number for the foreground renderers for (it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if (traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } } /** * Returns the number of renderers in the renderwindow. */ unsigned int mitk::VtkLayerController::GetNumberOfRenderers() { return static_cast(m_BackgroundRenderers.size() + m_SceneRenderers.size() + m_ForegroundRenderers.size()); } void mitk::VtkLayerController::SetEraseForAllRenderers(int i) { this->m_RenderWindow->SetErase(i); RendererVectorType::iterator it; for (it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) (*it)->SetErase(i); for (it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) (*it)->SetErase(i); for (it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) (*it)->SetErase(i); } diff --git a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp index 31eb0ac623..1c845bfc4e 100644 --- a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp @@ -1,332 +1,332 @@ /*============================================================================ 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 "mitkAbstractTransformGeometry.h" #include mitk::AbstractTransformGeometry::AbstractTransformGeometry() : Superclass(), m_Plane(nullptr), m_FrameGeometry(nullptr) { Initialize(); m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry &other) : Superclass(other), m_ParametricBoundingBox(other.m_ParametricBoundingBox) { if (other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); this->SetParametricBounds(m_ParametricBoundingBox->GetBounds()); } this->SetPlane(other.m_Plane); this->SetFrameGeometry(other.m_FrameGeometry); m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } mitk::AbstractTransformGeometry::~AbstractTransformGeometry() { } vtkAbstractTransform *mitk::AbstractTransformGeometry::GetVtkAbstractTransform() const { return m_ItkVtkAbstractTransform->GetVtkAbstractTransform(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtentInMM(int direction) const { if (m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is nullptr."); } return m_Plane->GetExtentInMM(direction); } const itk::Transform *mitk::AbstractTransformGeometry::GetParametricTransform() const { return m_ItkVtkAbstractTransform; } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); mitk::Point2D pt2d_mm; bool isInside; isInside = Map(pt3d_mm, pt2d_mm); Map(pt2d_mm, projectedPt3d_mm); return isInside; // Point3D pt3d_units; // pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); // pt3d_units[2] = 0; // projectedPt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_units); // return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_units; pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); return m_Plane->Map(pt3d_units, pt2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); m_Plane->Map(pt2d_mm, pt3d_mm); pt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_mm); } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by " "m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); assert(this->IsBoundingBoxNull() == false); auto inverse = mitk::AffineTransform3D::New(); GetIndexToWorldTransform()->GetInverse(inverse); Vector3D vec3d_units = inverse->GetMatrix() * vec3d_mm; vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; mitk::ScalarType temp[3]; unsigned int i, j; for (j = 0; j < 3; ++j) temp[j] = atPt3d_mm[j] - GetIndexToWorldTransform()->GetOffset()[j]; for (i = 0; i < 3; ++i) { pt3d_units[i] = 0.0; for (j = 0; j < 3; ++j) pt3d_units[i] += inverse->GetMatrix()[i][j] * temp[j]; } return this->GetBoundingBox()->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Project(const mitk::Vector3D & /*vec3d_mm*/, mitk::Vector3D & /*projectedVec3d_mm*/) const { MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, " - "const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not " + "const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunately this one is not " "implemented at the moment. Sorry :("; itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by " "m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); return false; } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); ScalarType vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::Vector3D vec3d_units; vtk2itk(vtkvec, vec3d_units); return m_Plane->Map(atPt3d_mm, vec3d_units, vec2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const { m_Plane->Map(atPt2d_mm, vec2d_mm, vec3d_mm); Point3D atPt3d_mm; Map(atPt2d_mm, atPt3d_mm); float vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); vtk2itk(vtkvec, vec3d_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const { m_Plane->IndexToWorld(pt_units, pt_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const { m_Plane->WorldToIndex(pt_mm, pt_units); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D & /*atPt2d_units*/, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::IndexToWorld(point, vec, vec). Use " "AbstractTransformGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { m_Plane->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::WorldToIndex(point, vec, vec). Use " "AbstractTransformGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { m_Plane->WorldToIndex(vec_mm, vec_units); } bool mitk::AbstractTransformGeometry::IsAbove(const mitk::Point3D &pt3d_mm, bool /*considerBoundingBox*/) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_ParametricWorld; pt3d_ParametricWorld = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); Point3D pt3d_ParametricUnits; ((BaseGeometry *)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits); return (pt3d_ParametricUnits[2] > m_ParametricBoundingBox->GetBounds()[4]); } void mitk::AbstractTransformGeometry::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform) { m_ItkVtkAbstractTransform->SetVtkAbstractTransform(aVtkAbstractTransform); } void mitk::AbstractTransformGeometry::SetPlane(const mitk::PlaneGeometry *aPlane) { if (aPlane != nullptr) { m_Plane = static_cast(aPlane->Clone().GetPointer()); BoundingBox::BoundsArrayType b = m_Plane->GetBoundingBox()->GetBounds(); SetParametricBounds(b); CalculateFrameGeometry(); } else { if (m_Plane.IsNull()) return; m_Plane = nullptr; } Modified(); } void mitk::AbstractTransformGeometry::CalculateFrameGeometry() { if ((m_Plane.IsNull()) || (m_FrameGeometry.IsNotNull())) return; //@warning affine-transforms and bounding-box should be set by specific sub-classes! SetBounds(m_Plane->GetBoundingBox()->GetBounds()); } void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::BaseGeometry *frameGeometry) { if ((frameGeometry != nullptr) && (frameGeometry->IsValid())) { m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform()); SetBounds(m_FrameGeometry->GetBounds()); } else { m_FrameGeometry = nullptr; } } itk::ModifiedTimeType mitk::AbstractTransformGeometry::GetMTime() const { if (Superclass::GetMTime() < m_ItkVtkAbstractTransform->GetMTime()) return m_ItkVtkAbstractTransform->GetMTime(); return Superclass::GetMTime(); } void mitk::AbstractTransformGeometry::SetOversampling(mitk::ScalarType oversampling) { if (m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is not set."); } mitk::BoundingBox::BoundsArrayType bounds = m_Plane->GetBounds(); bounds[1] *= oversampling; bounds[3] *= oversampling; bounds[5] *= oversampling; SetParametricBounds(bounds); } itk::LightObject::Pointer mitk::AbstractTransformGeometry::InternalClone() const { Self::Pointer newGeometry = new AbstractTransformGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::AbstractTransformGeometry::SetParametricBounds(const BoundingBox::BoundsArrayType &bounds) { m_ParametricBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < GetNDimensions(); ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_ParametricBoundingBox->SetPoints(pointscontainer); m_ParametricBoundingBox->ComputeBoundingBox(); this->Modified(); } const mitk::BoundingBox::BoundsArrayType &mitk::AbstractTransformGeometry::GetParametricBounds() const { assert(m_ParametricBoundingBox.IsNotNull()); return m_ParametricBoundingBox->GetBounds(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtent(int direction) const { if (direction < 0 || direction >= 3) mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. "; assert(m_ParametricBoundingBox.IsNotNull()); BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } diff --git a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp index f287df5d52..e46ec7c21f 100644 --- a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp @@ -1,344 +1,344 @@ /*============================================================================ 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 mitk::ArbitraryTimeGeometry::ArbitraryTimeGeometry() = default; mitk::ArbitraryTimeGeometry::~ArbitraryTimeGeometry() = default; void mitk::ArbitraryTimeGeometry::Initialize() { this->ClearAllGeometries(); Geometry3D::Pointer geo = Geometry3D::New(); geo->Initialize(); this->AppendNewTimeStep(geo, 0, 1); Update(); } mitk::TimeStepType mitk::ArbitraryTimeGeometry::CountTimeSteps() const { return static_cast(m_GeometryVector.size()); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint() const { return m_MinimumTimePoints.empty() ? 0.0 : m_MinimumTimePoints.front(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint() const { TimePointType result = 0; if ( !m_MaximumTimePoints.empty() ) { result = m_MaximumTimePoints.back(); } /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed as soon as T28262 is solved! + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed as soon as T28262 is solved! if (this->HasCollapsedFinalTimeStep()) { result = m_MinimumTimePoints.back() + 1; } - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// return result; } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint( TimeStepType step ) const { return step < m_MinimumTimePoints.size() ? m_MinimumTimePoints[step] : 0.0f; }; mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint( TimeStepType step ) const { TimePointType result = 0; if (step < m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step]; } /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed as soon as T28262 is solved! + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed as soon as T28262 is solved! if (step + 1 == m_MaximumTimePoints.size() && this->HasCollapsedFinalTimeStep()) { result = m_MinimumTimePoints[step] + 1; } - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// return result; }; mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds() const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint(); bounds[1] = this->GetMaximumTimePoint(); return bounds; } mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds(TimeStepType step) const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint( step ); bounds[1] = this->GetMaximumTimePoint( step ); return bounds; } bool mitk::ArbitraryTimeGeometry::IsValidTimePoint(TimePointType timePoint) const { return this->GetMinimumTimePoint() <= timePoint && (timePoint < this->GetMaximumTimePoint() || (this->HasCollapsedFinalTimeStep() && timePoint <= this->GetMaximumTimePoint())); } bool mitk::ArbitraryTimeGeometry::IsValidTimeStep(TimeStepType timeStep) const { return timeStep < this->CountTimeSteps(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::TimeStepToTimePoint( TimeStepType timeStep ) const { TimePointType result = 0.0; if (timeStep < m_MinimumTimePoints.size() ) { result = m_MinimumTimePoints[timeStep]; } return result; } mitk::TimeStepType mitk::ArbitraryTimeGeometry::TimePointToTimeStep(TimePointType timePoint) const { mitk::TimeStepType result = 0; if (timePoint >= GetMinimumTimePoint()) { for (auto pos = m_MaximumTimePoints.cbegin(); pos != m_MaximumTimePoints.cend(); ++pos) { /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // The part ("+1.") inline marked as workarround should be removed as soon as T28262 is solved! + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // The part ("+1.") inline marked as workaround should be removed as soon as T28262 is solved! if (timePoint < *pos || (pos == std::prev(m_MaximumTimePoints.cend()) - && timePoint <= *pos +1//<- +1 is the workarround + && timePoint <= *pos +1//<- +1 is the workaround && this->HasCollapsedFinalTimeStep())) { break; } - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// ++result; } } return result; } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimeStep(TimeStepType timeStep) const { if ( IsValidTimeStep( timeStep ) ) { return m_GeometryVector[timeStep]; } else { return nullptr; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimePoint( TimePointType timePoint ) const { if ( this->IsValidTimePoint( timePoint ) ) { const TimeStepType timeStep = this->TimePointToTimeStep( timePoint ); return this->GetGeometryForTimeStep( timeStep ); } else { return nullptr; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryCloneForTimeStep( TimeStepType timeStep ) const { if ( timeStep >= m_GeometryVector.size() ) return nullptr; return m_GeometryVector[timeStep]->Clone(); } bool mitk::ArbitraryTimeGeometry::IsValid() const { bool isValid = true; isValid &= m_GeometryVector.size() > 0; return isValid; } void mitk::ArbitraryTimeGeometry::ClearAllGeometries() { m_GeometryVector.clear(); m_MinimumTimePoints.clear(); m_MaximumTimePoints.clear(); } void mitk::ArbitraryTimeGeometry::ReserveSpaceForGeometries( TimeStepType numberOfGeometries ) { m_GeometryVector.reserve( numberOfGeometries ); m_MinimumTimePoints.reserve( numberOfGeometries ); m_MaximumTimePoints.reserve( numberOfGeometries ); } void mitk::ArbitraryTimeGeometry::Expand( mitk::TimeStepType size ) { m_GeometryVector.reserve( size ); const mitk::TimeStepType lastIndex = this->CountTimeSteps() - 1; const TimePointType minTP = this->GetMinimumTimePoint( lastIndex ); TimePointType maxTP = this->GetMaximumTimePoint( lastIndex ); const TimePointType duration = maxTP - minTP; while (m_GeometryVector.size() < size) { m_GeometryVector.push_back( Geometry3D::New().GetPointer() ); m_MinimumTimePoints.push_back( maxTP ); maxTP += duration; m_MaximumTimePoints.push_back( maxTP ); } } void mitk::ArbitraryTimeGeometry::ReplaceTimeStepGeometries(const BaseGeometry *geometry) { for ( auto pos = m_GeometryVector.begin(); pos != m_GeometryVector.end(); ++pos ) { *pos = geometry->Clone(); } } void mitk::ArbitraryTimeGeometry::SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) { assert( timeStep <= m_GeometryVector.size() ); if ( timeStep == m_GeometryVector.size() ) { m_GeometryVector.push_back( geometry ); } m_GeometryVector[timeStep] = geometry; } itk::LightObject::Pointer mitk::ArbitraryTimeGeometry::InternalClone() const { itk::LightObject::Pointer parent = Superclass::InternalClone(); ArbitraryTimeGeometry::Pointer newTimeGeometry = dynamic_cast(parent.GetPointer()); newTimeGeometry->m_MinimumTimePoints = this->m_MinimumTimePoints; newTimeGeometry->m_MaximumTimePoints = this->m_MaximumTimePoints; newTimeGeometry->m_GeometryVector.clear(); for (TimeStepType i = 0; i < CountTimeSteps(); ++i) { newTimeGeometry->m_GeometryVector.push_back( this->m_GeometryVector[i]->Clone() ); } return parent; } void mitk::ArbitraryTimeGeometry::AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { if ( !geometry ) { mitkThrow() << "Cannot append geometry to time geometry. Invalid geometry passed (nullptr pointer)."; } if (maximumTimePoint < minimumTimePoint) { - mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict. Maxmimum time point ("< minimumTimePoint ) { - mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict new time point and currently last time point overlapp."; + mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict new time point and currently last time point overlap."; } } m_GeometryVector.push_back( geometry ); m_MinimumTimePoints.push_back( minimumTimePoint ); m_MaximumTimePoints.push_back( maximumTimePoint ); } void mitk::ArbitraryTimeGeometry::AppendNewTimeStepClone(const BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { BaseGeometry::Pointer clone = geometry->Clone(); this->AppendNewTimeStep(clone, minimumTimePoint, maximumTimePoint); }; void mitk::ArbitraryTimeGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << " MinimumTimePoint: " << this->GetMinimumTimePoint() << " ms" << std::endl; os << indent << " MaximumTimePoint: " << this->GetMaximumTimePoint() << " ms" << std::endl; os << std::endl; os << indent << " min TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MinimumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MinimumTimePoints[i] << " ms" << std::endl; } os << std::endl; os << indent << " max TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MaximumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MaximumTimePoints[i] << " ms" << std::endl; } /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed as soon as T28262 is solved! + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed as soon as T28262 is solved! if (this->HasCollapsedFinalTimeStep()) { os << indent << "Caution: This time geometry has a collapsed finale time step." << std::endl; os << indent << " Most likely reason is that no duration could be deduced from the original data" << std::endl; os << indent << " (e.g. DICOM dynamic series stored as single frame images)." << std::endl; os << indent << " Currently we expand it by 1 ms (see T27883 for more details)." << std::endl; } - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// } bool mitk::ArbitraryTimeGeometry::HasCollapsedFinalTimeStep() const { bool result = false; if (!m_MaximumTimePoints.empty() && !m_MinimumTimePoints.empty()) { result = m_MinimumTimePoints.back() == m_MaximumTimePoints.back(); } return result; } diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index 09493418f5..55ea6e4b07 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1101 +1,1101 @@ /*============================================================================ 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 "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" #include "mitkMatrix.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref()); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (false); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); } } if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->GetVnlMatrix(); if (len > extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len); Matrix3D matrix; matrix = vnlmatrix; m_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const { bool inside = false; // if it is an image geometry, we need to convert the index to discrete values // this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0] = itk::Math::RoundHalfIntegerUp(index[0]); discretIndex[1] = itk::Math::RoundHalfIntegerUp(index[1]); discretIndex[2] = itk::Math::RoundHalfIntegerUp(index[2]); inside = this->GetBoundingBox()->IsInside(discretIndex); // we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = Point3D(tempOut); } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix(); if (inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { auto *pointOp = dynamic_cast(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { auto *scaleOp = dynamic_cast(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { auto *rotateOp = dynamic_cast(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { // Copy necessary to avoid vtk warning vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix( dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { auto *applyMatrixOp = dynamic_cast(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref(); } mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform( const mitk::AffineTransform3D *transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; if (transform != nullptr) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i))); } else { for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, GetCornerPoint(i)); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType) { std::ostringstream out; out << '['; for (int i = 0; i < 3; ++i) { out << '['; for (int j = 0; j < 3; ++j) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for (int i = 0; i < 3; ++i) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() { return m_GeometryTransform->GetVtkMatrix(); } bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if (m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if (isAnImageGeometry == true) FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5); else FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5); Point3D originWorld; originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; auto inverse = mitk::AffineTransform3D::New(); if (this->GetIndexToWorldTransform()->GetInverse(inverse)) { os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << inverse->GetMatrix()[i][j] << " "; } os << std::endl; } } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, eps))) { if (verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << coordinateEps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), directionEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << directionEps; } result = false; } if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << coordinateEps; } result = false; } } // Compare ImageGeometry Flag if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } // Compare FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), coordinateEps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose) { return Equal(leftHandSide, rightHandSide, eps, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings (must be equal) const auto testedSpacing = testGeo.GetSpacing(); if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is " << referenceGeo.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare ImageGeometry Flag (must be equal) if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is " << testGeo.GetImageGeometry(); } result = false; } // Compare IndexToWorldTransform Matrix (must be equal -> same axis directions) if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), directionEps, verbose)) { result = false; } //check if the geometry is within or equal to the bounds of reference geomentry. for (int i = 0; i<8; ++i) { auto testCorner = testGeo.GetCornerPoint(i); bool isInside = false; mitk::Point3D testCornerIndex; referenceGeo.WorldToIndex(testCorner, testCornerIndex); std::bitset bs(i); - //To regard the coordinateEps, we substract or add it to the index elements - //depending on wether it was constructed by a lower or an upper bound value + //To regard the coordinateEps, we subtract or add it to the index elements + //depending on whether it was constructed by a lower or an upper bound value //(see implementation of BaseGeometry::GetCorner()). if (bs.test(0)) { testCornerIndex[2] -= coordinateEps; } else { testCornerIndex[2] += coordinateEps; } if (bs.test(1)) { testCornerIndex[1] -= coordinateEps; } else { testCornerIndex[1] += coordinateEps; } if (bs.test(2)) { testCornerIndex[0] -= coordinateEps; } else { testCornerIndex[0] += coordinateEps; } isInside = referenceGeo.IsIndexInside(testCornerIndex); if (!isInside) { if (verbose) { MITK_INFO << "[( Geometry3D )] corner point is not inside. "; MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is " << testGeo.GetCornerPoint(i); } result = false; } } // check grid of test geometry is on the grid of the reference geometry. This is important as the // boundingbox is only checked for containing the tested geometry, but if a corner (one is enough // as we know that axis and spacing are equal, due to equal transfor (see above)) of the tested geometry // is on the grid it is really a sub geometry (as they have the same spacing and axis). auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0); mitk::Vector3D cornerIndexOffset; referenceGeo.WorldToIndex(cornerOffset, cornerIndexOffset); for (unsigned int i = 0; i < 3; ++i) { auto pixelCountContinous = cornerIndexOffset[i]; auto pixelCount = std::round(pixelCountContinous); if (std::abs(pixelCount - pixelCountContinous) > coordinateEps) { if (verbose) { MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. "; MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is " << pixelCountContinous << " for axis "<GetAll(); // for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); // m_NodeModifiedObserverTags.clear(); // m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(DataNode *node, DataNode *parent) { DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); if (parent != nullptr) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const DataStorage::SetOfObjects *nodes) { if (nodes == nullptr) return; for (DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const { DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const { if (condition == nullptr) return nullptr; DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os, indent); DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != nullptr) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const { if (set == nullptr) return nullptr; DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); for (DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == nullptr || condition->CheckNode(it.Value()) == - true) // alway copy the set, otherwise the iterator in DataStorage::Remove() will crash + true) // always copy the set, otherwise the iterator in DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { PropertyList *pl = nodeIt.Value()->GetPropertyList(); for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) if (dynamic_cast(propIt->second.GetPointer()) != nullptr) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const DataNode *node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const DataNode *node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &) { const auto *_Node = dynamic_cast(caller); if (_Node) { InteractorChangedNodeEvent.Send(_Node); } } void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event) { if (m_BlockNodeModifiedEvents) return; const auto *_Node = dynamic_cast(caller); if (_Node) { const auto *modEvent = dynamic_cast(&event); if (modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners(const DataNode *_Node) { std::lock_guard locked(m_MutexOne); // node must not be 0 and must not be yet registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver(InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners(const DataNode *_Node) { std::lock_guard locked(m_MutexOne); // node must not be 0 and must be registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { if (input == nullptr) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; Vector3D minSpacing; minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmax = itk::NumericTraits::max(); ScalarType stmin = itk::NumericTraits::NonpositiveMin(); std::set existingTimePoints; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++) { // We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing // in its original space, which, in case of an image geometry, can have the values in different // order than in world space. For the further calculations, we need to have the spacing values // in world coordinate order (sag-cor-ax). Vector3D spacing; spacing.Fill(1.0); node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing); for (int axis = 0; axis < 3; ++ axis) { ScalarType space = std::abs(spacing[axis]); if (space < minSpacing[axis]) { minSpacing[axis] = space; } } const auto curTimeBounds = timeGeometry->GetTimeBounds(i); if ((curTimeBounds[0] > stmin) && (curTimeBounds[0] < stmax)) { existingTimePoints.insert(curTimeBounds[0]); } if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax)) { maximalTime = curTimeBounds[1]; } } } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << e.GetDescription() << std::endl; } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps if (existingTimePoints.empty()) // make sure that there is at least one time sliced geometry in the data storage { existingTimePoints.insert(0.0); maximalTime = 1.0; } ArbitraryTimeGeometry::Pointer timeGeometry = nullptr; if (result->GetPoints()->Size() > 0) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); AffineTransform3D::OutputVectorType offset; for (int i = 0; i < 3; ++i) { offset[i] = bounds[i * 2]; bounds[i * 2] = 0.0; bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i]; } geometry->GetIndexToWorldTransform()->SetOffset(offset); geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry auto tsIterator = existingTimePoints.cbegin(); auto tsPredecessor = tsIterator++; auto tsEnd = existingTimePoints.cend(); timeGeometry = ArbitraryTimeGeometry::New(); for (; tsIterator != tsEnd; ++tsIterator, ++tsPredecessor) { timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, *tsIterator); } timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, maximalTime); timeGeometry->Update(); } return timeGeometry.GetPointer(); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = geometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin = itk::NumericTraits::NonpositiveMin(); stmax = itk::NumericTraits::max(); timeBounds[0] = stmax; timeBounds[1] = stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { const TimeBounds &curTimeBounds = geometry->GetTimeBounds(); cur = curTimeBounds[0]; // is it after -infinity, but before everything else that we found until now? if ((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur = curTimeBounds[1]; // is it before infinity, but after everything else that we found until now? if ((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if (!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents(bool block) { m_BlockNodeModifiedEvents = block; } mitk::DataNode::Pointer mitk::FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer* baseRender) { if (nodes.IsNull()) { return nullptr; } mitk::DataNode::Pointer topLayerNode = nullptr; int maxLayer = std::numeric_limits::min(); for (const auto &node : *nodes) { if (node.IsNull()) { continue; } bool isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { continue; } auto data = node->GetData(); if (nullptr == data) { continue; } auto geometry = data->GetGeometry(); if (nullptr == geometry || !geometry->IsInside(worldPosition)) { continue; } auto timeGeometry = data->GetUpdatedTimeGeometry(); if (nullptr == timeGeometry) { continue; } if (!timeGeometry->IsValidTimePoint(timePoint)) { continue; } int layer = 0; if (!node->GetIntProperty("layer", layer, baseRender)) { continue; } if (layer <= maxLayer) { continue; } if (!node->IsVisible(baseRender)) { continue; } topLayerNode = node; maxLayer = layer; } return topLayerNode; } diff --git a/Modules/Core/src/DataManagement/mitkImage.cpp b/Modules/Core/src/DataManagement/mitkImage.cpp index 0c10c1302a..f2b7b5b8aa 100644 --- a/Modules/Core/src/DataManagement/mitkImage.cpp +++ b/Modules/Core/src/DataManagement/mitkImage.cpp @@ -1,1355 +1,1355 @@ /*============================================================================ 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 "mitkImage.h" #include "mitkCompareImageDataFilter.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" #include "mitkPixelTypeMultiplex.h" #include // VTK #include // Other #include #define FILL_C_ARRAY(_arr, _size, _value) \ for (unsigned int i = 0u; i < _size; i++) \ \ { \ _arr[i] = _value; \ } mitk::Image::Image() : m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize(other.GetPixelType(), other.GetDimension(), other.GetDimensions()); // Since the above called "Initialize" method doesn't take the geometry into account we need to set it // here manually TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = other.GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = other.GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { this->Clear(); m_ReferenceCount = 3; m_ReferenceCount = 0; delete[] m_OffsetTable; delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if ((i >= 0) && (i < (int)m_Dimension)) return m_Dimensions[i]; return 1; } template void AccessPixel(const mitk::PixelType ptype, void *data, const unsigned int offset, double &value) { value = 0.0; if (data == nullptr) return; if (ptype.GetBpe() != 24) { value = (double)(((T *)data)[offset]); } else { const unsigned int rgboffset = offset; double returnvalue = (((T *)data)[rgboffset]); returnvalue += (((T *)data)[rgboffset + 1]); returnvalue += (((T *)data)[rgboffset + 2]); value = returnvalue; } } vtkImageData *mitk::Image::GetVtkImageData(int t, int n) { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } const vtkImageData *mitk::Image::GetVtkImageData(int t, int n) const { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidSlice(s, t, n) == false) return nullptr; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos = GetSliceIndex(s, t, n); if (m_Slices[pos].GetPointer() != nullptr) { return m_Slices[pos]; } // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // slice is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsSliceSet_unlocked(s, t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidVolume(t, n) == false) return nullptr; ImageDataItemPointer ch, vol; // volume directly available? int pos = GetVolumeIndex(t, n); vol = m_Volumes[pos]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); vol->SetComplete(true); return m_Volumes[pos] = vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete = true; unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { complete = false; break; } } if (complete) { // if there is only single slice we do not need to combine anything if (m_Dimensions[2] <= 1) { ImageDataItemPointer sl; sl = GetSliceData_unlocked(0, t, n, data, importMemoryManagement); vol = new ImageDataItem(*sl, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory); vol->SetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol = m_Volumes[pos]; // ok, let's combine the slices! if (vol.GetPointer() == nullptr) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); } vol->SetComplete(true); size_t size = m_OffsetTable[2] * (ptypeSize); for (s = 0; s < m_Dimensions[2]; ++s) { int posSl; ImageDataItemPointer sl; posSl = GetSliceIndex(s, t, n); sl = m_Slices[posSl]; if (sl->GetParent() != vol) { // copy data of slices in volume size_t offset = ((size_t)s) * size; std::memcpy(static_cast(vol->GetData()) + offset, sl->GetData(), size); // replace old slice with reference to volume sl = new ImageDataItem( *vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * size); sl->SetComplete(true); m_Slices[posSl] = sl; } } } return m_Volumes[pos] = vol; } // volume is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsVolumeSet_unlocked(t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData_unlocked(t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidChannel(n) == false) return nullptr; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if (IsChannelSet_unlocked(n)) { // if there is only one time frame we do not need to combine anything if (m_Dimensions[3] <= 1) { vol = GetVolumeData_unlocked(0, n, data, importMemoryManagement); ch = new ImageDataItem(*vol, m_ImageDescriptor, 0, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = m_Channels[n]; // ok, let's combine the volumes! if (ch.GetPointer() == nullptr) ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); ch->SetComplete(true); size_t size = m_OffsetTable[m_Dimension - 1] * (ptypeSize); unsigned int t; auto slicesIt = m_Slices.begin() + n * m_Dimensions[2] * m_Dimensions[3]; for (t = 0; t < m_Dimensions[3]; ++t) { int posVol; ImageDataItemPointer vol; posVol = GetVolumeIndex(t, n); vol = GetVolumeData_unlocked(t, n, data, importMemoryManagement); if (vol->GetParent() != ch) { // copy data of volume in channel size_t offset = ((size_t)t) * m_OffsetTable[3] * (ptypeSize); std::memcpy(static_cast(ch->GetData()) + offset, vol->GetData(), size); // replace old volume with reference to channel vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); m_Volumes[posVol] = vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull = nullptr; for (unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } } return m_Channels[n] = ch; } // channel is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); // did it work? if (IsChannelSet_unlocked(n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData_unlocked(n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateChannelData_unlocked(n, data, importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsSliceSet_unlocked(s, t, n); } bool mitk::Image::IsSliceSet_unlocked(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() != nullptr) { return true; } ImageDataItemPointer ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { return true; } ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { return true; } return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsVolumeSet_unlocked(t, n); } bool mitk::Image::IsVolumeSet_unlocked(int t, int n) const { if (IsValidVolume(t, n) == false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { return false; } } return true; } bool mitk::Image::IsChannelSet(int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsChannelSet_unlocked(n); } bool mitk::Image::IsChannelSet_unlocked(int n) const { if (IsValidChannel(n) == false) return false; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for (t = 0; t < m_Dimensions[3]; ++t) { if (IsVolumeSet_unlocked(t, n) == false) { return false; } } return true; } bool mitk::Image::SetSlice(const void *data, int s, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportSlice(const_cast(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidSlice(s, t, n) == false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if (IsSliceSet(s, t, n)) { sl = GetSliceData(s, t, n, data, importMemoryManagement); if (sl->GetManageMemory() == false) { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; } if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); sl->Modified(); // we have changed the data: call Modified()! Modified(); } else { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); // we just added a missing slice, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidVolume(t, n) == false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if (IsVolumeSet(t, n)) { vol = GetVolumeData(t, n, data, importMemoryManagement); if (vol->GetManageMemory() == false) { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; } if (vol->GetData() != data) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); vol->Modified(); vol->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; if (vol->GetData() != data) { std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(vol->GetData()); // we just added a missing Volume, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(const void *const_data, int t, int n) { return this->SetImportVolume(const_cast(const_data), t, n, CopyMemory); } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidChannel(n) == false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if (IsChannelSet(n)) { ch = GetChannelData(n, data, importMemoryManagement); if (ch->GetManageMemory() == false) { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; } if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->Modified(); ch->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(ch->GetData()); // we just added a missing Channel, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for (it = m_Slices.begin(), end = m_Slices.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Volumes.begin(), end = m_Volumes.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Channels.begin(), end = m_Channels.end(); it != end; ++it) { (*it) = nullptr; } m_CompleteData = nullptr; if (m_ImageStatistics == nullptr) { m_ImageStatistics = new mitk::ImageStatisticsHolder(this); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1); } void mitk::Image::Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension = dimension; if (!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for (i = 0; i < dimension; ++i) { if (dimensions[i] < 1) itkExceptionMacro(<< "invalid dimension[" << i << "]: " << dimensions[i]); } // create new array since the old was deleted m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; // initialize the first four dimensions to 1, the remaining 4 to 0 FILL_C_ARRAY(m_Dimensions, 4, 1u); FILL_C_ARRAY((m_Dimensions + 4), 4, 0u); // copy in the passed dimension information std::memcpy(m_Dimensions, dimensions, sizeof(unsigned int) * m_Dimension); this->m_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize(this->m_Dimensions, this->m_Dimension); for (i = 0; i < 4; ++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if (m_LargestPossibleRegion.GetNumberOfPixels() == 0) { delete[] m_Dimensions; m_Dimensions = nullptr; return; } for (unsigned int i = 0u; i < channels; i++) { this->m_ImageDescriptor->AddNewChannel(type); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull = nullptr; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels() * m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels() * m_Dimensions[3] * m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels, int tDim) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry.Clone(), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels, int tDim) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0) + 0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1) + 0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2) + 0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if (dimensions[2] > 1) dimension = 3; if (dimensions[3] > 1) dimension = 4; Initialize(type, dimension, dimensions, channels); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); // make sure the image geometry flag is properly set for all time steps for (TimeStepType step = 0; step < cloned->CountTimeSteps(); ++step) { if (!cloned->GetGeometryCloneForTimeStep(step)->GetImageGeometry()) { MITK_WARN("Image.3DnT.Initialize") << " Attempt to initialize an image with a non-image geometry. " "Re-interpretting the initialization geometry for timestep " << step << " as image geometry, the original geometry remains unchanged."; cloned->GetGeometryForTimeStep(step)->ImageGeometryOn(); } } } else { // make sure the image geometry coming from outside has proper value of the image geometry flag BaseGeometry::Pointer cloned = geometry.GetGeometryCloneForTimeStep(0)->Clone(); if (!cloned->GetImageGeometry()) { MITK_WARN("Image.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting " "the initialization geometry as image geometry, the original geometry remains " "unchanged."; cloned->ImageGeometryOn(); } Superclass::SetGeometry(cloned); } } void mitk::Image::Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels, int tDim) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d.Clone(), sDim); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image *image) { Initialize(image->GetPixelType(), *image->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData *vtkimagedata, int channels, int tDim, int sDim, int pDim) { if (vtkimagedata == nullptr) return; m_Dimension = vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = vtkimagedata->GetDimensions()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } if (pDim >= 0) { tmpDimensions[1] = pDim; if (m_Dimension < 2) m_Dimension = 2; } if (sDim >= 0) { tmpDimensions[2] = sDim; if (m_Dimension < 3) m_Dimension = 3; } if (tDim >= 0) { tmpDimensions[3] = tDim; if (m_Dimension < 4) m_Dimension = 4; } mitk::PixelType pixelType(MakePixelType(vtkimagedata)); Initialize(pixelType, m_Dimension, tmpDimensions, channels); const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = spacinglist[1]; if (m_Dimension >= 3) spacing[2] = spacinglist[2]; // access origin of vtkImage Point3D origin; double vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = vtkorigin[1]; if (m_Dimension >= 3) origin[2] = vtkorigin[2]; SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction auto *planeGeometry = static_cast(slicedGeometry->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete[] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if (m_Initialized) return ((s >= 0) && (s < (int)m_Dimensions[2]) && (t >= 0) && (t < (int)m_Dimensions[3]) && (n >= 0) && (n < (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if (m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if (m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if (m_OffsetTable != nullptr) delete[] m_OffsetTable; m_OffsetTable = new size_t[m_Dimension > 4 ? m_Dimension + 1 : 4 + 1]; unsigned int i; size_t num = 1; m_OffsetTable[0] = 1; for (i = 0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i + 1] = num; } for (; i < 4; ++i) m_OffsetTable[i + 1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ((m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0) || (t == 0)); } void mitk::Image::Expand(unsigned int timeSteps) { if (timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; return ((size_t)s) + ((size_t)t) * m_Dimensions[2] + ((size_t)n) * m_Dimensions[3] * m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if (IsValidVolume(t, n) == false) return false; return ((size_t)t) + ((size_t)n) * m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetSliceIndex(s, t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if (vol.GetPointer() != nullptr) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t, n)] = vol = AllocateVolumeData_unlocked(t, n, nullptr, importMemoryManagement); sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; ////ALTERNATIVE: //// allocate new slice // sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); // m_Slices[pos]=sl; // return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetVolumeIndex(t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); return m_Volumes[pos] = vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if (importMemoryManagement == CopyMemory) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); if (data != nullptr) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } else { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos] = vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { ImageDataItemPointer ch; // allocate new channel if (importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); if (data != nullptr) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); } else { ch = new ImageDataItem(this->m_ImageDescriptor, -1, data, importMemoryManagement == ManageMemory); } m_Channels[n] = ch; return ch; } unsigned int *mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete[] m_Dimensions; m_Dimensions = nullptr; } void mitk::Image::SetGeometry(BaseGeometry *aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if (aGeometry3D->GetImageGeometry() == false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is " "pixel-center-based! If it is not, you need to call " "Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream &os, itk::Indent indent) const { if (m_Initialized) { unsigned char i; os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for (i = 0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for (unsigned int ch = 0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os, indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry *geo = this->GetGeometry(); bool ret = false; if (geo) { const vnl_matrix_fixed &mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType ref = 0; for (short k = 0; k < 3; ++k) ref += mx[k][k]; ref /= 1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for (short i = 0; i < 3; ++i) { for (short j = 0; j < 3; ++j) { if (i != j) { if (std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } bool mitk::Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if (rightHandSide.GetDimension() != leftHandSide.GetDimension()) { if (verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide.GetDimension(), leftHandSide.GetDimension()); for (unsigned int i = 0; i < minDimensionality; ++i) { if (rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i)) { returnValue = false; if (verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "leftHandSide->GetDimension(" << i << ") is " << leftHandSide.GetDimension(i) << "rightHandSide->GetDimension(" << i << ") is " << rightHandSide.GetDimension(i); } } } // Pixeltype mitk::PixelType pixelTypeRightHandSide = rightHandSide.GetPixelType(); mitk::PixelType pixelTypeLeftHandSide = leftHandSide.GetPixelType(); if (!(pixelTypeRightHandSide == pixelTypeLeftHandSide)) { if (verbose) { MITK_INFO << "[( Image )] PixelType differs."; MITK_INFO << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString() << "rightHandSide is " << pixelTypeRightHandSide.GetTypeAsString(); } returnValue = false; } // Geometries if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) { MITK_INFO << "[( Image )] Geometries differ."; } returnValue = false; } // Pixel values - default mode [ 0 threshold in difference ] - // compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception + // compare only if all previous checks were successful, otherwise the ITK filter will throw an exception if (returnValue) { mitk::CompareImageDataFilter::Pointer compareFilter = mitk::CompareImageDataFilter::New(); compareFilter->SetInput(0, &rightHandSide); compareFilter->SetInput(1, &leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if ((!compareFilter->GetResult())) { returnValue = false; if (verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index 6ef243cb93..131eec028f 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -1,507 +1,507 @@ /*============================================================================ 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 "mitkLevelWindow.h" #include "mitkImage.h" #include "mitkImageSliceSelector.h" #include "mitkImageStatisticsHolder.h" #include void mitk::LevelWindow::EnsureConsistency() { // Check if total range is ok { if (m_RangeMin > m_RangeMax) std::swap(m_RangeMin, m_RangeMax); if (m_RangeMin == m_RangeMax) m_RangeMin = m_RangeMax - 1; } // Check if current window is ok { if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound <= m_RangeMin) m_LowerWindowBound = m_RangeMin; if (m_UpperWindowBound <= m_RangeMin) m_UpperWindowBound = m_RangeMin + 1; if (m_LowerWindowBound >= m_RangeMax) m_LowerWindowBound = m_RangeMax - 1; if (m_UpperWindowBound >= m_RangeMax) m_UpperWindowBound = m_RangeMax; if (m_LowerWindowBound == m_UpperWindowBound) { m_UpperWindowBound += 0.5; m_LowerWindowBound -= 0.5; m_UpperWindowBound = std::min(m_UpperWindowBound, m_RangeMax); m_LowerWindowBound = std::max(m_LowerWindowBound, m_RangeMin); } } } mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window) : m_LowerWindowBound(level - window / 2.0), m_UpperWindowBound(level + window / 2.0), m_RangeMin(-2048.0), m_RangeMax(4096.0), m_DefaultLowerBound(-2048.0), m_DefaultUpperBound(4096.0), m_IsFloatingImage(false), m_Fixed(false) { SetDefaultLevelWindow(level, window); SetLevelWindow(level, window, true); } mitk::LevelWindow::LevelWindow(const mitk::LevelWindow &levWin) : m_LowerWindowBound(levWin.GetLowerWindowBound()), m_UpperWindowBound(levWin.GetUpperWindowBound()), m_RangeMin(levWin.GetRangeMin()), m_RangeMax(levWin.GetRangeMax()), m_DefaultLowerBound(levWin.GetDefaultLowerBound()), m_DefaultUpperBound(levWin.GetDefaultUpperBound()), m_IsFloatingImage(levWin.IsFloatingValues()), m_Fixed(levWin.GetFixed()) { } mitk::LevelWindow::~LevelWindow() { } mitk::ScalarType mitk::LevelWindow::GetLevel() const { return (m_UpperWindowBound - m_LowerWindowBound) / 2.0 + m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetWindow() const { return (m_UpperWindowBound - m_LowerWindowBound); } mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const { return ((m_DefaultUpperBound + m_DefaultLowerBound) / 2.0); } mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const { return ((m_DefaultUpperBound - m_DefaultLowerBound)); } void mitk::LevelWindow::ResetDefaultLevelWindow() { SetLevelWindow(GetDefaultLevel(), GetDefaultWindow()); } mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const { return m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const { return m_UpperWindowBound; } void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window) { SetDefaultBoundaries((level - (window / 2.0)), (level + (window / 2.0))); } void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary) { SetWindowBounds((level - (window / 2.0)), (level + (window / 2.0)), expandRangesIfNecessary); } void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound, mitk::ScalarType upperBound, bool expandRangesIfNecessary) { if (IsFixed()) return; m_LowerWindowBound = lowerBound; m_UpperWindowBound = upperBound; if (expandRangesIfNecessary) { /* if caller is sure he wants exactly that level/window, we make sure the limits match */ if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound < m_RangeMin) { m_RangeMin = m_LowerWindowBound; } if (m_UpperWindowBound > m_RangeMax) { m_RangeMax = m_UpperWindowBound; } } EnsureConsistency(); } void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max) { if (IsFixed()) return; m_RangeMin = min; m_RangeMax = max; EnsureConsistency(); } void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up) { if (IsFixed()) return; m_DefaultLowerBound = low; m_DefaultUpperBound = up; // Check if default window is ok { if (m_DefaultLowerBound > m_DefaultUpperBound) std::swap(m_DefaultLowerBound, m_DefaultUpperBound); if (m_DefaultLowerBound == m_DefaultUpperBound) m_DefaultLowerBound--; } EnsureConsistency(); } void mitk::LevelWindow::SetToMaxWindowSize() { SetWindowBounds(m_RangeMin, m_RangeMax); } mitk::ScalarType mitk::LevelWindow::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetRangeMax() const { return m_RangeMax; } mitk::ScalarType mitk::LevelWindow::GetRange() const { return m_RangeMax - m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const { return m_DefaultUpperBound; } mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const { return m_DefaultLowerBound; } void mitk::LevelWindow::ResetDefaultRangeMinMax() { SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound); } /*! This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows: Default to taking the central image slice for quick analysis. Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and largest (maxValue) data value by traversing the pixel values only once. In the same scan it also computes the count of minValue values and maxValue values. After that a basic histogram with specific information about the -extrems is complete. +extremes is complete. If minValue == maxValue, the center slice is uniform and the above scan is repeated for the complete image, not just one slice Next, special cases of images with only 1, 2 or 3 distinct data values have hand assigned level window ranges. Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue]) For count(minValue) > 20% the smallest values are frequent and should be distinct from the min2ndValue and larger values (minValue may be std:min, may signify something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR For count(minValue) <= 20% the smallest values are not so important and can blend with the next ones => min(level window) = min2ndValue And analog for max(level window): count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR count(max2ndValue) < 20%: max(level window) = max2ndValue In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range In consequence the level window maximizes contrast with minimal amount of computation and does do useful things if the data contains std::min or std:max values or has only 1 or 2 or 3 data values. */ void mitk::LevelWindow::SetAuto(const mitk::Image *image, bool /*tryPicTags*/, bool guessByCentralSlice, unsigned selectedComponent) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; if (itk::IOComponentEnum::FLOAT == image->GetPixelType().GetComponentType() || itk::IOComponentEnum::DOUBLE == image->GetPixelType().GetComponentType()) { m_IsFloatingImage = true; } else { m_IsFloatingImage = false; } const mitk::Image *wholeImage = image; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); if (guessByCentralSlice) { sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); image = sliceSelector->GetOutput(); if (image == nullptr || !image->IsInitialized()) return; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); if (minValue == maxValue) { // guessByCentralSlice seems to have failed, lets look at all data image = wholeImage; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } } else { const_cast(image)->Update(); minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); for (unsigned int i = 1; i < image->GetDimension(3); ++i) { ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i, selectedComponent); if (minValue > minValueTemp) minValue = minValueTemp; ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i); if (maxValue < maxValueTemp) maxValue = maxValueTemp; ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i); if (min2ndValue > min2ndValueTemp) min2ndValue = min2ndValueTemp; ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i); if (max2ndValue > max2ndValueTemp) max2ndValue = max2ndValueTemp; } } // Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt if (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR && image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8) { - // the windows compiler complains about ambiguos 'pow' call, therefore static casting to (double, int) + // the windows compiler complains about ambiguous 'pow' call, therefore static casting to (double, int) if (minValue == -(pow((double)2.0, static_cast(image->GetPixelType().GetBpe() / 2)))) { minValue = min2ndValue; } } // End fix //// uniform image if (minValue == maxValue) { minValue = maxValue - 1; } else { // Due to bug #8690 level window now is no longer of fixed range by default but the range adapts according to // levelwindow interaction // This is done because the range should be a little bit larger from the beginning so that the scale doesn't start // to resize right from the beginning double additionalRange = 0.15 * (maxValue - minValue); minValue -= additionalRange; maxValue += additionalRange; } if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); size_t numPixelsInDataset = image->GetDimensions()[0]; for (decltype(image->GetDimension()) k = 1; k < image->GetDimension(); ++k) numPixelsInDataset *= image->GetDimensions()[k]; const auto minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); const auto maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); const auto minCountFraction = minCount / static_cast(numPixelsInDataset); const auto maxCountFraction = maxCount / static_cast(numPixelsInDataset); //// binary image if (min2ndValue == maxValue) { // noop; full range is fine } //// triple value image, put middle value in center of gray level ramp else if (min2ndValue == max2ndValue) { ScalarType minDelta = std::min(min2ndValue - minValue, maxValue - min2ndValue); minValue = min2ndValue - minDelta; maxValue = min2ndValue + minDelta; } // now we can assume more than three distict scalar values else { ScalarType innerRange = max2ndValue - min2ndValue; if (minCountFraction > 0.2) //// lots of min values -> make different from rest, but not miles away { ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange / 2.0; minValue = std::max(minValue, halfInnerRangeGapMinValue); } else //// few min values -> focus on innerRange { minValue = min2ndValue; } if (maxCountFraction > 0.2) //// lots of max values -> make different from rest { ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange / 2.0; maxValue = std::min(maxValue, halfInnerRangeGapMaxValue); } else //// few max values -> focus on innerRange { maxValue = max2ndValue; } } SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetToImageRange(const mitk::Image *image) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; ScalarType minValue = image->GetStatistics()->GetScalarValueMin(0); if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } ScalarType maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetFixed(bool fixed) { m_Fixed = fixed; } bool mitk::LevelWindow::GetFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFloatingValues() const { return m_IsFloatingImage; } void mitk::LevelWindow::SetFloatingValues(bool value) { m_IsFloatingImage = value; } bool mitk::LevelWindow::operator==(const mitk::LevelWindow &levWin) const { return mitk::Equal(this->m_RangeMin, levWin.m_RangeMin, mitk::sqrteps) && mitk::Equal(this->m_RangeMax, levWin.m_RangeMax, mitk::sqrteps) && mitk::Equal(this->m_DefaultLowerBound, levWin.m_DefaultLowerBound, mitk::sqrteps) && mitk::Equal(this->m_DefaultUpperBound, levWin.m_DefaultUpperBound, mitk::sqrteps) && mitk::Equal(this->m_LowerWindowBound, levWin.m_LowerWindowBound, mitk::sqrteps) && mitk::Equal(this->m_UpperWindowBound, levWin.m_UpperWindowBound, mitk::sqrteps) && m_Fixed == levWin.IsFixed() && m_IsFloatingImage == levWin.IsFloatingValues(); } bool mitk::LevelWindow::operator!=(const mitk::LevelWindow &levWin) const { return !((*this) == levWin); } mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin) { if (this == &levWin) { return *this; } else { m_RangeMin = levWin.GetRangeMin(); m_RangeMax = levWin.GetRangeMax(); m_LowerWindowBound = levWin.GetLowerWindowBound(); m_UpperWindowBound = levWin.GetUpperWindowBound(); m_DefaultLowerBound = levWin.GetDefaultLowerBound(); m_DefaultUpperBound = levWin.GetDefaultUpperBound(); m_Fixed = levWin.GetFixed(); m_IsFloatingImage = levWin.IsFloatingValues(); return *this; } } diff --git a/Modules/Core/src/DataManagement/mitkMemoryUtilities.cpp b/Modules/Core/src/DataManagement/mitkMemoryUtilities.cpp index 805d8464d1..c089328d1f 100755 --- a/Modules/Core/src/DataManagement/mitkMemoryUtilities.cpp +++ b/Modules/Core/src/DataManagement/mitkMemoryUtilities.cpp @@ -1,115 +1,115 @@ /*============================================================================ 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 "mitkMemoryUtilities.h" #include #if _MSC_VER #include #include #elif defined(__APPLE__) #include #include #include #include #else #include #include #endif /** * Returns the memory usage of the current process in bytes. * On linux, this refers to the virtual memory allocated by * the process (the VIRT column in top). * On windows, this refery to the size in bytes of the working * set pages (the "Speicherauslastung" column in the task manager). */ size_t mitk::MemoryUtilities::GetProcessMemoryUsage() { #if _MSC_VER size_t size = 0; DWORD pid = GetCurrentProcessId(); PROCESS_MEMORY_COUNTERS pmc; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (hProcess == nullptr) return 0; if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { size = pmc.WorkingSetSize; } CloseHandle(hProcess); return size; #elif defined(__APPLE__) struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); size_t size = t_info.virtual_size; return size; #else int size, res, shared, text, sharedLibs, stack, dirtyPages; if (!ReadStatmFromProcFS(&size, &res, &shared, &text, &sharedLibs, &stack, &dirtyPages)) return (size_t)size * getpagesize(); else return 0; #endif return 0; } /** - * Returns the total size of phyiscal memory in bytes + * Returns the total size of physical memory in bytes */ size_t mitk::MemoryUtilities::GetTotalSizeOfPhysicalRam() { #if _MSC_VER MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); GlobalMemoryStatusEx(&statex); return (size_t)statex.ullTotalPhys; #elif defined(__APPLE__) int mib[2]; int64_t physical_memory; mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; size_t length = sizeof(int64_t); sysctl(mib, 2, &physical_memory, &length, nullptr, 0); return physical_memory; #else struct sysinfo info; if (!sysinfo(&info)) return info.totalram * info.mem_unit; else return 0; #endif } #ifndef _MSC_VER #ifndef __APPLE__ int mitk::MemoryUtilities::ReadStatmFromProcFS( int *size, int *res, int *shared, int *text, int *sharedLibs, int *stack, int *dirtyPages) { int ret = 0; FILE *f; f = fopen("/proc/self/statm", "r"); if (f) { size_t ignored = fscanf(f, "%d %d %d %d %d %d %d", size, res, shared, text, sharedLibs, stack, dirtyPages); ++ignored; fclose(f); } else { ret = -1; } return ret; } #endif #endif diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp index 73ea650550..1850b8dab4 100644 --- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp @@ -1,972 +1,972 @@ /*============================================================================ 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 "mitkPlaneGeometry.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkPlaneOperation.h" #include #include #include namespace mitk { PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry(nullptr) { Initialize(); } PlaneGeometry::~PlaneGeometry() {} PlaneGeometry::PlaneGeometry(const PlaneGeometry &other) : Superclass(other), m_ReferenceGeometry(other.m_ReferenceGeometry) { } bool PlaneGeometry::CheckRotationMatrix(mitk::AffineTransform3D *transform, double epsilon) { bool rotation = true; auto matrix = transform->GetMatrix().GetVnlMatrix(); matrix.normalize_columns(); auto det = vnl_determinant(matrix); if (fabs(det-1.0) > epsilon) { MITK_WARN << "Invalid rotation matrix! Determinant != 1 (" << det << ")"; rotation = false; } vnl_matrix_fixed id; id.set_identity(); auto should_be_id = matrix*matrix.transpose(); should_be_id -= id; auto max = should_be_id.absolute_value_max(); if (max > epsilon) { MITK_WARN << "Invalid rotation matrix! R*R^T != ID. Max value: " << max << " (should be 0)"; rotation = false; } return rotation; } void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) { this->CheckRotationMatrix(transform); } void PlaneGeometry::CheckBounds(const BoundingBox::BoundsArrayType &bounds) { // error: unused parameter 'bounds' // this happens in release mode, where the assert macro is defined empty // hence we "use" the parameter: (void)bounds; // currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0] == 0); assert(bounds[2] == 0); // the unit rectangle must be two-dimensional assert(bounds[1] > 0); assert(bounds[3] > 0); } void PlaneGeometry::IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const { pt_mm[0] = GetExtentInMM(0) / GetExtent(0) * pt_units[0]; pt_mm[1] = GetExtentInMM(1) / GetExtent(1) * pt_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const { pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::IndexToWorld(const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use " "PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0]; vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use " "PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex(const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated, bool top) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0, 0) = spacing[0]; vnlmatrix(1, 1) = spacing[1]; vnlmatrix(2, 2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, mitk::ScalarType height, const AffineTransform3D *transform /* = nullptr */, PlaneGeometry::PlaneOrientation planeorientation /* = Axial */, mitk::ScalarType zPosition /* = 0 */, bool frontside /* = true */, bool rotated /* = false */, bool top /* = true */) { Superclass::Initialize(); /// construct standard view. // We define at the moment "frontside" as: axial from above, // coronal from front (nose), sagittal from right. // TODO: Double check with medicals doctors or radiologists [ ]. // We define the orientation in patient's view, e.g. LAI is in a axial cut // (parallel to the triangle ear-ear-nose): // first axis: To the left ear of the patient // seecond axis: To the nose of the patient // third axis: To the legs of the patient. // Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior // (AKA caudal/cranial). // We note on all cases in the following switch block r.h. for right handed // or l.h. for left handed to describe the different cases. // However, which system is chosen is defined at the end of the switch block. // CAVE / be careful: the vectors right and bottom are relative to the plane // and do NOT describe e.g. the right side of the patient. Point3D origin; /** Bottom means downwards, DV means Direction Vector. Both relative to the image! */ VnlVector rightDV(3), bottomDV(3); /** Origin of this plane is by default a zero vector and implicitly in the top-left corner: */ origin.Fill(0); /** This is different to all definitions in MITK, except the QT mouse clicks. * But it is like this here and we don't want to change a running system. * Just be aware, that IN THIS FUNCTION we define the origin at the top left (e.g. your screen). */ /** NormalDirection defines which axis (i.e. column index in the transform matrix) * is perpendicular to the plane: */ int normalDirection; switch (planeorientation) // Switch through our limited choice of standard planes: { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: if (frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose. { if (rotated == false) /** Origin in the top-left corner, x=[1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], * origin=[0,0,zpos]: LAI (r.h.) * * 0---rightDV----> | * | | * | Picture of a finite, rectangular plane | * | ( insert LOLCAT-scan here ^_^ ) | * | | * v _________________________________________| * */ { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[w,h,zpos]: RPI (r.h.) { // Caveat emptor: Still using top-left as origin of index coordinate system! FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient. { if (rotated == false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[0,h,zpos]: LPS (r.h.) { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system. break; case Coronal: // Coronal=Frontal plane; cuts through patient's ear-ear-heel-heel: if (frontside) { if (rotated == false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.) { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.) { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; // Normal vector = posterior direction. break; case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face. if (frontside) { if (rotated == false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.) { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.) { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system. break; default: itkExceptionMacro("unknown PlaneOrientation"); } VnlVector normal(3); FillVector3D(normal, 0, 0, 0); normal[normalDirection] = top ? 1 : -1; if ( transform != nullptr ) { origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ).as_ref(); bottomDV = transform->TransformVector( bottomDV ).as_ref(); normal = transform->TransformVector( normal ).as_ref(); } ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); AffineTransform3D::Pointer planeTransform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV.as_ref()); matrix.GetVnlMatrix().set_column(1, bottomDV.as_ref()); matrix.GetVnlMatrix().set_column(2, normal.as_ref()); planeTransform->SetMatrix(matrix); planeTransform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); this->SetIndexToWorldTransform(planeTransform); this->SetOrigin(origin); } std::vector< int > PlaneGeometry::CalculateDominantAxes(mitk::AffineTransform3D::MatrixType::InternalMatrixType& rotation_matrix) { std::vector< int > axes; bool dominant_axis_error = false; for (int i = 0; i < 3; ++i) { int dominantAxis = itk::Function::Max3( rotation_matrix[0][i], rotation_matrix[1][i], rotation_matrix[2][i] ); for (int j=0; jSetReferenceGeometry(geometry3D); ScalarType width, height; // 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(); /// The index of the sagittal, coronal and axial axes in the reference geometry. auto axes = CalculateDominantAxes(inverseMatrix); /// The direction of the sagittal, coronal and axial axes in the reference geometry. /// +1 means that the direction is straight, i.e. greater index translates to greater /// world coordinate. -1 means that the direction is inverted. int directions[3]; ScalarType extents[3]; ScalarType spacings[3]; for (int i=0; i<3; ++i) { int dominantAxis = axes.at(i); directions[i] = itk::Function::Sign(inverseMatrix[dominantAxis][i]); extents[i] = geometry3D->GetExtent(dominantAxis); spacings[i] = geometry3D->GetSpacing()[dominantAxis]; } // matrix(column) = inverseTransformMatrix(row) * flippedAxes * spacing matrix[0][0] = inverseMatrix[axes[0]][0] * directions[0] * spacings[0]; matrix[1][0] = inverseMatrix[axes[0]][1] * directions[0] * spacings[0]; matrix[2][0] = inverseMatrix[axes[0]][2] * directions[0] * spacings[0]; matrix[0][1] = inverseMatrix[axes[1]][0] * directions[1] * spacings[1]; matrix[1][1] = inverseMatrix[axes[1]][1] * directions[1] * spacings[1]; matrix[2][1] = inverseMatrix[axes[1]][2] * directions[1] * spacings[1]; matrix[0][2] = inverseMatrix[axes[2]][0] * directions[2] * spacings[2]; matrix[1][2] = inverseMatrix[axes[2]][1] * directions[2] * spacings[2]; matrix[2][2] = inverseMatrix[axes[2]][2] * directions[2] * spacings[2]; /// The "world origin" is the corner with the lowest physical coordinates. /// We use it as a reference point so that we get the correct anatomical /// orientations. Point3D worldOrigin = geometry3D->GetOrigin(); for (int i = 0; i < 3; ++i) { /// The distance of the plane origin from the world origin in voxels. double offset = directions[i] > 0 ? 0.0 : extents[i]; if (geometry3D->GetImageGeometry()) { offset += directions[i] * 0.5; } for (int j = 0; j < 3; ++j) { worldOrigin[j] -= offset * matrix[j][i]; } } switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: width = extents[0]; height = extents[1]; break; case Coronal: width = extents[0]; height = extents[2]; break; case Sagittal: width = extents[1]; height = extents[2]; break; default: itkExceptionMacro("unknown PlaneOrientation"); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(worldOrigin.GetVectorFromOrigin()); InitializeStandardPlane( width, height, transform, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated) { /// The index of the sagittal, coronal and axial axes in world coordinate system. int worldAxis; switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: worldAxis = 2; break; case Coronal: worldAxis = 1; break; case Sagittal: worldAxis = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::ConstPointer affineTransform = geometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix = affineTransform->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); /// The index of the sagittal, coronal and axial axes in the reference geometry. int dominantAxis = CalculateDominantAxes(inverseMatrix).at(worldAxis); ScalarType zPosition = top ? 0.5 : geometry3D->GetExtent(dominantAxis) - 0.5; InitializeStandardPlane(geometry3D, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { ScalarType width = rightVector.two_norm(); ScalarType height = downVector.two_norm(); InitializeStandardPlane(width, height, rightVector, downVector, spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // spacing with 1 or 3 negative components could still make it lefthanded. if (spacing != nullptr) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); this->SetIndexToWorldTransform(transform); } void PlaneGeometry::InitializePlane(const Point3D &origin, const Vector3D &normal) { VnlVector rightVectorVnl(3), downVectorVnl; if (Equal(normal[1], 0.0f) == false) { FillVector3D(rightVectorVnl, 1.0f, -normal[0] / normal[1], 0.0f); rightVectorVnl.normalize(); } else { FillVector3D(rightVectorVnl, 0.0f, 1.0f, 0.0f); } downVectorVnl = vnl_cross_3d(normal.GetVnlVector(), rightVectorVnl); downVectorVnl.normalize(); // Crossproduct vnl_cross_3d is always righthanded. InitializeStandardPlane(rightVectorVnl, downVectorVnl); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness /* = 1.0 */) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // a negative thickness could still make it lefthanded. AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref(); } ScalarType PlaneGeometry::DistanceFromPlane(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); } ScalarType PlaneGeometry::SignedDistance(const Point3D &pt3d_mm) const { return SignedDistanceFromPlane(pt3d_mm); } bool PlaneGeometry::IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox) const { if (considerBoundingBox) { Point3D pt3d_units; BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); } else return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine(const PlaneGeometry* plane, Line3D& crossline) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct(normal, planeNormal); if (direction.GetSquaredNorm() < eps) return false; crossline.SetDirection(direction); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = (d1 - d2 * N1dN2) / determinant; double c2 = (d2 - d1 * N1dN2) / determinant; Vector3D p = normal * c1 + planeNormal * c2; crossline.GetPoint()[0] = p.GetVnlVector()[0]; crossline.GetPoint()[1] = p.GetVnlVector()[1]; crossline.GetPoint()[2] = p.GetVnlVector()[2]; return true; } unsigned int PlaneGeometry::IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const { Line3D crossline; if (this->IntersectionLine(plane, crossline) == false) return 0; Point2D point2; Vector2D direction2; this->Map(crossline.GetPoint(), point2); this->Map(crossline.GetPoint(), crossline.GetDirection(), direction2); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo); } double PlaneGeometry::Angle(const PlaneGeometry *plane) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle(const Line3D &line) const { return vnl_math::pi_over_2 - angle(line.GetDirection().GetVnlVector(), GetMatrixColumn(2)); } bool PlaneGeometry::IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam(const Line3D &line, double &t) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; return true; } bool PlaneGeometry::IsParallel(const PlaneGeometry *plane) const { return ((Angle(plane) < 10.0 * mitk::sqrteps) || (Angle(plane) > (vnl_math::pi - 10.0 * sqrteps))); } bool PlaneGeometry::IsOnPlane(const Point3D &point) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane(const Line3D &line) const { return ((Distance(line.GetPoint()) < eps) && (Distance(line.GetPoint2()) < eps)); } bool PlaneGeometry::IsOnPlane(const PlaneGeometry *plane) const { return (IsParallel(plane) && (Distance(plane->GetOrigin()) < eps)); } Point3D PlaneGeometry::ProjectPointOntoPlane(const Point3D &pt) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane(pt) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation(Operation *operation) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpORIENT: { auto *planeOp = dynamic_cast(operation); if (planeOp == nullptr) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D(defaultVector, 0.0, 0.0, 1.0); Vector3D rotationAxis = itk::CrossProduct(orientationVector, defaultVector); // double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2((double)rotationAxis.GetNorm(), (double)(orientationVector * defaultVector)); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate(center[0], center[1], center[2]); transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]); transform->Translate(-center[0], -center[1], -center[2]); break; } case OpRESTOREPLANEPOSITION: { auto *op = dynamic_cast(operation); if (op == nullptr) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0, 1}; this->SetBounds(bounds); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation(operation); transform->Delete(); return; } this->SetVtkMatrixDeepCopy(transform); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl; os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0); pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1); pt3d_units[2] = 0; return this->GetBoundingBox()->IsInside(pt3d_units); } void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { - // pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event) + // pt2d_mm is measured from the origin of the world geometry (at least it called form BaseRendere::Mouse...Event) Point3D pt3d_units; pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0)); pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1)); pt3d_units[2] = 0; - // pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm + // pt3d_units is a continuous index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm // to index units. // pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); // now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as // GetITW->Transform... } void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) { ScalarType bounds[6] = {0, width, 0, height, 0, 1}; ScalarType extent, newextentInMM; if (GetExtent(0) > 0) { extent = GetExtent(0); if (width > extent) newextentInMM = GetExtentInMM(0) / width * extent; else newextentInMM = GetExtentInMM(0) * extent / width; SetExtentInMM(0, newextentInMM); } if (GetExtent(1) > 0) { extent = GetExtent(1); if (width > extent) newextentInMM = GetExtentInMM(1) / height * extent; else newextentInMM = GetExtentInMM(1) * extent / height; SetExtentInMM(1, newextentInMM); } SetBounds(bounds); } bool PlaneGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt3d_units[2] = 0; projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); return this->GetBoundingBox()->IsInside(pt3d_units); } bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); return true; } bool PlaneGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; Superclass::WorldToIndex(atPt3d_mm, pt3d_units); return this->GetBoundingBox()->IsInside(pt3d_units); } bool PlaneGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { Point2D pt2d_mm_start, pt2d_mm_end; Point3D pt3d_mm_end; bool inside = Map(atPt3d_mm, pt2d_mm_start); pt3d_mm_end = atPt3d_mm + vec3d_mm; inside &= Map(pt3d_mm_end, pt2d_mm_end); vec2d_mm = pt2d_mm_end - pt2d_mm_start; return inside; } void PlaneGeometry::Map(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D & /*vec2d_mm*/, mitk::Vector3D & /*vec3d_mm*/) const { //@todo implement parallel to the other Map method! assert(false); } void PlaneGeometry::SetReferenceGeometry(const mitk::BaseGeometry *geometry) { m_ReferenceGeometry = geometry; } const mitk::BaseGeometry *PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; } bool PlaneGeometry::HasReferenceGeometry() const { return (m_ReferenceGeometry != nullptr); } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkPointSet.cpp b/Modules/Core/src/DataManagement/mitkPointSet.cpp index 7a05bb3bc0..d9c3915ef6 100755 --- a/Modules/Core/src/DataManagement/mitkPointSet.cpp +++ b/Modules/Core/src/DataManagement/mitkPointSet.cpp @@ -1,965 +1,965 @@ /*============================================================================ 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 "mitkPointSet.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include #include namespace mitk { itkEventMacroDefinition(PointSetEvent, itk::AnyEvent); itkEventMacroDefinition(PointSetMoveEvent, PointSetEvent); itkEventMacroDefinition(PointSetSizeChangeEvent, PointSetEvent); itkEventMacroDefinition(PointSetAddEvent, PointSetSizeChangeEvent); itkEventMacroDefinition(PointSetRemoveEvent, PointSetSizeChangeEvent); itkEventMacroDefinition(PointSetExtendTimeRangeEvent, PointSetEvent); } mitk::PointSet::PointSet() : m_CalculateBoundingBox(true) { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other), m_PointSetSeries(other.GetPointSetSeriesSize()), m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize(1); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData(pointData); m_CalculateBoundingBox = false; Superclass::InitializeTimeGeometry(1); m_Initialized = true; m_EmptyPointsContainer = DataType::PointsContainer::New(); } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand(unsigned int timeSteps) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if (timeSteps > oldSize) { Superclass::Expand(timeSteps); m_PointSetSeries.resize(timeSteps); for (unsigned int i = oldSize; i < timeSteps; ++i) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData(pointData); } // if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent(PointSetExtendTimeRangeEvent()); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize(unsigned int t) const { if (t < m_PointSetSeries.size()) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet(int t) const { if (t < (int)m_PointSetSeries.size()) { return m_PointSetSeries[t]; } else { return nullptr; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin(int t) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::End(int t) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::GetMaxId(int t) { if ((unsigned int)t >= m_PointSetSeries.size()) { return m_EmptyPointsContainer->End(); } return this->Begin(t) == this->End(t) ? this->End(t) : --End(t); } int mitk::PointSet::SearchPoint(Point3D point, ScalarType distance, int t) const { if (t >= (int)m_PointSetSeries.size()) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill(0); PointType indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); - // Searching the first point in the Set, that is +- distance far away fro + // Searching the first point in the Set, that is +- distance far away from // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for (it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i) { bool ok = m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(it->Index(), &out); if (!ok) { return -1; } else if (indexPoint == out) // if totally equal { return it->Index(); } // distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if (dist < bestDist) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint(PointIdentifier id, int t) const { PointType out; out.Fill(0); if ((unsigned int)t >= m_PointSetSeries.size()) { return out; } if (m_PointSetSeries[t]->GetPoints()->IndexExists(id)) { m_PointSetSeries[t]->GetPoint(id, &out); this->GetGeometry(t)->IndexToWorld(out, out); return out; } else { return out; } } bool mitk::PointSet::GetPointIfExists(PointIdentifier id, PointType *point, int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return false; } if (m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point)) { this->GetGeometry(t)->IndexToWorld(*point, *point); return true; } else { return false; } } void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, int t) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t) { if ((unsigned int)t < m_PointSetSeries.size()) { mitk::Point3D indexPoint; mitk::BaseGeometry *tempGeometry = this->GetGeometry(t); if (tempGeometry == nullptr) { MITK_INFO << __FILE__ << ", l." << __LINE__ << ": GetGeometry of " << t << " returned nullptr!" << std::endl; return; } tempGeometry->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->GetPoints()->InsertElement(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } mitk::PointSet::PointIdentifier mitk::PointSet::InsertPoint(PointType point, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); PointIdentifier id = 0; if (m_PointSetSeries[t]->GetNumberOfPoints() > 0) { PointsIterator it = --End(t); id = it.Index(); ++id; } mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); return id; } bool mitk::PointSet::RemovePointIfExists(PointIdentifier id, int t) { if ((unsigned int)t < m_PointSetSeries.size()) { DataType *pointSet = m_PointSetSeries[t]; PointsContainer *points = pointSet->GetPoints(); PointDataContainer *pdata = pointSet->GetPointData(); bool exists = points->IndexExists(id); if (exists) { points->DeleteIndex(id); pdata->DeleteIndex(id); return true; } } return false; } mitk::PointSet::PointsIterator mitk::PointSet::RemovePointAtEnd(int t) { if ((unsigned int)t < m_PointSetSeries.size()) { DataType *pointSet = m_PointSetSeries[t]; PointsContainer *points = pointSet->GetPoints(); PointDataContainer *pdata = pointSet->GetPointData(); PointsIterator bit = points->Begin(); PointsIterator eit = points->End(); if (eit != bit) { PointsContainer::ElementIdentifier id = (--eit).Index(); points->DeleteIndex(id); pdata->DeleteIndex(id); PointsIterator eit2 = points->End(); return points->empty()? eit2 : --eit2; } else { return eit; } } return m_EmptyPointsContainer->End(); } bool mitk::PointSet::SwapPointPosition(PointIdentifier id, bool moveUpwards, int t) { if (IndexExists(id, t)) { PointType point = GetPoint(id, t); if (moveUpwards) { // up if (IndexExists(id - 1, t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id - 1, point, t); this->Modified(); return true; } } else { // down if (IndexExists(id + 1, t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id + 1, point, t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists(int position, int t) const { if ((unsigned int)t < m_PointSetSeries.size()) { return m_PointSetSeries[t]->GetPoints()->IndexExists(position); } else { return false; } } bool mitk::PointSet::GetSelectInfo(int position, int t) const { if (this->IndexExists(position, t)) { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[t]->GetPointData(position, &pointData); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo(int position, bool selected, int t) { if (this->IndexExists(position, t)) { // timeStep to ms TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint(t); // point Point3D point = this->GetPoint(position, t); std::unique_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position)); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position)); } this->ExecuteOperation(op.get()); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo(int position, int t) const { if (this->IndexExists(position, t)) { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[t]->GetPointData(position, &pointData); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected(int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return 0; } int numberOfSelected = 0; PointDataIterator it; for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint(int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return -1; } PointDataIterator it; for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++) { if (it->Value().selected == true) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation(Operation *operation) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if (pointOp) { timeStep = this->GetTimeGeometry()->TimePointToTimeStep(pointOp->GetTimeInMS()); } if (timeStep < 0) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT: // inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); if (timeStep >= (int)this->GetTimeSteps()) this->Expand(timeStep + 1); // transfer from world to index coordinates mitk::BaseGeometry *geometry = this->GetGeometry(timeStep); if (geometry == nullptr) { MITK_INFO << "GetGeometry returned nullptr!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType()}; m_PointSetSeries[timeStep]->GetPointData()->InsertElement(position, pointData); this->Modified(); // boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent(PointSetAddEvent()); this->OnPointSetChange(); } break; case OpMOVE: // moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); // transfer from world to index coordinates this->GetGeometry(timeStep)->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if (!m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData)) { m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); } this->OnPointSetChange(); this->Modified(); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent(PointSetMoveEvent()); } break; case OpREMOVE: // removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent(PointSetRemoveEvent()); } break; case OpSELECTPOINT: // select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT: // unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the // container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } // to tell the mappers, that the data is modified and has to be updated // only call modified if anything is done, so call in cases // this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object *)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // TimeGeometry *timeGeometry = GetTimeGeometry(); if (timeGeometry->CountTimeSteps() != m_PointSetSeries.size()) { itkExceptionMacro(<< "timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with " "correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for (unsigned int i = 0; i < m_PointSetSeries.size(); ++i) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if (m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull)) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for (unsigned int j = 0; j < 3; ++j) { if (itkBounds[j * 2 + 1] - itkBounds[j * 2] < 1.0) { BoundingBox::CoordRepType center = (itkBounds[j * 2] + itkBounds[j * 2 + 1]) / 2.0; itkBounds[j * 2] = center - 0.5; itkBounds[j * 2 + 1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeGeometry()->Update(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject *) { } void mitk::PointSet::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (auto it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer *points = ps->GetPoints(); MeshType::PointDataContainer *datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } bool mitk::PointSet::PointDataType::operator==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } bool mitk::Equal(const mitk::PointSet *leftHandSide, const mitk::PointSet *rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, " "mitk::ScalarType eps, bool verbose ) does not work with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose, checkGeometry); } bool mitk::Equal(const mitk::PointSet &leftHandSide, const mitk::PointSet &rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry) { bool result = true; // If comparing point sets from file, you must not compare the geometries, as they are not saved. In other cases, you // do need to check them. if (checkGeometry) { if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) MITK_INFO << "[( PointSet )] Geometries differ."; result = false; } } if (leftHandSide.GetSize() != rightHandSide.GetSize()) { if (verbose) MITK_INFO << "[( PointSet )] Number of points differ."; result = false; } else { // if the size is equal, we compare the point values mitk::Point3D pointLeftHandSide; mitk::Point3D pointRightHandSide; int numberOfIncorrectPoints = 0; // Iterate over both pointsets in order to compare all points pair-wise mitk::PointSet::PointsConstIterator end = leftHandSide.End(); for (mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(), pointSetIteratorRight = rightHandSide.Begin(); pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) // iterate simultaneously over both sets { pointLeftHandSide = pointSetIteratorLeft.Value(); pointRightHandSide = pointSetIteratorRight.Value(); if (!mitk::Equal(pointLeftHandSide, pointRightHandSide, eps, verbose)) { if (verbose) MITK_INFO << "[( PointSet )] Point values are different."; result = false; numberOfIncorrectPoints++; } } if ((numberOfIncorrectPoints > 0) && verbose) { MITK_INFO << numberOfIncorrectPoints << " of a total of " << leftHandSide.GetSize() << " points are different."; } } return result; } diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index 994d5af78c..d15eac3f6e 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -1,376 +1,376 @@ /*============================================================================ 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 "mitkPropertyList.h" #include "mitkNumericTypes.h" #include "mitkProperties.h" #include "mitkStringProperty.h" mitk::BaseProperty::ConstPointer mitk::PropertyList::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second.GetPointer(); else return nullptr; }; std::vector mitk::PropertyList::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty() || includeDefaultContext) { for (const auto &property : this->m_Properties) propertyKeys.push_back(property.first); } return propertyKeys; }; std::vector mitk::PropertyList::GetPropertyContextNames() const { return std::vector(); }; mitk::BaseProperty *mitk::PropertyList::GetProperty(const std::string &propertyKey) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second; else return nullptr; } mitk::BaseProperty * mitk::PropertyList::GetNonConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { return this->GetProperty(propertyKey); } void mitk::PropertyList::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!property) return; // make sure that BaseProperty*, which may have just been created and never been // assigned to a SmartPointer, is registered/unregistered properly. If we do not // do that, it will a) not deleted in case it is identical to the old one or // b) possibly deleted when temporarily added to a smartpointer somewhere below. BaseProperty::Pointer tmpSmartPointerToProperty = property; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { // yes // is the property contained in the list identical to the new one? if (it->second->operator==(*property)) { // yes? do nothing and return. return; } if (it->second->AssignProperty(*property)) { - // The assignment was successfull + // The assignment was successful this->Modified(); } else { MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first << " of type " << it->second->GetNameOfClass() << " to a property with different type " << property->GetNameOfClass() << "." << " Use ReplaceProperty() instead." << std::endl; } return; } // no? add it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); this->Modified(); } void mitk::PropertyList::ReplaceProperty(const std::string &propertyKey, BaseProperty *property) { if (!property) return; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); } // no? add/replace it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); Modified(); } void mitk::PropertyList::RemoveProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); Modified(); } } mitk::PropertyList::PropertyList() { } mitk::PropertyList::PropertyList(const mitk::PropertyList &other) : itk::Object() { for (auto i = other.m_Properties.cbegin(); i != other.m_Properties.cend(); ++i) { m_Properties.insert(std::make_pair(i->first, i->second->Clone())); } } mitk::PropertyList::~PropertyList() { Clear(); } /** * Consider the list as changed when any of the properties has changed recently. */ itk::ModifiedTimeType mitk::PropertyList::GetMTime() const { for (auto it = m_Properties.cbegin(); it != m_Properties.cend(); ++it) { if (it->second.IsNull()) { itkWarningMacro(<< "Property '" << it->first << "' contains nothing (nullptr)."); continue; } if (Superclass::GetMTime() < it->second->GetMTime()) { Modified(); break; } } return Superclass::GetMTime(); } bool mitk::PropertyList::DeleteProperty(const std::string &propertyKey) { auto it = m_Properties.find(propertyKey); if (it != m_Properties.end()) { it->second = nullptr; m_Properties.erase(it); Modified(); return true; } return false; } void mitk::PropertyList::Clear() { auto it = m_Properties.begin(), end = m_Properties.end(); while (it != end) { it->second = nullptr; ++it; } m_Properties.clear(); } itk::LightObject::Pointer mitk::PropertyList::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace) { if (pList) { const PropertyMap *propertyMap = pList->GetMap(); for (auto iter = propertyMap->cbegin(); // m_PropertyList is created in the constructor, so we don't check it here iter != propertyMap->cend(); ++iter) { const std::string key = iter->first; BaseProperty *value = iter->second; if (replace) { ReplaceProperty(key.c_str(), value); } else { SetProperty(key.c_str(), value); } } } } bool mitk::PropertyList::GetBoolProperty(const char *propertyKey, bool &boolValue) const { BoolProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { boolValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, boolValue); } bool mitk::PropertyList::GetIntProperty(const char *propertyKey, int &intValue) const { IntProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { intValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, intValue); } bool mitk::PropertyList::GetFloatProperty(const char *propertyKey, float &floatValue) const { FloatProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { floatValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, floatValue); } bool mitk::PropertyList::GetStringProperty(const char *propertyKey, std::string &stringValue) const { StringProperty *sp = dynamic_cast(GetProperty(propertyKey)); if (sp != nullptr) { stringValue = sp->GetValue(); return true; } return false; } void mitk::PropertyList::SetIntProperty(const char *propertyKey, int intValue) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::PropertyList::SetBoolProperty(const char *propertyKey, bool boolValue) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::PropertyList::SetFloatProperty(const char *propertyKey, float floatValue) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::PropertyList::SetStringProperty(const char *propertyKey, const char *stringValue) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::PropertyList::Set(const char *propertyKey, bool boolValue) { this->SetBoolProperty(propertyKey, boolValue); } void mitk::PropertyList::Set(const char *propertyKey, int intValue) { this->SetIntProperty(propertyKey, intValue); } void mitk::PropertyList::Set(const char *propertyKey, float floatValue) { this->SetFloatProperty(propertyKey, floatValue); } void mitk::PropertyList::Set(const char *propertyKey, double doubleValue) { this->SetDoubleProperty(propertyKey, doubleValue); } void mitk::PropertyList::Set(const char *propertyKey, const char *stringValue) { this->SetStringProperty(propertyKey, stringValue); } void mitk::PropertyList::Set(const char *propertyKey, const std::string &stringValue) { this->SetStringProperty(propertyKey, stringValue.c_str()); } bool mitk::PropertyList::Get(const char *propertyKey, bool &boolValue) const { return this->GetBoolProperty(propertyKey, boolValue); } bool mitk::PropertyList::Get(const char *propertyKey, int &intValue) const { return this->GetIntProperty(propertyKey, intValue); } bool mitk::PropertyList::Get(const char *propertyKey, float &floatValue) const { return this->GetFloatProperty(propertyKey, floatValue); } bool mitk::PropertyList::Get(const char *propertyKey, double &doubleValue) const { return this->GetDoubleProperty(propertyKey, doubleValue); } bool mitk::PropertyList::Get(const char *propertyKey, std::string &stringValue) const { return this->GetStringProperty(propertyKey, stringValue); } bool mitk::PropertyList::GetDoubleProperty(const char *propertyKey, double &doubleValue) const { DoubleProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { doubleValue = gp->GetValue(); return true; } return false; } void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubleValue) { SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } diff --git a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp index 58f507e211..3658e8cb66 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -1,871 +1,871 @@ /*============================================================================ 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 "mitkPropertyRelationRuleBase.h" #include #include #include #include #include #include #include #include bool mitk::PropertyRelationRuleBase::IsAbstract() const { return true; } bool mitk::PropertyRelationRuleBase::IsSourceCandidate(const IPropertyProvider *owner) const { return owner != nullptr; } bool mitk::PropertyRelationRuleBase::IsDestinationCandidate(const IPropertyProvider *owner) const { return owner != nullptr; } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRootKeyPath() { return PropertyKeyPath().AddElement("MITK").AddElement("Relations"); } bool mitk::PropertyRelationRuleBase::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID(); } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath(const std::string propName, const InstanceIDType& instanceID) { auto path = GetRootKeyPath(); if (instanceID.empty()) { path.AddAnyElement(); } else { path.AddElement(instanceID); } if (!propName.empty()) { path.AddElement(propName); } return path; } std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const { return PropertyKeyPathToPropertyRegEx(GetRIIPropertyKeyPath(propName, instanceID)); } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath(const InstanceIDType& instanceID) { return GetRIIPropertyKeyPath("relationUID", instanceID); } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath(const InstanceIDType& instanceID) { return GetRIIPropertyKeyPath("ruleID", instanceID); } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath(const InstanceIDType& instanceID) { return GetRIIPropertyKeyPath("destinationUID", instanceID); } //workaround until T24729 is done. Please remove if T24728 is done //then could directly use owner->GetPropertyKeys() again. std::vector mitk::PropertyRelationRuleBase::GetPropertyKeys(const mitk::IPropertyProvider *owner) { std::vector keys; auto sourceCasted = dynamic_cast(owner); if (sourceCasted) { auto sourceData = sourceCasted->GetData(); if (sourceData) { keys = sourceData->GetPropertyKeys(); } else { keys = sourceCasted->GetPropertyKeys(); } } else { keys = owner->GetPropertyKeys(); } return keys; } //end workaround for T24729 bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const { return !this->GetExistingRelations(owner).empty(); } bool mitk::PropertyRelationRuleBase::HasRelation( const IPropertyProvider* source, const IPropertyProvider* destination, RelationType requiredRelation) const { auto relTypes = this->GetRelationTypes(source, destination); if (requiredRelation == RelationType::None) { return !relTypes.empty(); } RelationVectorType allowedTypes = { RelationType::Complete }; if (requiredRelation == RelationType::Data) { allowedTypes.emplace_back(RelationType::Data); } else if (requiredRelation == RelationType::ID) { allowedTypes.emplace_back(RelationType::ID); } return relTypes.end() != std::find_first_of(relTypes.begin(), relTypes.end(), allowedTypes.begin(), allowedTypes.end()); } mitk::PropertyRelationRuleBase::RelationVectorType mitk::PropertyRelationRuleBase::GetRelationTypes( const IPropertyProvider* source, const IPropertyProvider* destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed owner pointer is NULL"; } auto instanceIDs_IDLayer = this->GetInstanceID_IDLayer(source, destination); auto relIDs_dataLayer = this->GetRelationUIDs_DataLayer(source, destination, {}); if (relIDs_dataLayer.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Relation UID: " << relIDs_dataLayer.front().first; } bool hasComplete = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); bool hasID = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relIDs_dataLayer.end() == std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); bool hasData = relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return instanceIDs_IDLayer.end() == std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); RelationVectorType result; if (hasData) { result.emplace_back(RelationType::Data); } if (hasID) { result.emplace_back(RelationType::ID); } if (hasComplete) { result.emplace_back(RelationType::Complete); } return result; } mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( const IPropertyProvider *source, RelationType layer) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } RelationUIDVectorType relationUIDs; InstanceIDVectorType instanceIDs; if (layer != RelationType::Data) { auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); auto regEx = std::regex(ruleIDRegExStr); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto& key : keys) { if (std::regex_match(key, regEx)) { auto idProp = source->GetConstProperty(key); auto ruleID = idProp->GetValueAsString(); if (this->IsSupportedRuleID(ruleID)) { auto instanceID = this->GetInstanceIDByPropertyName(key); instanceIDs.emplace_back(instanceID); relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } } } } if (layer == RelationType::ID) { return relationUIDs; } DataRelationUIDVectorType relationUIDandRuleID_Data; if (layer != RelationType::ID) { relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source, nullptr, instanceIDs); } RelationUIDVectorType relationUIDs_Data; std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relationUIDs_Data), [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); if (layer == RelationType::Data) { return relationUIDs_Data; } std::sort(relationUIDs.begin(), relationUIDs.end()); std::sort(relationUIDs_Data.begin(), relationUIDs_Data.end()); RelationUIDVectorType result; if (layer == RelationType::Complete) { std::set_intersection(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); } else { std::set_union(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); } return result; } mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetRelationUIDs( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } RelationUIDVectorType relUIDs_id; auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); for (const auto& instanceID : instanceIDs) { relUIDs_id.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } DataRelationUIDVectorType relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source,destination,instanceIDs); RelationUIDVectorType relUIDs_Data; std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relUIDs_Data), [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); std::sort(relUIDs_id.begin(), relUIDs_id.end()); std::sort(relUIDs_Data.begin(), relUIDs_Data.end()); RelationUIDVectorType result; std::set_union(relUIDs_id.begin(), relUIDs_id.end(), relUIDs_Data.begin(), relUIDs_Data.end(), std::back_inserter(result)); return result; } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const { auto result = this->GetRelationUIDs(source, destination); if (result.empty()) { mitkThrowException(NoPropertyRelationException); } else if(result.size()>1) { mitkThrow() << "Cannot return one(!) relation UID. Multiple relations exists for given rule, source and destination."; } return result[0]; } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::NULL_INSTANCE_ID() { return std::string(); }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUIDByInstanceID( const IPropertyProvider *source, const InstanceIDType &instanceID) const { RelationUIDType result; if (instanceID != NULL_INSTANCE_ID()) { auto idProp = source->GetConstProperty( PropertyKeyPathToPropertyName(GetRIIRelationUIDPropertyKeyPath(instanceID))); if (idProp.IsNotNull()) { result = idProp->GetValueAsString(); } } if (result.empty()) { mitkThrowException(NoPropertyRelationException); } return result; } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByRelationUID( const IPropertyProvider *source, const RelationUIDType &relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } InstanceIDType result = NULL_INSTANCE_ID(); auto destRegExStr = PropertyKeyPathToPropertyRegEx(GetRIIRelationUIDPropertyKeyPath()); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == relationUID) { if (instance_matches.size()>1) { result = instance_matches[1]; break; } } } } return result; } mitk::PropertyRelationRuleBase::InstanceIDVectorType mitk::PropertyRelationRuleBase::GetInstanceID_IDLayer( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto identifiable = CastProviderAsIdentifiable(destination); InstanceIDVectorType result; if (identifiable) { // check for relations of type Connected_ID; auto destRegExStr = this->GetRIIPropertyRegEx("destinationUID"); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto destUID = identifiable->GetUID(); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == destUID) { if (instance_matches.size()>1) { auto instanceID = instance_matches[1]; if (this->IsSupportedRuleID(GetRuleIDByInstanceID(source, instanceID))) { result.push_back(instanceID); } } } } } } return result; } const mitk::Identifiable* mitk::PropertyRelationRuleBase::CastProviderAsIdentifiable(const mitk::IPropertyProvider* destination) const { auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. - //This could be removed at the point we can get rid of DataNodes or they get realy transparent. + //This could be removed at the point we can get rid of DataNodes or they get really transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } return identifiable; } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } if (this->IsAbstract()) { mitkThrow() << "Error. This is an abstract property relation rule. Abstract rule must not make a connection. Please use a concrete rule."; } auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); bool hasIDlayer = !instanceIDs.empty(); auto relUIDs_data = this->GetRelationUIDs_DataLayer(source, destination, {}); if (relUIDs_data.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. RelationUID ID: " << relUIDs_data.front().first; } bool hasDatalayer = !relUIDs_data.empty(); RelationUIDType relationUID = this->CreateRelationUID(); InstanceIDType instanceID = NULL_INSTANCE_ID(); if (hasIDlayer) { instanceID = instanceIDs.front(); } else if (hasDatalayer) { try { instanceID = this->GetInstanceIDByRelationUID(source, relUIDs_data.front().first); } catch(...) { } } if(instanceID == NULL_INSTANCE_ID()) { instanceID = this->CreateNewRelationInstance(source, relationUID); } auto relUIDKey = PropertyKeyPathToPropertyName(GetRIIRelationUIDPropertyKeyPath(instanceID)); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); auto ruleIDKey = PropertyKeyPathToPropertyName(GetRIIRuleIDPropertyKeyPath(instanceID)); source->SetProperty(ruleIDKey, mitk::StringProperty::New(this->GetRuleID())); if (!hasIDlayer) { auto identifiable = this->CastProviderAsIdentifiable(destination); if (identifiable) { auto destUIDKey = PropertyKeyPathToPropertyName(GetRIIDestinationUIDPropertyKeyPath(instanceID)); source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); } } this->Connect_datalayer(source, destination, instanceID); return relationUID; } void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer) const { if (source == nullptr) { mitkThrow() << "Error. Source is invalid. Cannot disconnect."; } if (destination == nullptr) { mitkThrow() << "Error. Destination is invalid. Cannot disconnect."; } try { const auto relationUIDs = this->GetRelationUIDs(source, destination); for (const auto& relUID: relationUIDs) { this->Disconnect(source, relUID, layer); } } catch (const NoPropertyRelationException &) { // nothing to do and no real error in context of disconnect. } } void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer) const { if (source == nullptr) { mitkThrow() << "Error. Source is invalid. Cannot disconnect."; } if (layer == RelationType::Data || layer == RelationType::Complete) { this->Disconnect_datalayer(source, relationUID); } auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); if ((layer == RelationType::ID || layer == RelationType::Complete) && instanceID != NULL_INSTANCE_ID()) { auto instancePrefix = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(instancePrefix) == 0) { source->RemoveProperty(key); } } } } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::CreateRelationUID() { UIDGenerator generator; return generator.GetUID(); } /**This mutex is used to guard mitk::PropertyRelationRuleBase::CreateNewRelationInstance by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ std::mutex relationCreationLock; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::CreateNewRelationInstance( IPropertyOwner *source, const RelationUIDType &relationUID) const { std::lock_guard guard(relationCreationLock); ////////////////////////////////////// // Get all existing instanc IDs std::vector instanceIDs; InstanceIDType newID = "1"; auto destRegExStr = PropertyKeyPathToPropertyRegEx(GetRIIRelationUIDPropertyKeyPath()); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = std::to_string(instanceIDs.back() + 1); } ////////////////////////////////////// // reserve new ID auto relUIDKey = PropertyKeyPathToPropertyName(GetRIIRelationUIDPropertyKeyPath(newID)); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); return newID; } itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const { return Superclass::InternalClone(); } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByPropertyName(const std::string propName) { auto proppath = PropertyNameToPropertyKeyPath(propName); auto ref = GetRootKeyPath(); if (proppath.GetSize() < 3 || !(proppath.GetFirstNode() == ref.GetFirstNode()) || !(proppath.GetNode(1) == ref.GetNode(1))) { - mitkThrow() << "Property name is not for a RII property or containes no instance ID. Wrong name: " << propName; + mitkThrow() << "Property name is not for a RII property or contains no instance ID. Wrong name: " << propName; } return proppath.GetNode(2).name; } mitk::PropertyRelationRuleBase::RuleIDType mitk::PropertyRelationRuleBase::GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } auto path = GetRIIRuleIDPropertyKeyPath(instanceID); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } if (result.empty()) { mitkThrowException(NoPropertyRelationException) << "Error. Source has no property relation with the passed instance ID. Instance ID: " << instanceID; } return result; } std::string mitk::PropertyRelationRuleBase::GetDestinationUIDByInstanceID(const IPropertyProvider* source, const InstanceIDType& instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } auto path = GetRIIDestinationUIDPropertyKeyPath(instanceID); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } return result; } namespace mitk { /** * \brief Predicate used to wrap rule checks. * * \ingroup DataStorage */ class NodePredicateRuleFunction : public NodePredicateBase { public: using FunctionType = std::function; mitkClassMacro(NodePredicateRuleFunction, NodePredicateBase) mitkNewMacro2Param(NodePredicateRuleFunction, const FunctionType &, PropertyRelationRuleBase::ConstPointer) ~NodePredicateRuleFunction() override = default; bool CheckNode(const mitk::DataNode *node) const override { if (!node) { return false; } return m_Function(node, m_Rule); }; protected: explicit NodePredicateRuleFunction(const FunctionType &function, PropertyRelationRuleBase::ConstPointer rule) : m_Function(function), m_Rule(rule) { }; FunctionType m_Function; PropertyRelationRuleBase::ConstPointer m_Rule; }; } // namespace mitk mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourceCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSourceCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsDestinationCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetConnectedSourcesDetector() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSource(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourcesDetector( const IPropertyProvider *destination, RelationType exclusiveRelation) const { if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto check = [destination, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(node, destination, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( const IPropertyProvider *source, RelationType exclusiveRelation) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto check = [source, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(source, node, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationDetector( const IPropertyProvider *source, RelationUIDType relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto relUIDs = this->GetExistingRelations(source); if (std::find(relUIDs.begin(), relUIDs.end(), relationUID) == relUIDs.end()) { mitkThrow() << "Error. Passed relationUID does not identify a relation instance of the passed source for this rule instance."; }; auto check = [source, relationUID](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { try { auto relevantUIDs = rule->GetRelationUIDs(source, node); for (const auto& aUID : relevantUIDs) { if (aUID == relationUID) { return true; } } } catch(const NoPropertyRelationException &) { return false; } return false; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } diff --git a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp index ace7603c62..bda3bde5bf 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 contrained in geometry2D, because it may not be changed + // 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::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated) { m_ReferenceGeometry = geometry3D; PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(geometry3D, top, planeorientation, frontside, rotated); int worldAxis = planeorientation == PlaneGeometry::Sagittal ? 0 : planeorientation == PlaneGeometry::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); 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 avaialble, stop here + // 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; } - // Perfom Rotation + // 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); this->Modified(); // End Reinitialization if (m_SliceNavigationController) { m_SliceNavigationController->GetSlice()->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/DataManagement/mitkStandaloneDataStorage.cpp b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp index 4eedfcceeb..dc8fcdd82f 100644 --- a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp @@ -1,256 +1,256 @@ /*============================================================================ 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 "mitkStandaloneDataStorage.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" mitk::StandaloneDataStorage::StandaloneDataStorage() : mitk::DataStorage() { } mitk::StandaloneDataStorage::~StandaloneDataStorage() { for (auto it = m_SourceNodes.begin(); it != m_SourceNodes.end(); ++it) { this->RemoveListeners(it->first); } } bool mitk::StandaloneDataStorage::IsInitialized() const { return true; } void mitk::StandaloneDataStorage::Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents) { { std::lock_guard locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); /* check if node is in its own list of sources */ if ((parents != nullptr) && (std::find(parents->begin(), parents->end(), node) != parents->end())) throw std::invalid_argument("Node is it's own parent"); /* check if node already exists in StandaloneDataStorage */ if (m_SourceNodes.find(node) != m_SourceNodes.end()) throw std::invalid_argument("Node is already in DataStorage"); /* create parent list if it does not exist */ mitk::DataStorage::SetOfObjects::ConstPointer sp; if (parents != nullptr) sp = parents; else sp = mitk::DataStorage::SetOfObjects::New(); /* Store node and parent list in sources adjacency list */ m_SourceNodes.insert(std::make_pair(node, sp)); /* Store node and an empty children list in derivations adjacency list */ mitk::DataStorage::SetOfObjects::Pointer childrenPointer = mitk::DataStorage::SetOfObjects::New(); mitk::DataStorage::SetOfObjects::ConstPointer children = childrenPointer.GetPointer(); m_DerivedNodes.insert(std::make_pair(node, children)); /* create entry in derivations adjacency list for each parent of the new node */ for (SetOfObjects::ConstIterator it = sp->Begin(); it != sp->End(); it++) { mitk::DataNode::ConstPointer parent = it.Value().GetPointer(); mitk::DataStorage::SetOfObjects::ConstPointer derivedObjects = m_DerivedNodes[parent]; // get or create pointer to list of derived objects for that parent node if (derivedObjects.IsNull()) m_DerivedNodes[parent] = mitk::DataStorage::SetOfObjects::New(); // Create a set of Objects, if it does not already exist auto *deob = const_cast( m_DerivedNodes[parent].GetPointer()); // temporarily get rid of const pointer to insert new element deob->InsertElement(deob->Size(), node); // node is derived from parent. Insert it into the parents list of derived objects } // register for ITK changed events this->AddListeners(node); } /* Notify observers */ EmitAddNodeEvent(node); } void mitk::StandaloneDataStorage::Remove(const mitk::DataNode *node) { if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); if (node == nullptr) return; // remove ITK modified event listener this->RemoveListeners(node); // muellerm, 22.9.10: add additional reference count to ensure // that the node is not deleted when removed from the relation map // while m_Mutex is locked. This would cause the an itk::DeleteEvent // is thrown and a deadlock will occur when event receivers // access the DataStorage again in their event processing function // mitk::DataNode::ConstPointer nodeGuard(node); /* Notify observers of imminent node removal */ EmitRemoveNodeEvent(node); { std::lock_guard locked(m_Mutex); /* remove node from both relation adjacency lists */ this->RemoveFromRelation(node, m_SourceNodes); this->RemoveFromRelation(node, m_DerivedNodes); } } bool mitk::StandaloneDataStorage::Exists(const mitk::DataNode *node) const { std::lock_guard locked(m_Mutex); return (m_SourceNodes.find(node) != m_SourceNodes.end()); } void mitk::StandaloneDataStorage::RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation) { for (auto mapIter = relation.cbegin(); mapIter != relation.cend(); ++mapIter) // for each node in the relation if (mapIter->second.IsNotNull()) // if node has a relation list { SetOfObjects::Pointer s = const_cast(mapIter->second.GetPointer()); // search for node to be deleted in the relation list auto relationListIter = std::find( s->begin(), s->end(), node); // this assumes, that the relation list does not contain duplicates (which should be safe to assume) if (relationListIter != s->end()) // if node to be deleted is in relation list s->erase(relationListIter); // remove it from parentlist } /* now remove node from the relation */ AdjacencyList::iterator adIt; adIt = relation.find(node); if (adIt != relation.end()) relation.erase(adIt); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetAll() const { std::lock_guard locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); mitk::DataStorage::SetOfObjects::Pointer resultset = mitk::DataStorage::SetOfObjects::New(); /* Fill resultset with all objects that are managed by the StandaloneDataStorage object */ unsigned int index = 0; for (auto it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it) if (it->first.IsNull()) continue; else resultset->InsertElement(index++, const_cast(it->first.GetPointer())); return SetOfObjects::ConstPointer(resultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetRelations( const mitk::DataNode *node, const AdjacencyList &relation, const NodePredicateBase *condition, bool onlyDirectlyRelated) const { if (node == nullptr) throw std::invalid_argument("invalid node"); /* Either read direct relations directly from adjacency list */ if (onlyDirectlyRelated) { auto it = relation.find(node); // get parents of current node if ((it == relation.cend()) || (it->second.IsNull())) // node not found in list or no set of parents return SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return an empty set else return this->FilterSetOfObjects(it->second, condition); } /* Or traverse adjacency list to collect all related nodes */ std::vector resultset; std::vector openlist; /* Initialize openlist with node. this will add node to resultset, but that is necessary to detect circular relations that would lead to endless recursion */ openlist.push_back(node); while (openlist.size() > 0) { mitk::DataNode::ConstPointer current = openlist.back(); // get element that needs to be processed openlist.pop_back(); // remove last element, because it gets processed now resultset.push_back(current); // add current element to resultset auto it = relation.find(current); // get parents of current node if ((it == relation.cend()) // if node not found in list || (it->second.IsNull()) // or no set of parents available || (it->second->Size() == 0)) // or empty set of parents continue; // then continue with next node in open list else for (SetOfObjects::ConstIterator parentIt = it->second->Begin(); parentIt != it->second->End(); ++parentIt) // for each parent of current node { mitk::DataNode::ConstPointer p = parentIt.Value().GetPointer(); if (!(std::find(resultset.cbegin(), resultset.cend(), p) != resultset.end()) // if it is not already in resultset && !(std::find(openlist.cbegin(), openlist.cend(), p) != openlist.cend())) // and not already in openlist openlist.push_back(p); // then add it to openlist, so that it can be processed } } - /* now finally copy the results to a proper SetOfObjects variable exluding the initial node and checking the condition + /* now finally copy the results to a proper SetOfObjects variable excluding the initial node and checking the condition * if any is given */ mitk::DataStorage::SetOfObjects::Pointer realResultset = mitk::DataStorage::SetOfObjects::New(); if (condition != nullptr) { for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if ((*resultIt != node) && (condition->CheckNode(*resultIt) == true)) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } else { for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if (*resultIt != node) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } return SetOfObjects::ConstPointer(realResultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetSources( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectSources) const { std::lock_guard locked(m_Mutex); return this->GetRelations(node, m_SourceNodes, condition, onlyDirectSources); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetDerivations( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectDerivations) const { std::lock_guard locked(m_Mutex); return this->GetRelations(node, m_DerivedNodes, condition, onlyDirectDerivations); } void mitk::StandaloneDataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << "StandaloneDataStorage:\n"; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index 5d9649c5b8..a3e0b1f549 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -1,493 +1,493 @@ /*============================================================================ 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 "mitkTemporoSpatialStringProperty.h" #include using namespace nlohmann; mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const char *s) { if (s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const std::string &s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const TemporoSpatialStringProperty &other) : BaseProperty(other), m_Values(other.m_Values) { } bool mitk::TemporoSpatialStringProperty::IsEqual(const BaseProperty &property) const { return this->m_Values == static_cast(property).m_Values; } bool mitk::TemporoSpatialStringProperty::Assign(const BaseProperty &property) { this->m_Values = static_cast(property).m_Values; return true; } std::string mitk::TemporoSpatialStringProperty::GetValueAsString() const { return GetValue(); } bool mitk::TemporoSpatialStringProperty::IsUniform() const { auto refValue = this->GetValue(); for (const auto& timeStep : m_Values) { auto finding = std::find_if_not(timeStep.second.begin(), timeStep.second.end(), [&refValue](const mitk::TemporoSpatialStringProperty::SliceMapType::value_type& val) { return val.second == refValue; }); if (finding != timeStep.second.end()) { return false; } } return true; } itk::LightObject::Pointer mitk::TemporoSpatialStringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue() const { std::string result = ""; if (!m_Values.empty()) { if (!m_Values.begin()->second.empty()) { result = m_Values.begin()->second.begin()->second; } } return result; }; std::pair mitk::TemporoSpatialStringProperty::CheckValue( const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { std::string value = ""; bool found = false; auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter == timeEnd && allowCloseTime) { // search for closest time step (earlier preverd) timeIter = m_Values.upper_bound(timeStep); if (timeIter != m_Values.begin()) { // there is a key lower than time step timeIter = std::prev(timeIter); } } if (timeIter != timeEnd) { const SliceMapType &slices = timeIter->second; auto sliceIter = slices.find(zSlice); auto sliceEnd = slices.end(); if (sliceIter == sliceEnd && allowCloseSlice) { // search for closest slice (earlier preverd) sliceIter = slices.upper_bound(zSlice); if (sliceIter != slices.begin()) { // there is a key lower than slice sliceIter = std::prev(sliceIter); } } if (sliceIter != sliceEnd) { value = sliceIter->second; found = true; } } return std::make_pair(found, value); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).second; }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueBySlice( const IndexValueType &zSlice, bool allowClose) const { return GetValue(0, zSlice, true, allowClose); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueByTimeStep( const TimeStepType &timeStep, bool allowClose) const { return GetValue(timeStep, 0, allowClose, true); }; bool mitk::TemporoSpatialStringProperty::HasValue() const { return !m_Values.empty(); }; bool mitk::TemporoSpatialStringProperty::HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).first; }; bool mitk::TemporoSpatialStringProperty::HasValueBySlice(const IndexValueType &zSlice, bool allowClose) const { return HasValue(0, zSlice, true, allowClose); }; bool mitk::TemporoSpatialStringProperty::HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose) const { return HasValue(timeStep, 0, allowClose, true); }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices() const { std::set uniqueSlices; for (const auto& timeStep : m_Values) { for (const auto& slice : timeStep.second) { uniqueSlices.insert(slice.first); } } return std::vector(std::begin(uniqueSlices), std::end(uniqueSlices)); } std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices( const TimeStepType &timeStep) const { std::vector result; auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter != timeEnd) { for (auto const &element : timeIter->second) { result.push_back(element.first); } } return result; }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps() const { std::vector result; for (auto const &element : m_Values) { result.push_back(element.first); } return result; }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps(const IndexValueType& slice) const { std::vector result; for (const auto& timeStep : m_Values) { if (timeStep.second.find(slice) != std::end(timeStep.second)) { result.push_back(timeStep.first); } } return result; } void mitk::TemporoSpatialStringProperty::SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value) { auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter == timeEnd) { SliceMapType slices{{zSlice, value}}; m_Values.insert(std::make_pair(timeStep, slices)); } else { timeIter->second[zSlice] = value; } this->Modified(); }; void mitk::TemporoSpatialStringProperty::SetValue(const ValueType &value) { this->Modified(); m_Values.clear(); this->SetValue(0, 0, value); }; // Create necessary escape sequences from illegal characters // REMARK: This code is based upon code from boost::ptree::json_writer. // The corresponding boost function was not used directly, because it is not part of // the public interface of ptree::json_writer. :( // A own serialization strategy was implemented instead of using boost::ptree::json_write because // currently (<= boost 1.60) everything (even numbers) are converted into string representations -// by the writer, so e.g. it becomes "t":"2" instaed of "t":2 +// by the writer, so e.g. it becomes "t":"2" instead of "t":2 template std::basic_string CreateJSONEscapes(const std::basic_string &s) { std::basic_string result; typename std::basic_string::const_iterator b = s.begin(); typename std::basic_string::const_iterator e = s.end(); while (b != e) { using UCh = std::make_unsigned_t; UCh c(*b); // This assumes an ASCII superset. // We escape everything outside ASCII, because this code can't // handle high unicode characters. if (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x2E) || (c >= 0x30 && c <= 0x5B) || (c >= 0x5D && c <= 0x7F)) result += *b; else if (*b == Ch('\b')) result += Ch('\\'), result += Ch('b'); else if (*b == Ch('\f')) result += Ch('\\'), result += Ch('f'); else if (*b == Ch('\n')) result += Ch('\\'), result += Ch('n'); else if (*b == Ch('\r')) result += Ch('\\'), result += Ch('r'); else if (*b == Ch('\t')) result += Ch('\\'), result += Ch('t'); else if (*b == Ch('/')) result += Ch('\\'), result += Ch('/'); else if (*b == Ch('"')) result += Ch('\\'), result += Ch('"'); else if (*b == Ch('\\')) result += Ch('\\'), result += Ch('\\'); else { const char *hexdigits = "0123456789ABCDEF"; unsigned long u = (std::min)(static_cast(static_cast(*b)), 0xFFFFul); int d1 = u / 4096; u -= d1 * 4096; int d2 = u / 256; u -= d2 * 256; int d3 = u / 16; u -= d3 * 16; int d4 = u; result += Ch('\\'); result += Ch('u'); result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); } ++b; } return result; } using CondensedTimeKeyType = std::pair; using CondensedTimePointsType = std::map; using CondensedSliceKeyType = std::pair; using CondensedSlicesType = std::map; -/** Helper function that checks if between an ID and a successing ID is no gap.*/ +/** Helper function that checks if between an ID and a successive ID is no gap.*/ template bool isGap(const TValue& value, const TValue& successor) { return value successor + 1; } template void CheckAndCondenseElement(const TNewKey& newKeyMinID, const TNewValue& newValue, TMasterKey& masterKey, TMasterValue& masterValue, TCondensedContainer& condensedContainer) { if (newValue != masterValue || isGap(newKeyMinID, masterKey.second)) { condensedContainer[masterKey] = masterValue; masterValue = newValue; masterKey.first = newKeyMinID; } masterKey.second = newKeyMinID; } /** Helper function that tries to condense the values of time points for a slice as much as possible and returns all slices with condensed timepoint values.*/ CondensedSlicesType CondenseTimePointValuesOfProperty(const mitk::TemporoSpatialStringProperty* tsProp) { CondensedSlicesType uncondensedSlices; auto zs = tsProp->GetAvailableSlices(); for (const auto z : zs) { CondensedTimePointsType condensedTimePoints; auto timePointIDs = tsProp->GetAvailableTimeSteps(z); CondensedTimeKeyType condensedKey = { timePointIDs.front(),timePointIDs.front() }; auto refValue = tsProp->GetValue(timePointIDs.front(), z); for (const auto timePointID : timePointIDs) { const auto& newVal = tsProp->GetValue(timePointID, z); CheckAndCondenseElement(timePointID, newVal, condensedKey, refValue, condensedTimePoints); } condensedTimePoints[condensedKey] = refValue; uncondensedSlices[{ z, z }] = condensedTimePoints; } return uncondensedSlices; } ::std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON( const mitk::BaseProperty *prop) { // REMARK: Implemented own serialization instead of using boost::ptree::json_write because // currently (<= boost 1.60) everything (even numbers) are converted into string representations - // by the writer, so e.g. it becomes "t":"2" instaed of "t":2 - // If this problem is fixed with boost, we shoud switch back to json_writer (and remove the custom + // by the writer, so e.g. it becomes "t":"2" instead of "t":2 + // If this problem is fixed with boost, we should switch back to json_writer (and remove the custom // implementation of CreateJSONEscapes (see above)). const auto *tsProp = dynamic_cast(prop); if (!tsProp) { mitkThrow() << "Cannot serialize properties of types other than TemporoSpatialStringProperty."; } std::ostringstream stream; stream.imbue(std::locale("C")); stream << "{\"values\":["; //we condense the content of the property to have a compact serialization. //we start with condensing time points and then slices (in difference to the //internal layout). Reason: There is more entropy in slices (looking at DICOM) //than across time points for one slice, so we can "compress" to a higher rate. //We don't wanted to change the internal structure of the property as it would - //introduce API inconvinience and subtle changes in behavior. + //introduce API inconvenience and subtle changes in behavior. CondensedSlicesType uncondensedSlices = CondenseTimePointValuesOfProperty(tsProp); //now condense the slices CondensedSlicesType condensedSlices; if(!uncondensedSlices.empty()) { CondensedTimePointsType& masterSlice = uncondensedSlices.begin()->second; CondensedSliceKeyType masterSliceKey = uncondensedSlices.begin()->first; for (const auto& uncondensedSlice : uncondensedSlices) { const auto& uncondensedSliceID = uncondensedSlice.first.first; CheckAndCondenseElement(uncondensedSliceID, uncondensedSlice.second, masterSliceKey, masterSlice, condensedSlices); } condensedSlices[masterSliceKey] = masterSlice; } bool first = true; for (const auto& z : condensedSlices) { for (const auto& t : z.second) { if (first) { first = false; } else { stream << ", "; } const auto& minSliceID = z.first.first; const auto& maxSliceID = z.first.second; const auto& minTimePointID = t.first.first; const auto& maxTimePointID = t.first.second; stream << "{\"z\":" << minSliceID << ", "; if (minSliceID != maxSliceID) { stream << "\"zmax\":" << maxSliceID << ", "; } stream << "\"t\":" << minTimePointID << ", "; if (minTimePointID != maxTimePointID) { stream << "\"tmax\":" << maxTimePointID << ", "; } const auto& value = t.second; stream << "\"value\":\"" << CreateJSONEscapes(value) << "\"}"; } } stream << "]}"; return stream.str(); } mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty( const std::string &value) { if (value.empty()) return nullptr; mitk::TemporoSpatialStringProperty::Pointer prop = mitk::TemporoSpatialStringProperty::New(); auto root = json::parse(value); for (const auto& element : root["values"]) { auto value = element.value("value", ""); auto z = element.value("z", 0); auto zmax = element.value("zmax", z); auto t = element.value("t", 0); auto tmax = element.value("tmax", t); for (auto currentT = t; currentT <= tmax; ++currentT) { for (auto currentZ = z; currentZ <= zmax; ++currentZ) { prop->SetValue(currentT, currentZ, value); } } } return prop.GetPointer(); } diff --git a/Modules/Core/src/IO/mitkFileReaderWriterBase.h b/Modules/Core/src/IO/mitkFileReaderWriterBase.h index 4ea0693020..44fe486237 100644 --- a/Modules/Core/src/IO/mitkFileReaderWriterBase.h +++ b/Modules/Core/src/IO/mitkFileReaderWriterBase.h @@ -1,106 +1,106 @@ /*============================================================================ 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 MITKFILEREADERWRITERBASE_H #define MITKFILEREADERWRITERBASE_H #include #include #include #include #include #include namespace mitk { class FileReaderWriterBase { public: typedef std::map Options; typedef mitk::MessageAbstractDelegate1 ProgressCallback; FileReaderWriterBase(); virtual ~FileReaderWriterBase(); Options GetOptions() const; us::Any GetOption(const std::string &name) const; void SetOptions(const Options &options); void SetOption(const std::string &name, const us::Any &value); void SetDefaultOptions(const Options &defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file reader. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which reader to use if several * equivalent readers have been found. * It may be used to replace a default reader from MITK in your own project. * E.g. if you want to use your own reader for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; void SetMimeType(const CustomMimeType &mimeType); const CustomMimeType *GetMimeType() const; CustomMimeType *GetMimeType(); MimeType GetRegisteredMimeType() const; void SetMimeTypePrefix(const std::string &prefix); std::string GetMimeTypePrefix() const; void SetDescription(const std::string &description); std::string GetDescription() const; void AddProgressCallback(const ProgressCallback &callback); void RemoveProgressCallback(const ProgressCallback &callback); us::ServiceRegistration RegisterMimeType(us::ModuleContext *context); void UnregisterMimeType(); protected: FileReaderWriterBase(const FileReaderWriterBase &other); std::string m_Description; int m_Ranking; std::string m_MimeTypePrefix; /** * \brief Options supported by this reader. Set sensible default values! * - * Can be left emtpy if no special options are required. + * Can be left empty if no special options are required. */ Options m_Options; Options m_DefaultOptions; // us::PrototypeServiceFactory* m_PrototypeFactory; Message1 m_ProgressMessage; std::unique_ptr m_CustomMimeType; us::ServiceRegistration m_MimeTypeReg; private: // purposely not implemented FileReaderWriterBase &operator=(const FileReaderWriterBase &other); }; } #endif // MITKFILEREADERWRITERBASE_H diff --git a/Modules/Core/src/IO/mitkIOUtil.cpp b/Modules/Core/src/IO/mitkIOUtil.cpp index 98a473e9a6..2e05ce7d71 100644 --- a/Modules/Core/src/IO/mitkIOUtil.cpp +++ b/Modules/Core/src/IO/mitkIOUtil.cpp @@ -1,1015 +1,1015 @@ /*============================================================================ 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 "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include // VTK #include #include #include #include #include static std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } #ifdef US_PLATFORM_WINDOWS #include #include -// make the posix flags point to the obsolte bsd types on windows +// make the posix flags point to the obsolete bsd types on windows #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #else #include #include #include #endif #include #include static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // A cross-platform version of the mkstemps function static int mkstemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary files to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return -1; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif for (unsigned int count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; int fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { errno = savedErrno; return fd; } else if (errno != EEXIST) { return -1; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } // A cross-platform version of the POSIX mkdtemp function static char *mkdtemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary dirs to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary dir. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return nullptr; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return nullptr; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif unsigned int count = 0; for (; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; #ifdef US_PLATFORM_WINDOWS int fd = _mkdir(tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC); #else int fd = mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR); #endif if (fd >= 0) { errno = savedErrno; return tmpl; } else if (errno != EEXIST) { return nullptr; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return nullptr; } //#endif //************************************************************** // mitk::IOUtil method definitions namespace mitk { struct IOUtil::Impl { struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase { FixedReaderOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(LoadInfo &loadInfo) const override { IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader) { reader->SetOptions(m_Options); } return false; } private: const IFileReader::Options &m_Options; }; struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase { FixedWriterOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(SaveInfo &saveInfo) const override { IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer) { writer->SetOptions(m_Options); } return false; } private: const IFileWriter::Options &m_Options; }; static BaseData::Pointer LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase* optionsCallback = nullptr); }; BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector baseDataList = Load(path, optionsCallback); // The Load(path) call above should throw an exception if nothing could be loaded assert(!baseDataList.empty()); return baseDataList.front(); } #ifdef US_PLATFORM_WINDOWS std::string IOUtil::GetProgramPath() { char path[512]; std::size_t index = std::string(path, GetModuleFileName(nullptr, path, 512)).find_last_of('\\'); return std::string(path, index); } #elif defined(US_PLATFORM_APPLE) #include std::string IOUtil::GetProgramPath() { char path[512]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) { std::size_t index = std::string(path).find_last_of('/'); std::string strPath = std::string(path, index); // const char* execPath = strPath.c_str(); // mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false); return strPath; } return std::string(); } #else #include #include #include std::string IOUtil::GetProgramPath() { std::stringstream ss; ss << "/proc/" << getpid() << "/exe"; char proc[512] = {0}; ssize_t ch = readlink(ss.str().c_str(), proc, 512); if (ch == -1) return std::string(); std::size_t index = std::string(proc).find_last_of('/'); return std::string(proc, index); } #endif char IOUtil::GetDirectorySeparator() { #ifdef US_PLATFORM_WINDOWS return '\\'; #else return '/'; #endif } std::string IOUtil::GetTempPath() { static std::string result; if (result.empty()) { #ifdef US_PLATFORM_WINDOWS char tempPathTestBuffer[1]; DWORD bufferLength = GetTempPathA(1, tempPathTestBuffer); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } std::vector tempPath(bufferLength); bufferLength = GetTempPathA(bufferLength, &tempPath[0]); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } result.assign(tempPath.begin(), tempPath.begin() + static_cast(bufferLength)); #else result = "/tmp/"; #endif } return result; } std::string IOUtil::CreateTemporaryFile(const std::string &templateName, std::string path) { std::ofstream tmpOutputStream; std::string returnValue = CreateTemporaryFile(tmpOutputStream, templateName, path); tmpOutputStream.close(); return returnValue; } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, const std::string &templateName, std::string path) { return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path); } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, std::ios_base::openmode mode, const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; int fd = mkstemps_compat(&dst_path[0], suffixlen); if (fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc); close(fd); } else { mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr(); } return path; } std::string IOUtil::CreateTemporaryDirectory(const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; if (mkdtemps_compat(&dst_path[0], suffixlen) == nullptr) { mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr(); } path.assign(dst_path.begin(), dst_path.end() - 1); return path; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, storage, optionsCallback); } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, optionsCallback); } std::vector IOUtil::Load(const std::string &path, const IFileReader::Options &options) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return loadInfos.front().m_Output; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); std::vector loadInfos; for (const auto &loadInfo : paths) { loadInfos.emplace_back(loadInfo); } std::string errMsg = Load(loadInfos, nodeResult, &storage, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback) { std::vector result; std::vector loadInfos; for (const auto &loadInfo : paths) { loadInfos.emplace_back(loadInfo); } std::string errMsg = Load(loadInfos, nullptr, nullptr, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } for (std::vector::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd; ++iter) { result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end()); } return result; } std::string IOUtil::Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback) { if (loadInfos.empty()) { return "No input files given"; } int filesToRead = loadInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToRead); std::string errMsg; std::map usedReaderItems; std::vector< std::string > read_files; for (auto &loadInfo : loadInfos) { if(std::find(read_files.begin(), read_files.end(), loadInfo.m_Path) != read_files.end()) continue; std::vector readers = loadInfo.m_ReaderSelector.Get(); if (readers.empty()) { if (!itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(loadInfo.m_Path).c_str())) { errMsg += "File '" + loadInfo.m_Path + "' does not exist\n"; } else { errMsg += "No reader available for '" + loadInfo.m_Path + "'\n"; } continue; } bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty(); // check if we already used a reader which should be re-used std::vector currMimeTypes = loadInfo.m_ReaderSelector.GetMimeTypes(); std::string selectedMimeType; for (std::vector::const_iterator mimeTypeIter = currMimeTypes.begin(), mimeTypeIterEnd = currMimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::map::const_iterator oldSelectedItemIter = usedReaderItems.find(mimeTypeIter->GetName()); if (oldSelectedItemIter != usedReaderItems.end()) { // we found an already used item for a mime-type which is contained // in the current reader set, check all current readers if there service // id equals the old reader for (std::vector::const_iterator currReaderItem = readers.begin(), currReaderItemEnd = readers.end(); currReaderItem != currReaderItemEnd; ++currReaderItem) { if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() && currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() && currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel()) { // okay, we used the same reader already, re-use its options selectedMimeType = mimeTypeIter->GetName(); callOptionsCallback = false; loadInfo.m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId()); loadInfo.m_ReaderSelector.GetSelected().GetReader()->SetOptions( oldSelectedItemIter->second.GetReader()->GetOptions()); break; } } if (!selectedMimeType.empty()) break; } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(loadInfo); if (!callOptionsCallback && !loadInfo.m_Cancel) { usedReaderItems.erase(selectedMimeType); FileReaderSelector::Item selectedItem = loadInfo.m_ReaderSelector.GetSelected(); usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem)); } } if (loadInfo.m_Cancel) { errMsg += "Reading operation(s) cancelled."; break; } IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader == nullptr) { errMsg += "Unexpected nullptr reader."; break; } reader->SetProperties(loadInfo.m_Properties); // Do the actual reading try { DataStorage::SetOfObjects::Pointer nodes; if (ds != nullptr) { nodes = reader->Read(*ds); std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } else { nodes = DataStorage::SetOfObjects::New(); std::vector baseData = reader->Read(); for (auto iter = baseData.begin(); iter != baseData.end(); ++iter) { if (iter->IsNotNull()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); nodes->InsertElement(nodes->Size(), node); } } std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { const mitk::DataNode::Pointer &node = nodeIter->Value(); mitk::BaseData::Pointer data = node->GetData(); if (data.IsNull()) { continue; } data->SetProperty("path", mitk::StringProperty::New(Utf8Util::Local8BitToUtf8(loadInfo.m_Path))); loadInfo.m_Output.push_back(data); if (nodeResult) { nodeResult->push_back(nodeIter->Value()); } } if (loadInfo.m_Output.empty() || (nodeResult && nodeResult->Size() == 0)) { errMsg += "Unknown read error occurred reading " + loadInfo.m_Path; } } catch (const std::exception &e) { - errMsg += "Exception occured when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n"; + errMsg += "Exception occurred when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToRead; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToRead); return errMsg; } std::vector IOUtil::Load(const us::ModuleResource &usResource, std::ios_base::openmode mode) { us::ModuleResourceStream resStream(usResource, mode); mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector mimetypes = mimeTypeProvider->GetMimeTypesForFile(usResource.GetResourcePath()); std::vector data; if (mimetypes.empty()) { mitkThrow() << "No mimetype for resource stream: " << usResource.GetResourcePath(); return data; } mitk::FileReaderRegistry fileReaderRegistry; std::vector> refs = fileReaderRegistry.GetReferences(mimetypes[0]); if (refs.empty()) { mitkThrow() << "No reader available for resource stream: " << usResource.GetResourcePath(); return data; } mitk::IFileReader *reader = fileReaderRegistry.GetReader(refs[0]); reader->SetInput(usResource.GetResourcePath(), &resStream); data = reader->Read(); return data; } BaseData::Pointer IOUtil::Load(const std::string& path, const PropertyList* properties) { LoadInfo loadInfo(path); loadInfo.m_Properties = properties; std::vector loadInfos; loadInfos.push_back(loadInfo); auto errMsg = Load(loadInfos, nullptr, nullptr, nullptr); if (!errMsg.empty()) mitkThrow() << errMsg; return loadInfos.front().m_Output.front(); } void IOUtil::Save(const BaseData *data, const std::string &path, bool setPathProperty) { Save(data, path, IFileWriter::Options(), setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty) { Save(data, std::string(), path, options, setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension, bool setPathProperty) { Save(data, mimeType, path, IFileWriter::Options(), addExtension, setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, const IFileWriter::Options &options, bool addExtension, bool setPathProperty) { if ((data == nullptr) || (data->IsEmpty())) mitkThrow() << "BaseData cannotbe null or empty for save methods in IOUtil.h."; std::string errMsg; if (options.empty()) { errMsg = Save(data, mimeType, path, nullptr, addExtension, setPathProperty); } else { Impl::FixedWriterOptionsFunctor optionsCallback(options); errMsg = Save(data, mimeType, path, &optionsCallback, addExtension, setPathProperty); } if (!errMsg.empty()) { mitkThrow() << errMsg; } } void IOUtil::Save(std::vector &saveInfos, bool setPathProperty) { std::string errMsg = Save(saveInfos, nullptr, setPathProperty); if (!errMsg.empty()) { mitkThrow() << errMsg; } } std::string IOUtil::Save(const BaseData *data, const std::string &mimeTypeName, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension, bool setPathProperty) { if (path.empty()) { return "No output filename given"; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); SaveInfo saveInfo(data, mimeType, path); std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(path))); if (saveInfo.m_WriterSelector.IsEmpty()) { return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + (mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) + (ext.empty() ? std::string() : (std::string(" with extension ") + ext)); } // Add an extension if not already specified if (ext.empty() && addExtension) { ext = saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); saveInfo.m_Path += ext; } std::vector infos; infos.push_back(saveInfo); return Save(infos, optionsCallback, setPathProperty); } std::string IOUtil::Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty) { if (saveInfos.empty()) { return "No data for saving available"; } int filesToWrite = saveInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToWrite); std::string errMsg; std::set usedSaveInfos; for (auto &saveInfo : saveInfos) { const std::string baseDataType = saveInfo.m_BaseData->GetNameOfClass(); std::vector writers = saveInfo.m_WriterSelector.Get(); // Error out if no compatible Writer was found if (writers.empty()) { errMsg += std::string("No writer available for ") + baseDataType + " data.\n"; continue; } bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty(); // check if we already used a writer for this base data type // which should be re-used auto oldSaveInfoIter = usedSaveInfos.find(saveInfo); if (oldSaveInfoIter != usedSaveInfos.end()) { // we previously saved a base data object of the same data with the same mime-type, // check if the same writer is contained in the current writer set and if the // confidence level matches FileWriterSelector::Item oldSelectedItem = oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); for (std::vector::const_iterator currWriterItem = writers.begin(), currWriterItemEnd = writers.end(); currWriterItem != currWriterItemEnd; ++currWriterItem) { if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() && currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel()) { // okay, we used the same writer already, re-use its options callOptionsCallback = false; saveInfo.m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); saveInfo.m_WriterSelector.GetSelected().GetWriter()->SetOptions(oldSelectedItem.GetWriter()->GetOptions()); break; } } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(saveInfo); if (!callOptionsCallback && !saveInfo.m_Cancel) { usedSaveInfos.erase(saveInfo); usedSaveInfos.insert(saveInfo); } } if (saveInfo.m_Cancel) { errMsg += "Writing operation(s) cancelled."; break; } IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer == nullptr) { errMsg += "Unexpected nullptr writer."; break; } // Do the actual writing try { writer->SetOutputLocation(saveInfo.m_Path); writer->Write(); } catch (const std::exception &e) { errMsg += std::string("Exception occurred when writing to ") + saveInfo.m_Path + ":\n" + e.what() + "\n"; } if (setPathProperty) saveInfo.m_BaseData->GetPropertyList()->SetStringProperty("path", Utf8Util::Local8BitToUtf8(saveInfo.m_Path).c_str()); mitk::ProgressBar::GetInstance()->Progress(2); --filesToWrite; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToWrite); return errMsg; } IOUtil::SaveInfo::SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path) : m_BaseData(baseData), m_WriterSelector(baseData, mimeType.GetName(), path), m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type : (m_WriterSelector.IsEmpty() ? mimeType // no writer found, use the original invalid mime-type : m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type )), m_Path(path), m_Cancel(false) { } bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo &other) const { int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass()); if (r == 0) { return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType(); } return r < 0; } IOUtil::LoadInfo::LoadInfo(const std::string &path) : m_Path(path), m_ReaderSelector(path), m_Cancel(false), m_Properties(nullptr) { } } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index e4672cba4a..fb164cca4a 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,754 +1,754 @@ /*============================================================================ 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 "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName) { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType) { if ("GE4ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4"); } else if ("GE5ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5"); } else if ("Bruker2dseqImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq"); } } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } auto extensions = customReaderMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customReaderMimeType); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } auto extensions = customWriterMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customWriterMimeType); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const auto* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry) { std::stringstream stream; stream << timeGeometry->GetTimeBounds(0)[0]; const auto maxTimePoints = timeGeometry->CountTimeSteps(); for (TimeStepType pos = 0; pos < maxTimePoints; ++pos) { auto timeBounds = timeGeometry->GetTimeBounds(pos); /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed as soon as T28262 is solved! + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed as soon as T28262 is solved! if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1]) { timeBounds[1] = timeBounds[0] + 1.; } - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// stream << " " << timeBounds[1]; } auto result = itk::MetaDataObject::New(); result->SetMetaDataObjectValue(stream.str()); return result.GetPointer(); }; std::vector ItkImageIO::DoRead() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); if (loadedProp.IsNull()) { MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; break; } image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(image); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(static_cast(pixelType.GetComponentType()) < PixelComponentUserType ? pixelType.GetComponentType() : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection(0.0); mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING; try { value = infoList.front()->GetSerializationFunction()(property.second); } catch (const std::exception& e) { MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what(); } catch (...) { - MITK_ERROR << "Unkown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first; + MITK_ERROR << "Unknown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first; } if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID()); ImageReadAccessor imageAccess(image); LocaleSwitch localeSwitch2("C"); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/src/IO/mitkPointSetReaderService.h b/Modules/Core/src/IO/mitkPointSetReaderService.h index f72bc23fdb..88a027e3d3 100644 --- a/Modules/Core/src/IO/mitkPointSetReaderService.h +++ b/Modules/Core/src/IO/mitkPointSetReaderService.h @@ -1,65 +1,65 @@ /*============================================================================ 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 _MITK_POINT_SET_READER_SERVICE__H_ #define _MITK_POINT_SET_READER_SERVICE__H_ // MITK #include #include namespace tinyxml2 { class XMLElement; } namespace mitk { /** * @internal * * @brief reads xml representations of mitk::PointSets from a file * - * Reader for xml files containing one or multiple xml represenations of + * Reader for xml files containing one or multiple xml representations of * mitk::PointSet. If multiple mitk::PointSet objects are stored in one file, * these are assigned to multiple BaseData objects. * * The reader is able to read the old 3D Pointsets without the "specification" and "timeseries" tags and the new 4D * Pointsets. * * @ingroup IO */ class PointSetReaderService : public AbstractFileReader { public: PointSetReaderService(); ~PointSetReaderService() override; using AbstractFileReader::Read; protected: std::vector> DoRead() override; private: PointSetReaderService(const PointSetReaderService &other); mitk::BaseGeometry::Pointer ReadGeometry(tinyxml2::XMLElement *parentElement); mitk::PointSet::Pointer ReadPoints(mitk::PointSet::Pointer newPointSet, tinyxml2::XMLElement *currentTimeSeries, unsigned int currentTimeStep); PointSetReaderService *Clone() const override; }; } #endif diff --git a/Modules/Core/src/IO/mitkStandardFileLocations.cpp b/Modules/Core/src/IO/mitkStandardFileLocations.cpp index 07b069fece..903f49eefe 100644 --- a/Modules/Core/src/IO/mitkStandardFileLocations.cpp +++ b/Modules/Core/src/IO/mitkStandardFileLocations.cpp @@ -1,239 +1,239 @@ /*============================================================================ 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 mitk::StandardFileLocations::StandardFileLocations() { } mitk::StandardFileLocations::~StandardFileLocations() { } mitk::StandardFileLocations *mitk::StandardFileLocations::GetInstance() { static StandardFileLocations::Pointer m_Instance = nullptr; if (m_Instance.IsNull()) m_Instance = StandardFileLocations::New(); return m_Instance; } void mitk::StandardFileLocations::AddDirectoryForSearch(const char *dir, bool insertInFrontOfSearchList) { // Do nothing if directory is already included into search list (TODO more clever: search only once!) FileSearchVectorType::iterator iter; if (m_SearchDirectories.size() > 0) { iter = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(), std::string(dir)); if (iter != m_SearchDirectories.end()) return; } // insert dir into queue if (insertInFrontOfSearchList) { auto it = m_SearchDirectories.begin(); m_SearchDirectories.insert(it, std::string(dir)); } else m_SearchDirectories.push_back(std::string(dir)); } void mitk::StandardFileLocations::RemoveDirectoryForSearch(const char *dir) { FileSearchVectorType::iterator it; // background layers if (m_SearchDirectories.size() > 0) { it = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(), std::string(dir)); if (it != m_SearchDirectories.end()) { m_SearchDirectories.erase(it); return; } } } std::string mitk::StandardFileLocations::SearchDirectoriesForFile(const char *filename) { FileSearchVectorType::iterator it; for (it = m_SearchDirectories.begin(); it != m_SearchDirectories.end(); ++it) { std::string currDir = (*it); // Perhaps append "/" before appending filename if (currDir.find_last_of("\\") + 1 != currDir.size() || currDir.find_last_of("/") + 1 != currDir.size()) currDir += "/"; // Append filename currDir += filename; // Perhaps remove "/" after filename if (currDir.find_last_of("\\") + 1 == currDir.size() || currDir.find_last_of("/") + 1 == currDir.size()) currDir.erase(currDir.size() - 1, currDir.size()); // convert to OS dependent path schema currDir = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::ConvertToOutputPath(Utf8Util::Local8BitToUtf8(currDir).c_str())); - // On windows systems, the ConvertToOutputPath method quotes pathes that contain empty spaces. + // On windows systems, the ConvertToOutputPath method quotes paths that contain empty spaces. // These quotes are not expected by the FileExists method and therefore removed, if existing. if (currDir.find_last_of("\"") + 1 == currDir.size()) currDir.erase(currDir.size() - 1, currDir.size()); if (currDir.find_last_of("\"") == 0) currDir.erase(0, 1); // Return first found path if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(currDir).c_str())) return currDir; } return std::string(""); } std::string mitk::StandardFileLocations::FindFile(const char *filename, const char *pathInSourceDir) { std::string directoryPath; // 1. look for MITKCONF environment variable const char *mitkConf = itksys::SystemTools::GetEnv("MITKCONF"); if (mitkConf != nullptr) AddDirectoryForSearch(mitkConf, false); // 2. use .mitk-subdirectory in home directory of the user #if defined(_WIN32) && !defined(__CYGWIN__) const char *homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE"); const char *homePath = itksys::SystemTools::GetEnv("HOMEPATH"); if ((homeDrive != nullptr) || (homePath != nullptr)) { directoryPath = homeDrive; directoryPath += homePath; directoryPath += "/.mitk/"; AddDirectoryForSearch(directoryPath.c_str(), false); } #else const char *homeDirectory = itksys::SystemTools::GetEnv("HOME"); if (homeDirectory != nullptr) { directoryPath = homeDirectory; directoryPath += "/.mitk/"; AddDirectoryForSearch(directoryPath.c_str(), false); } #endif // defined(_WIN32) && !defined(__CYGWIN__) // 3. look in the current working directory directoryPath = ""; AddDirectoryForSearch(directoryPath.c_str()); directoryPath = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()); AddDirectoryForSearch(directoryPath.c_str(), false); std::string directoryBinPath = directoryPath + "/bin"; AddDirectoryForSearch(directoryBinPath.c_str(), false); // 4. use a source tree location from compile time directoryPath = MITK_ROOT; if (pathInSourceDir) { directoryPath += pathInSourceDir; } directoryPath += '/'; AddDirectoryForSearch(directoryPath.c_str(), false); return SearchDirectoriesForFile(filename); } std::string mitk::StandardFileLocations::GetOptionDirectory() { const char *mitkoptions = itksys::SystemTools::GetEnv("MITKOPTIONS"); std::string optionsDirectory; if (mitkoptions != nullptr) { // 1. look for MITKOPTIONS environment variable optionsDirectory = mitkoptions; optionsDirectory += "/"; } else { // 2. use .mitk-subdirectory in home directory of the user std::string homeDirectory; #if defined(_WIN32) && !defined(__CYGWIN__) const char *homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE"); const char *homePath = itksys::SystemTools::GetEnv("HOMEPATH"); if ((homeDrive == nullptr) || (homePath == nullptr)) { itkGenericOutputMacro(<< "Environment variables HOMEDRIVE and/or HOMEPATH not set" << ". Using current working directory as home directory: " << Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory())); homeDirectory = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()); } else { homeDirectory = homeDrive; homeDirectory += homePath; } if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(homeDirectory).c_str()) == false) { itkGenericOutputMacro(<< "Could not find home directory at " << homeDirectory << ". Using current working directory as home directory: " << Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory())); homeDirectory = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()); } #else const char *home = itksys::SystemTools::GetEnv("HOME"); if (home == nullptr) { itkGenericOutputMacro(<< "Environment variable HOME not set" << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } else homeDirectory = home; if (itksys::SystemTools::FileExists(homeDirectory.c_str()) == false) { itkGenericOutputMacro(<< "Could not find home directory at " << homeDirectory << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } #endif // defined(_WIN32) && !defined(__CYGWIN__) optionsDirectory = homeDirectory; optionsDirectory += "/.mitk"; } optionsDirectory = itksys::SystemTools::ConvertToOutputPath(Utf8Util::Local8BitToUtf8(optionsDirectory).c_str()); if (itksys::SystemTools::CountChar(optionsDirectory.c_str(), '"') > 0) { char *unquoted = itksys::SystemTools::RemoveChars(optionsDirectory.c_str(), "\""); optionsDirectory = unquoted; delete[] unquoted; } if (itksys::SystemTools::MakeDirectory(optionsDirectory.c_str()) == false) { itkGenericExceptionMacro(<< "Could not create .mitk directory at " << Utf8Util::Utf8ToLocal8Bit(optionsDirectory)); } return Utf8Util::Utf8ToLocal8Bit(optionsDirectory); } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp index 5740ea3a58..b8ae8d12ff 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -1,914 +1,914 @@ /*============================================================================ 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 #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_LastCoordinateInMM.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("updateStatusbar", UpdateStatusbar) 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(); - // allwaysReact + // 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[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); m_LastCoordinateInMM = 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(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayActionEventBroadcast::DecreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer::Pointer renderer = positionEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNull()) { return; } Point3D worldposition; renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); auto globalCurrentTimePoint = renderer->GetTime(); Image::Pointer image3D; DataNode::Pointer node; DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); if (node.IsNull()) { return; } bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } // get the position and pixel value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } else { ScalarType pixelValue; mitkPixelTypeMultiplex5( FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(renderer->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } 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/mitkXML2EventParser.cpp b/Modules/Core/src/Interactions/mitkXML2EventParser.cpp index 56ebe041b9..e7fa2b60c2 100755 --- a/Modules/Core/src/Interactions/mitkXML2EventParser.cpp +++ b/Modules/Core/src/Interactions/mitkXML2EventParser.cpp @@ -1,134 +1,134 @@ /*============================================================================ 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 "mitkXML2EventParser.h" #include "mitkEventFactory.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventConst.h" #include "mitkInteractionKeyEvent.h" #include "mitkInternalEvent.h" // VTK #include // us #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" namespace mitk { void mitk::XML2EventParser::StartElement(const char *elementName, const char **atts) { std::string name(elementName); if (name == InteractionEventConst::xmlTagConfigRoot()) { // } else if (name == InteractionEventConst::xmlTagEventVariant()) { std::string eventClass = ReadXMLStringAttribute(InteractionEventConst::xmlParameterEventClass(), atts); // New list in which all parameters are stored that are given within the tag m_EventPropertyList = PropertyList::New(); m_EventPropertyList->SetStringProperty(InteractionEventConst::xmlParameterEventClass().c_str(), eventClass.c_str()); // MITK_INFO << "event class " << eventClass; } else if (name == InteractionEventConst::xmlTagAttribute()) { // Attributes that describe an Input Event, such as which MouseButton triggered the event,or which modifier keys // are // pressed std::string name = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName(), atts); std::string value = ReadXMLStringAttribute(InteractionEventConst::xmlParameterValue(), atts); // MITK_INFO << "tag attr " << value; m_EventPropertyList->SetStringProperty(name.c_str(), value.c_str()); } } void mitk::XML2EventParser::EndElement(const char *elementName) { std::string name(elementName); // At end of input section, all necessary infos are collected to created an interaction event. if (name == InteractionEventConst::xmlTagEventVariant()) { InteractionEvent::Pointer event = EventFactory::CreateEvent(m_EventPropertyList); if (event.IsNotNull()) { m_InteractionList.push_back(event); } else { MITK_WARN << "EventConfig: Unknown Event-Type in config. Entry skipped: " << name; } } } std::string mitk::XML2EventParser::ReadXMLStringAttribute(const std::string &name, const char **atts) { if (atts) { const char **attsIter = atts; while (*attsIter) { if (name == *attsIter) { attsIter++; return *attsIter; } attsIter += 2; } } return std::string(); } bool mitk::XML2EventParser::ReadXMLBooleanAttribute(const std::string &name, const char **atts) { std::string s = ReadXMLStringAttribute(name, atts); std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s == "TRUE"; } mitk::XML2EventParser::XML2EventParser(const std::string &filename, const us::Module *module) { if (module == nullptr) { module = us::GetModuleContext()->GetModule(); } us::ModuleResource resource = module->GetResource("Interactions/" + filename); if (!resource.IsValid()) { MITK_ERROR << "Resource not valid. State machine pattern in module " << module->GetName() << " not found: /Interactions/" << filename; return; } us::ModuleResourceStream stream(resource); this->SetStream(&stream); bool success = this->Parse(); if (!success) - MITK_ERROR << "Error occured during parsing of EventXML File."; + MITK_ERROR << "Error occurred during parsing of EventXML File."; } mitk::XML2EventParser::XML2EventParser(std::istream &inputStream) { this->SetStream(&inputStream); bool success = this->Parse(); if (!success) - MITK_ERROR << "Error occured during parsing of EventXML File."; + MITK_ERROR << "Error occurred during parsing of EventXML File."; } } diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index 08ccfa7762..6c75786446 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,724 +1,724 @@ /*============================================================================ 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 "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkSlicedGeometry3D.h" #include "mitkVtkLayerController.h" #include "mitkInteractionConst.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" // VTK #include #include #include #include #include namespace mitk { itkEventMacroDefinition(RendererResetEvent, itk::AnyEvent); } mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin) { auto mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRenderer(const char *name, vtkRenderWindow *renWin) : m_RenderWindow(nullptr), m_VtkRenderer(nullptr), m_MapperID(StandardMapperSlot::Standard2D), m_DataStorage(nullptr), m_LastUpdateTime(0), m_CameraController(nullptr), m_SliceNavigationController(nullptr), m_CameraRotationController(nullptr), m_WorldTimeGeometry(nullptr), m_CurrentWorldGeometry(nullptr), m_CurrentWorldPlaneGeometry(nullptr), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_TimeStepUpdateTime(), m_KeepDisplayedRegion(true), m_CurrentWorldPlaneGeometryData(nullptr), m_CurrentWorldPlaneGeometryNode(nullptr), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), m_EmptyWorldGeometry(true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } // instances.insert( this ); // adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySendEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); m_VtkRenderer->SetMaximumNumberOfPeels(16); if (AntiAliasing::FastApproximate == RenderingManager::GetInstance()->GetAntiAliasing()) m_VtkRenderer->UseFXAAOn(); if (nullptr == mitk::VtkLayerController::GetInstance(m_RenderWindow)) mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage *storage) { if (storage != m_DataStorage && storage != nullptr) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::Resize(int w, int h) { m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry* geometry) { if (m_WorldTimeGeometry == geometry) { return; } m_WorldTimeGeometry = geometry; this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice == slice) { return; } m_Slice = slice; this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep == timeStep) { return; } m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); this->UpdateCurrentGeometries(); } mitk::TimeStepType mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetGeometry(const itk::EventObject& geometrySendEvent) { const auto* sendEvent = dynamic_cast(&geometrySendEvent); if (nullptr == sendEvent) { return; } SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject& geometryUpdateEvent) { const auto* updateEvent = dynamic_cast(&geometryUpdateEvent); if (nullptr == updateEvent) { return; } if (m_CurrentWorldGeometry.IsNull()) { return; } const auto* slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry* geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject& geometrySliceEvent) { const auto* sliceEvent = dynamic_cast(&geometrySliceEvent); if (nullptr == sliceEvent) { return; } this->SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject& geometryTimeEvent) { const auto* timeEvent = dynamic_cast(&geometryTimeEvent); if (nullptr == timeEvent) { return; } this->SetTimeStep(timeEvent->GetPos()); } void mitk::BaseRenderer::SendUpdateSlice() { m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } void mitk::BaseRenderer::SetMapperID(MapperSlotId id) { if (m_MapperID != id) { bool useDepthPeeling = Standard3D == id; m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling); m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling); m_MapperID = id; this->Modified(); } } int* mitk::BaseRenderer::GetSize() const { return m_RenderWindow->GetSize(); } int* mitk::BaseRenderer::GetViewportSize() const { return m_VtkRenderer->GetSize(); } const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; // copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); // set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySendEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this); } } void mitk::BaseRenderer::DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { double display[3], * world; - // For the rigth z-position in display coordinates, take the focal point, convert it to display and use it for + // For the right z-position in display coordinates, take the focal point, convert it to display and use it for // correct depth. double* displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { // Seems to be the same code as above, but subclasses may contain different implementations. PickWorldPoint(displayPoint, worldIndex); } return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const { double world[4], *view; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToView(); view = this->GetVtkRenderer()->GetViewPoint(); this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]); viewPoint[0] = view[0] * this->GetViewportSize()[0]; viewPoint[1] = view[1] * this->GetViewportSize()[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const { Point3D worldPoint; m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const { Point3D worldPoint; m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToView(worldPoint,viewPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { // GetParallelScale returns half of the height of the render window in mm. // Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } void mitk::BaseRenderer::UpdateCurrentGeometries() { if (m_WorldTimeGeometry.IsNull()) { // simply mark the base renderer as modified Modified(); } if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) { m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; } auto slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { if (m_Slice >= slicedWorldGeometry->GetSlices()) { m_Slice = slicedWorldGeometry->GetSlices() - 1; } SetCurrentWorldGeometry(slicedWorldGeometry); SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); } } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry* geometry2d) { if (m_CurrentWorldPlaneGeometry != geometry2d) { m_CurrentWorldPlaneGeometry = geometry2d->Clone(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry* geometry) { m_CurrentWorldGeometry = geometry; if (geometry == nullptr) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) { m_EmptyWorldGeometry = true; } else { m_EmptyWorldGeometry = false; } } void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index c4af3d3340..3d6da2125f 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1153 +1,1153 @@ /*============================================================================ 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 #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include namespace { bool IsBinaryImage(mitk::Image* image) { if (nullptr != image && image->IsInitialized()) { bool isBinary = true; auto statistics = image->GetStatistics(); const auto numTimeSteps = image->GetTimeSteps(); for (std::remove_const_t t = 0; t < numTimeSteps; ++t) { const auto numChannels = image->GetNumberOfChannels(); for (std::remove_const_t c = 0; c < numChannels; ++c) { auto minValue = statistics->GetScalarValueMin(t, c); auto maxValue = statistics->GetScalarValueMax(t, c); if (std::abs(maxValue - minValue) < mitk::eps) continue; auto min2ndValue = statistics->GetScalarValue2ndMin(t, c); auto max2ndValue = statistics->GetScalarValue2ndMax(t, c); if (std::abs(maxValue - min2ndValue) < mitk::eps && std::abs(max2ndValue - minValue) < mitk::eps) continue; isBinary = false; break; } if (!isBinary) break; } return isBinary; } return false; } } mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and sagittal) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_PublicActors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { this->SetToInvalidState(localStorage); return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { this->SetToInvalidState(localStorage); return; } image->Update(); localStorage->m_PublicActors = localStorage->m_Actors.Get(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { this->SetToInvalidState(localStorage); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } - // set the vtk output property to true, makes sure that no unneeded mitk image convertion + // set the vtk output property to true, makes sure that no unneeded mitk image conversion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const auto *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const auto *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { - // this is needed when thick mode was enable bevore. These variable have to be reset to default values + // this is needed when thick mode was enable before. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image auto componentType = image->GetPixelType().GetComponentType(); switch (componentType) { case itk::IOComponentEnum::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; case itk::IOComponentEnum::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); localStorage->m_ShadowOutlineActor->GetProperty()->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); localStorage->m_ImageActor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_ImageActor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { localStorage->m_ShadowOutlineActor->SetVisibility(true); } else { localStorage->m_ShadowOutlineActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_ImageActor->SetTexture(localStorage->m_Texture); localStorage->m_ShadowOutlineActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(rgbConv); localStorage->m_ImageActor->GetProperty()->SetColor(rgbConv); float shadowRGB[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(shadowRGB, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double shadowRGBConv[3] = {(double)shadowRGB[0], (double)shadowRGB[1], (double)shadowRGB[2]}; // conversion to double for VTK localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(shadowRGBConv); } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_ImageActor->GetProperty()->SetOpacity(opacity); localStorage->m_ShadowOutlineActor->GetProperty()->SetOpacity(opacity); } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable", renderer)); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output - // in every interation of the rendering. + // in every iteration of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::SetToInvalidState(mitk::ImageVtkMapper2D::LocalStorage* localStorage) { localStorage->m_PublicActors = localStorage->m_EmptyActors.Get(); // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } auto *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { this->SetToInvalidState(localStorage); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPropertyList()->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp, renderer); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp, renderer); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); isBinaryImage = IsBinaryImage(centralSliceImage); if (isBinaryImage) // Potential binary image. Now take a close look. isBinaryImage = IsBinaryImage(image); } std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ mitk::LevelWindow contrast; std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // fallback } contrast.SetLevelWindow(level, window, true); } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // fallback } node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::RGBA) && (image->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } const mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetConstLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image auto* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or sagittal vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal) localStorage->m_ImageActor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_ImageActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); localStorage->m_ShadowOutlineActor->SetUserTransform(trans); localStorage->m_ShadowOutlineActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_ImageActor = vtkSmartPointer::New(); m_ShadowOutlineActor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_EmptyActors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_ImageActor->SetMapper(m_Mapper); m_ShadowOutlineActor->SetMapper(m_Mapper); m_Actors->AddPart(m_ShadowOutlineActor); m_Actors->AddPart(m_ImageActor); m_PublicActors = m_EmptyActors.Get(); } diff --git a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp index 3df46593c5..55d87b3364 100644 --- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp @@ -1,781 +1,781 @@ /*============================================================================ 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 "mitkPointSetVtkMapper2D.h" // mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include // vtk includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { double GetScreenResolution(const mitk::BaseRenderer* renderer) { if (nullptr == renderer) return 1.0; mitk::Point2D pD1, pD2; pD1[0] = 0.0; pD1[1] = 0.0; pD2[0] = 0.0; pD2[1] = 1.0; // Calculate world coordinates of in-plane screen pixels (0, 0) and (0, 1). mitk::Point3D pW1, pW2; renderer->DisplayToWorld(pD1, pW1); renderer->DisplayToWorld(pD2, pW2); // For 2D renderers, the distance between these points is the screen resolution. return pW1.EuclideanDistanceTo(pW2); } } // constructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage() { // points m_UnselectedPoints = vtkSmartPointer::New(); m_SelectedPoints = vtkSmartPointer::New(); m_ContourPoints = vtkSmartPointer::New(); // scales m_UnselectedScales = vtkSmartPointer::New(); m_SelectedScales = vtkSmartPointer::New(); // distances m_DistancesBetweenPoints = vtkSmartPointer::New(); // lines m_ContourLines = vtkSmartPointer::New(); // glyph source (provides the different shapes) m_UnselectedGlyphSource2D = vtkSmartPointer::New(); m_SelectedGlyphSource2D = vtkSmartPointer::New(); // glyphs m_UnselectedGlyph3D = vtkSmartPointer::New(); m_SelectedGlyph3D = vtkSmartPointer::New(); // polydata m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); m_VtkSelectedPointListPolyData = vtkSmartPointer::New(); m_VtkContourPolyData = vtkSmartPointer::New(); // actors m_UnselectedActor = vtkSmartPointer::New(); m_SelectedActor = vtkSmartPointer::New(); m_ContourActor = vtkSmartPointer::New(); // mappers m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkContourPolyDataMapper = vtkSmartPointer::New(); // propassembly m_PropAssembly = vtkSmartPointer::New(); } // destructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage() { } // input for this mapper ( = point set) const mitk::PointSet *mitk::PointSetVtkMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } // constructor PointSetVtkMapper2D mitk::PointSetVtkMapper2D::PointSetVtkMapper2D() : m_ShowContour(false), m_CloseContour(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(false), m_LineWidth(1), m_PointLineWidth(1), m_Point2DSize(6), m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS), m_FillShape(false), m_DistanceToPlane(4.0f), m_FixedSizeOnScreen(false) { } // destructor mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D() { } -// reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly +// reset mapper so that nothing is displayed e.g. toggle visibility of the propassembly void mitk::PointSetVtkMapper2D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } // returns propassembly vtkProp *mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } static bool makePerpendicularVector2D(const mitk::Vector2D &in, mitk::Vector2D &out) { // The dot product of orthogonal vectors is zero. // In two dimensions the slopes of perpendicular lines are negative reciprocals. if ((fabs(in[0]) > 0) && ((fabs(in[0]) > fabs(in[1])) || (in[1] == 0))) { // negative reciprocal out[0] = -in[1] / in[0]; out[1] = 1; out.Normalize(); return true; } else if (fabs(in[1]) > 0) { out[0] = 1; // negative reciprocal out[1] = -in[0] / in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); unsigned i = 0; // The vtk text actors need to be removed manually from the propassembly - // since the same vtk text actors are not overwriten within this function, + // since the same vtk text actors are not overwritten within this function, // but new actors are added to the propassembly each time this function is executed. // Thus, the actors from the last call must be removed in the beginning. for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i)); } // initialize polydata here, otherwise we have update problems when // executing this function again ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkSelectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkContourPolyData = vtkSmartPointer::New(); // get input point set and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); // only update the input data, if the property tells us to bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep); if (itkPointSet.GetPointer() == nullptr) { ls->m_PropAssembly->VisibilityOff(); return; } // iterator for point set mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin(); // PointDataContainer has additional information to each point, e.g. whether // it is selected or not mitk::PointSet::PointDataContainer::Iterator pointDataIter; pointDataIter = itkPointSet->GetPointData()->Begin(); // check if the list for the PointDataContainer is the same size as the PointsContainer. // If not, then the points were inserted manually and can not be visualized according to the PointData // (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken) { ls->m_PropAssembly->VisibilityOff(); return; } ls->m_PropAssembly->VisibilityOn(); // empty point sets, cellarrays, scalars ls->m_UnselectedPoints->Reset(); ls->m_SelectedPoints->Reset(); ls->m_ContourPoints->Reset(); ls->m_ContourLines->Reset(); ls->m_UnselectedScales->Reset(); ls->m_SelectedScales->Reset(); ls->m_DistancesBetweenPoints->Reset(); ls->m_VtkTextLabelActors.clear(); ls->m_VtkTextDistanceActors.clear(); ls->m_VtkTextAngleActors.clear(); ls->m_UnselectedScales->SetNumberOfComponents(3); ls->m_SelectedScales->SetNumberOfComponents(3); int NumberContourPoints = 0; bool pointsOnSameSideOfPlane = false; const int text2dDistance = 10; // initialize points with a random start value // current point in point set itk::Point point = pointsIter->Value(); mitk::Point3D p = point; // currently visited point mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point") mitk::Vector3D vec; // p - lastP mitk::Vector3D lastVec; // lastP - point before lastP vec.Fill(0.0); lastVec.Fill(0.0); mitk::Point2D pt2d; pt2d[0] = point[0]; // projected_p in display coordinates pt2d[1] = point[1]; mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d") mitk::Point2D preLastPt2d = pt2d; // projected_p in display coordinates before lastPt2 const mitk::PlaneGeometry *geo2D = renderer->GetCurrentWorldPlaneGeometry(); double resolution = GetScreenResolution(renderer); vtkLinearTransform *dataNodeTransform = input->GetGeometry()->GetVtkTransform(); int count = 0; for (pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++) { lastP = p; // valid for number of points count > 0 preLastPt2d = lastPt2d; // valid only for count > 1 lastPt2d = pt2d; // valid for number of points count > 0 lastVec = vec; // valid only for counter > 1 // get current point in point set point = pointsIter->Value(); // transform point { float vtkp[3]; itk2vtk(point, vtkp); dataNodeTransform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, point); } p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; renderer->WorldToDisplay(p, pt2d); vec = p - lastP; // valid only for counter > 0 // compute distance to current plane float dist = geo2D->Distance(point); // measure distance in screen pixel units if requested if (m_FixedSizeOnScreen) { dist /= resolution; } // draw markers on slices a certain distance away from the points // location according to the tolerance threshold (m_DistanceToPlane) if (dist < m_DistanceToPlane) { // is point selected or not? if (pointDataIter->Value().selected) { ls->m_SelectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane ls->m_SelectedScales->InsertNextTuple3( std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); } else { ls->m_UnselectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane ls->m_UnselectedScales->InsertNextTuple3( std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); } //---- LABEL -----// // paint label for each point if available if (dynamic_cast(this->GetDataNode()->GetProperty("label")) != nullptr) { const char *pointLabel = dynamic_cast(this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize() > 1) { std::stringstream ss; ss << pointsIter->Index(); l.append(ss.str()); } ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance); ls->m_VtkTextActor->SetInput(l.c_str()); ls->m_VtkTextActor->GetTextProperty()->SetOpacity(100); float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0}; // check if there is a color property GetDataNode()->GetColor(unselectedColor); ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor); } } // draw contour, distance text and angle text in render window // lines between points, which intersect the current plane, are drawn if (m_ShowContour && count > 0) { ScalarType distance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(point); ScalarType lastDistance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(lastP); pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5; // Points must be on different side of plane in order to draw a contour. // If "show distant lines" is enabled this condition is disregarded. if (!pointsOnSameSideOfPlane || m_ShowDistantLines) { vtkSmartPointer line = vtkSmartPointer::New(); ls->m_ContourPoints->InsertNextPoint(lastP[0], lastP[1], lastP[2]); line->GetPointIds()->SetId(0, NumberContourPoints); NumberContourPoints++; ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]); line->GetPointIds()->SetId(1, NumberContourPoints); NumberContourPoints++; ls->m_ContourLines->InsertNextCell(line); if (m_ShowDistances) // calculate and print distance between adjacent points { float distancePoints = point.EuclideanDistanceTo(lastP); std::stringstream buffer; buffer << std::fixed << std::setprecision(m_DistancesDecimalDigits) << distancePoints << " mm"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; makePerpendicularVector2D(vec2d, vec2d); // text is rendered within text2dDistance perpendicular to current line Vector2D pos2d = (lastPt2d.GetVectorFromOrigin() + pt2d.GetVectorFromOrigin()) * 0.5 + vec2d * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor); } if (m_ShowAngles && count > 1) // calculate and print angle between connected lines { std::stringstream buffer; buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector()) * 180 / vnl_math::pi << "°"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; // first arm enclosing the angle vec2d.Normalize(); Vector2D lastVec2d = lastPt2d - preLastPt2d; // second arm enclosing the angle lastVec2d.Normalize(); vec2d = vec2d - lastVec2d; // vector connecting both arms vec2d.Normalize(); // middle between two vectors that enclose the angle Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor); } } } if (pointDataIter != itkPointSet->GetPointData()->End()) { pointDataIter++; count++; } } // add each single text actor to the assembly for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i)); } //---- CONTOUR -----// // create lines between the points which intersect the plane if (m_ShowContour) { // draw line between first and last point which is rendered if (m_CloseContour && NumberContourPoints > 1) { vtkSmartPointer closingLine = vtkSmartPointer::New(); closingLine->GetPointIds()->SetId(0, 0); // index of first point closingLine->GetPointIds()->SetId(1, NumberContourPoints - 1); // index of last point ls->m_ContourLines->InsertNextCell(closingLine); } ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints); ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines); ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData); ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper); ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth); ls->m_PropAssembly->AddPart(ls->m_ContourActor); } // the point set must be transformed in order to obtain the appropriate glyph orientation // according to the current view vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer a, b = vtkSmartPointer::New(); a = geo2D->GetVtkTransform()->GetMatrix(); b->DeepCopy(a); // delete transformation from matrix, only take orientation b->SetElement(3, 3, 1); b->SetElement(2, 3, 0); b->SetElement(1, 3, 0); b->SetElement(0, 3, 0); b->SetElement(3, 2, 0); b->SetElement(3, 1, 0); b->SetElement(3, 0, 0); Vector3D spacing = geo2D->GetSpacing(); // If you find a way to simplyfy the following, feel free to change! b->SetElement(0, 0, b->GetElement(0, 0) / spacing[0]); b->SetElement(1, 0, b->GetElement(1, 0) / spacing[0]); b->SetElement(2, 0, b->GetElement(2, 0) / spacing[0]); b->SetElement(1, 1, b->GetElement(1, 1) / spacing[1]); b->SetElement(2, 1, b->GetElement(2, 1) / spacing[1]); b->SetElement(0, 2, b->GetElement(0, 2) / spacing[2]); b->SetElement(1, 2, b->GetElement(1, 2) / spacing[2]); b->SetElement(2, 2, b->GetElement(2, 2) / spacing[2]); transform->SetMatrix(b); //---- UNSELECTED POINTS -----// // apply properties to glyph ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty); if (m_FillShape) ls->m_UnselectedGlyphSource2D->FilledOn(); else ls->m_UnselectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterU = vtkSmartPointer::New(); transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort()); transformFilterU->SetTransform(transform); ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints); ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales); // apply transform of current plane to glyphs ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort()); ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData); ls->m_UnselectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0); ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_UnselectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort()); ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper); ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_UnselectedActor); //---- SELECTED POINTS -----// ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond(); ls->m_SelectedGlyphSource2D->CrossOn(); ls->m_SelectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterS = vtkSmartPointer::New(); transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort()); transformFilterS->SetTransform(transform); ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints); ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales); // apply transform of current plane to glyphs ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort()); ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData); ls->m_SelectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0); ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_SelectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort()); ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper); ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_SelectedActor); } void mitk::PointSetVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check whether the input data has been changed bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); // toggle visibility bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); ls->m_ContourActor->VisibilityOff(); ls->m_PropAssembly->VisibilityOff(); return; } else { ls->m_PropAssembly->VisibilityOn(); } node->GetBoolProperty("show contour", m_ShowContour, renderer); node->GetBoolProperty("close contour", m_CloseContour, renderer); node->GetBoolProperty("show points", m_ShowPoints, renderer); node->GetBoolProperty("show distances", m_ShowDistances, renderer); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer); node->GetBoolProperty("show angles", m_ShowAngles, renderer); node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer); node->GetIntProperty("line width", m_LineWidth, renderer); node->GetIntProperty("point line width", m_PointLineWidth, renderer); if (!node->GetFloatProperty( "point 2D size", m_Point2DSize, renderer)) // re-defined to float 2015-08-13, keep a fallback { int oldPointSize = m_Point2DSize; if (node->GetIntProperty("point 2D size", oldPointSize, renderer)) { m_Point2DSize = oldPointSize; } } node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer); node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer); node->GetBoolProperty("Pointset.2D.fixed size on screen", m_FixedSizeOnScreen, renderer); mitk::PointSetShapeProperty::Pointer shape = dynamic_cast(this->GetDataNode()->GetProperty("Pointset.2D.shape", renderer)); if (shape.IsNotNull()) { m_IDShapeProperty = shape->GetPointSetShape(); } // check for color props and use it for rendering of selected/unselected points and contour // due to different params in VTK (double/float) we have to convert float opacity = 1.0; GetDataNode()->GetOpacity(opacity, renderer); // apply color and opacity if (m_ShowPoints) { float unselectedColor[4]; double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red ls->m_UnselectedActor->VisibilityOn(); ls->m_SelectedActor->VisibilityOn(); // check if there is a color property GetDataNode()->GetColor(unselectedColor); // get selected color property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } ls->m_SelectedActor->GetProperty()->SetColor(selectedColor); ls->m_SelectedActor->GetProperty()->SetOpacity(opacity); ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity); } else { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); } if (m_ShowContour) { double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red ls->m_ContourActor->VisibilityOn(); // get contour color property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } ls->m_ContourActor->GetProperty()->SetColor(contourColor); ls->m_ContourActor->GetProperty()->SetOpacity(opacity); } else { ls->m_ContourActor->VisibilityOff(); } if (needGenerateData) { // create new vtk render objects (e.g. a circle for a point) this->CreateVTKRenderObjects(renderer); } } void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("point line width", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("point 2D size", mitk::FloatProperty::New(6), renderer, overwrite); node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("show distances", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("show angles", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show distant lines", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(false), renderer, overwrite); // fill or do not fill the glyph shape mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New(); node->AddProperty("Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite); node->AddProperty("Pointset.2D.distance to plane", mitk::FloatProperty::New(4.0f), renderer, overwrite); // show the point at a certain distance above/below the 2D imaging plane. node->AddProperty("Pointset.2D.fixed size on screen", mitk::BoolProperty::New(false), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp index 70fedcf6c8..43d6e1c746 100644 --- a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp @@ -1,417 +1,417 @@ /*============================================================================ 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 "mitkSurfaceVtkMapper2D.h" // MITK includes #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include // VTK includes #include #include #include #include #include #include #include #include #include #include #include // constructor LocalStorage mitk::SurfaceVtkMapper2D::LocalStorage::LocalStorage() { m_Mapper = vtkSmartPointer::New(); m_Mapper->ScalarVisibilityOff(); m_Actor = vtkSmartPointer::New(); m_PropAssembly = vtkSmartPointer::New(); m_PropAssembly->AddPart(m_Actor); m_CuttingPlane = vtkSmartPointer::New(); m_Cutter = vtkSmartPointer::New(); m_Cutter->SetCutFunction(m_CuttingPlane); m_Mapper->SetInputConnection(m_Cutter->GetOutputPort()); m_NormalGlyph = vtkSmartPointer::New(); m_InverseNormalGlyph = vtkSmartPointer::New(); // Source for the glyph filter m_ArrowSource = vtkSmartPointer::New(); // set small default values for fast rendering m_ArrowSource->SetTipRadius(0.05); m_ArrowSource->SetTipLength(0.20); m_ArrowSource->SetTipResolution(5); m_ArrowSource->SetShaftResolution(5); m_ArrowSource->SetShaftRadius(0.01); m_NormalGlyph->SetSourceConnection(m_ArrowSource->GetOutputPort()); m_NormalGlyph->SetVectorModeToUseNormal(); m_NormalGlyph->OrientOn(); m_InverseNormalGlyph->SetSourceConnection(m_ArrowSource->GetOutputPort()); m_InverseNormalGlyph->SetVectorModeToUseNormal(); m_InverseNormalGlyph->OrientOn(); m_NormalMapper = vtkSmartPointer::New(); m_NormalMapper->SetInputConnection(m_NormalGlyph->GetOutputPort()); m_NormalMapper->ScalarVisibilityOff(); m_InverseNormalMapper = vtkSmartPointer::New(); m_InverseNormalMapper->SetInputConnection(m_NormalGlyph->GetOutputPort()); m_InverseNormalMapper->ScalarVisibilityOff(); m_NormalActor = vtkSmartPointer::New(); m_NormalActor->SetMapper(m_NormalMapper); m_InverseNormalActor = vtkSmartPointer::New(); m_InverseNormalActor->SetMapper(m_InverseNormalMapper); m_ReverseSense = vtkSmartPointer::New(); } // destructor LocalStorage mitk::SurfaceVtkMapper2D::LocalStorage::~LocalStorage() { } const mitk::Surface *mitk::SurfaceVtkMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } // constructor PointSetVtkMapper2D mitk::SurfaceVtkMapper2D::SurfaceVtkMapper2D() { } mitk::SurfaceVtkMapper2D::~SurfaceVtkMapper2D() { } -// reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly +// reset mapper so that nothing is displayed e.g. toggle visibility of the propassembly void mitk::SurfaceVtkMapper2D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } vtkProp *mitk::SurfaceVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } void mitk::SurfaceVtkMapper2D::Update(mitk::BaseRenderer *renderer) { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) { this->ResetMapper(renderer); return; } bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) { this->ResetMapper(renderer); return; } auto *surface = static_cast(node->GetData()); if (surface == nullptr) { this->ResetMapper(renderer); return; } const auto* worldGeometry = renderer->GetWorldTimeGeometry(); const auto timeBounds = worldGeometry->GetTimeBounds(renderer->GetTimeStep()); if (!surface->GetTimeGeometry()->IsValidTimePoint(timeBounds[0])) { this->ResetMapper(renderer); return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); surface->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); localStorage->m_PropAssembly->VisibilityOn(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < surface->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::SurfaceVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { const DataNode *node = GetDataNode(); auto *surface = static_cast(node->GetData()); const TimeGeometry *dataTimeGeometry = surface->GetTimeGeometry(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); ScalarType time = renderer->GetTime(); int timestep = 0; if (time > itk::NumericTraits::NonpositiveMin()) timestep = dataTimeGeometry->TimePointToTimeStep(time); vtkSmartPointer inputPolyData = surface->GetVtkPolyData(timestep); if ((inputPolyData == nullptr) || (inputPolyData->GetNumberOfPoints() < 1)) return; // apply color and opacity read from the PropertyList this->ApplyAllProperties(renderer); const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry())) { return; } if (localStorage->m_Actor->GetMapper() == nullptr) localStorage->m_Actor->SetMapper(localStorage->m_Mapper); double origin[3]; origin[0] = planeGeometry->GetOrigin()[0]; origin[1] = planeGeometry->GetOrigin()[1]; origin[2] = planeGeometry->GetOrigin()[2]; double normal[3]; normal[0] = planeGeometry->GetNormal()[0]; normal[1] = planeGeometry->GetNormal()[1]; normal[2] = planeGeometry->GetNormal()[2]; localStorage->m_CuttingPlane->SetOrigin(origin); localStorage->m_CuttingPlane->SetNormal(normal); // Transform the data according to its geometry. // See UpdateVtkTransform documentation for details. vtkSmartPointer vtktransform = GetDataNode()->GetVtkTransform(this->GetTimestep()); vtkSmartPointer filter = vtkSmartPointer::New(); filter->SetTransform(vtktransform); filter->SetInputData(inputPolyData); localStorage->m_Cutter->SetInputConnection(filter->GetOutputPort()); localStorage->m_Cutter->Update(); bool generateNormals = false; node->GetBoolProperty("draw normals 2D", generateNormals); if (generateNormals) { localStorage->m_NormalGlyph->SetInputConnection(localStorage->m_Cutter->GetOutputPort()); localStorage->m_NormalGlyph->Update(); localStorage->m_NormalMapper->SetInputConnection(localStorage->m_NormalGlyph->GetOutputPort()); localStorage->m_PropAssembly->AddPart(localStorage->m_NormalActor); } else { localStorage->m_NormalGlyph->SetInputConnection(nullptr); localStorage->m_PropAssembly->RemovePart(localStorage->m_NormalActor); } bool generateInverseNormals = false; node->GetBoolProperty("invert normals", generateInverseNormals); if (generateInverseNormals) { localStorage->m_ReverseSense->SetInputConnection(localStorage->m_Cutter->GetOutputPort()); localStorage->m_ReverseSense->ReverseCellsOff(); localStorage->m_ReverseSense->ReverseNormalsOn(); localStorage->m_InverseNormalGlyph->SetInputConnection(localStorage->m_ReverseSense->GetOutputPort()); localStorage->m_InverseNormalGlyph->Update(); localStorage->m_InverseNormalMapper->SetInputConnection(localStorage->m_InverseNormalGlyph->GetOutputPort()); localStorage->m_PropAssembly->AddPart(localStorage->m_InverseNormalActor); } else { localStorage->m_ReverseSense->SetInputConnection(nullptr); localStorage->m_PropAssembly->RemovePart(localStorage->m_InverseNormalActor); } } void mitk::SurfaceVtkMapper2D::FixupLegacyProperties(PropertyList *properties) { // Before bug 18528, "line width" was an IntProperty, now it is a FloatProperty float lineWidth = 1.0f; if (!properties->GetFloatProperty("line width", lineWidth)) { int legacyLineWidth = lineWidth; if (properties->GetIntProperty("line width", legacyLineWidth)) { properties->ReplaceProperty("line width", FloatProperty::New(static_cast(legacyLineWidth))); } } } void mitk::SurfaceVtkMapper2D::ApplyAllProperties(mitk::BaseRenderer *renderer) { const DataNode *node = GetDataNode(); if (node == nullptr) { return; } FixupLegacyProperties(node->GetPropertyList(renderer)); FixupLegacyProperties(node->GetPropertyList()); float lineWidth = 1.0f; node->GetFloatProperty("line width", lineWidth, renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check for color and opacity properties, use it for rendering if they exists float color[3] = {1.0f, 1.0f, 1.0f}; node->GetColor(color, renderer, "color"); float opacity = 1.0f; node->GetOpacity(opacity, renderer, "opacity"); // Pass properties to VTK localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); localStorage->m_Actor->GetProperty()->SetOpacity(opacity); localStorage->m_NormalActor->GetProperty()->SetOpacity(opacity); localStorage->m_InverseNormalActor->GetProperty()->SetOpacity(opacity); localStorage->m_Actor->GetProperty()->SetLineWidth(lineWidth); // By default, the cutter will also copy/compute normals of the cut // to the output polydata. The normals will influence the // vtkPolyDataMapper lightning. To view a clean cut the lighting has // to be disabled. localStorage->m_Actor->GetProperty()->SetLighting(false); // same block for scalar data rendering as in 3D mapper mitk::TransferFunctionProperty::Pointer transferFuncProp; this->GetDataNode()->GetProperty(transferFuncProp, "Surface.TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { localStorage->m_Mapper->SetLookupTable(transferFuncProp->GetValue()->GetColorTransferFunction()); } mitk::LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull()) { localStorage->m_Mapper->SetLookupTable(lookupTableProp->GetLookupTable()->GetVtkLookupTable()); } mitk::LevelWindow levelWindow; if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelWindow")) { localStorage->m_Mapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } else if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer)) { localStorage->m_Mapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); localStorage->m_Mapper->SetScalarVisibility((scalarVisibility ? 1 : 0)); if (scalarVisibility) { mitk::VtkScalarModeProperty *scalarMode; if (this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) localStorage->m_Mapper->SetScalarMode(scalarMode->GetVtkScalarMode()); else localStorage->m_Mapper->SetScalarModeToDefault(); bool colorMode = false; this->GetDataNode()->GetBoolProperty("color mode", colorMode); localStorage->m_Mapper->SetColorMode((colorMode ? 1 : 0)); double scalarsMin = 0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMinimum", scalarsMin, renderer); double scalarsMax = 1.0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMaximum", scalarsMax, renderer); localStorage->m_Mapper->SetScalarRange(scalarsMin, scalarsMax); } // color for inverse normals float inverseNormalsColor[3] = {1.0f, 0.0f, 0.0f}; node->GetColor(inverseNormalsColor, renderer, "back color"); localStorage->m_InverseNormalActor->GetProperty()->SetColor( inverseNormalsColor[0], inverseNormalsColor[1], inverseNormalsColor[2]); // color for normals float normalsColor[3] = {0.0f, 1.0f, 0.0f}; node->GetColor(normalsColor, renderer, "front color"); localStorage->m_NormalActor->GetProperty()->SetColor(normalsColor[0], normalsColor[1], normalsColor[2]); // normals scaling float normalScaleFactor = 10.0f; - node->GetFloatProperty("front normal lenth (px)", normalScaleFactor, renderer); + node->GetFloatProperty("front normal length (px)", normalScaleFactor, renderer); localStorage->m_NormalGlyph->SetScaleFactor(normalScaleFactor); // inverse normals scaling float inverseNormalScaleFactor = 10.0f; - node->GetFloatProperty("back normal lenth (px)", inverseNormalScaleFactor, renderer); + node->GetFloatProperty("back normal length (px)", inverseNormalScaleFactor, renderer); localStorage->m_InverseNormalGlyph->SetScaleFactor(inverseNormalScaleFactor); } void mitk::SurfaceVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::CoreServicePointer aliases(mitk::CoreServices::GetPropertyAliases()); node->AddProperty("line width", FloatProperty::New(2.0f), renderer, overwrite); aliases->AddAlias("line width", "Surface.2D.Line Width", "Surface"); node->AddProperty("scalar mode", VtkScalarModeProperty::New(), renderer, overwrite); node->AddProperty("draw normals 2D", BoolProperty::New(false), renderer, overwrite); aliases->AddAlias("draw normals 2D", "Surface.2D.Normals.Draw Normals", "Surface"); node->AddProperty("invert normals", BoolProperty::New(false), renderer, overwrite); aliases->AddAlias("invert normals", "Surface.2D.Normals.Draw Inverse Normals", "Surface"); node->AddProperty("front color", ColorProperty::New(0.0, 1.0, 0.0), renderer, overwrite); aliases->AddAlias("front color", "Surface.2D.Normals.Normals Color", "Surface"); node->AddProperty("back color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); aliases->AddAlias("back color", "Surface.2D.Normals.Inverse Normals Color", "Surface"); - node->AddProperty("front normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite); - aliases->AddAlias("front normal lenth (px)", "Surface.2D.Normals.Normals Scale Factor", "Surface"); - node->AddProperty("back normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite); - aliases->AddAlias("back normal lenth (px)", "Surface.2D.Normals.Inverse Normals Scale Factor", "Surface"); + node->AddProperty("front normal length (px)", FloatProperty::New(10.0), renderer, overwrite); + aliases->AddAlias("front normal length (px)", "Surface.2D.Normals.Normals Scale Factor", "Surface"); + node->AddProperty("back normal length (px)", FloatProperty::New(10.0), renderer, overwrite); + aliases->AddAlias("back normal length (px)", "Surface.2D.Normals.Inverse Normals Scale Factor", "Surface"); node->AddProperty("layer", IntProperty::New(100), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp b/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp index 5bbc93108e..243003e4d3 100644 --- a/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp +++ b/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp @@ -1,580 +1,580 @@ /*============================================================================ 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 "vtkMitkLevelWindowFilter.h" #include "vtkObjectFactory.h" #include #include #include #include #include #include #include #include // used for acos etc. #include // used for PI #include #include static const double PI = itk::Math::pi; vtkStandardNewMacro(vtkMitkLevelWindowFilter); vtkMitkLevelWindowFilter::vtkMitkLevelWindowFilter() : m_LookupTable(nullptr), m_OpacityFunction(nullptr), m_MinOpacity(0.0), m_MaxOpacity(255.0) { // MITK_INFO << "mitk level/window filter uses " << GetNumberOfThreads() << " thread(s)"; } vtkMitkLevelWindowFilter::~vtkMitkLevelWindowFilter() { } vtkMTimeType vtkMitkLevelWindowFilter::GetMTime() { vtkMTimeType mTime = this->vtkObject::GetMTime(); vtkMTimeType time; if (this->m_LookupTable != nullptr) { time = this->m_LookupTable->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } void vtkMitkLevelWindowFilter::SetLookupTable(vtkScalarsToColors *lookupTable) { if (m_LookupTable != lookupTable) { m_LookupTable = lookupTable; this->Modified(); } } vtkScalarsToColors *vtkMitkLevelWindowFilter::GetLookupTable() { return m_LookupTable; } void vtkMitkLevelWindowFilter::SetOpacityPiecewiseFunction(vtkPiecewiseFunction *opacityFunction) { if (m_OpacityFunction != opacityFunction) { m_OpacityFunction = opacityFunction; this->Modified(); } } // This code was copied from the iil. The template works only for float and double. // Internal method which should never be used anywhere else and should not be in th header. // Convert color pixels from (R,G,B) to (H,S,I). // Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. template void RGBtoHSI(T *RGB, T *HSI) { T R = RGB[0], G = RGB[1], B = RGB[2], nR = (R < 0 ? 0 : (R > 255 ? 255 : R)) / 255, nG = (G < 0 ? 0 : (G > 255 ? 255 : G)) / 255, nB = (B < 0 ? 0 : (B > 255 ? 255 : B)) / 255, m = nR < nG ? (nR < nB ? nR : nB) : (nG < nB ? nG : nB), theta = (T)(std::acos(0.5f * ((nR - nG) + (nR - nB)) / std::sqrt(std::pow(nR - nG, 2) + (nR - nB) * (nG - nB))) * 180 / PI), sum = nR + nG + nB; T H = 0, S = 0, I = 0; if (theta > 0) H = (nB <= nG) ? theta : 360 - theta; if (sum > 0) S = 1 - 3 / sum * m; I = sum / 3; HSI[0] = (T)H; HSI[1] = (T)S; HSI[2] = (T)I; } // This code was copied from the iil. The template works only for float and double. // Internal method which should never be used anywhere else and should not be in th header. // Convert color pixels from (H,S,I) to (R,G,B). template void HSItoRGB(T *HSI, T *RGB) { T H = (T)HSI[0], S = (T)HSI[1], I = (T)HSI[2], a = I * (1 - S), R = 0, G = 0, B = 0; if (H < 120) { B = a; R = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180))); G = 3 * I - (R + B); } else if (H < 240) { H -= 120; R = a; G = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180))); B = 3 * I - (R + G); } else { H -= 240; G = a; B = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180))); R = 3 * I - (G + B); } R *= 255; G *= 255; B *= 255; RGB[0] = (T)(R < 0 ? 0 : (R > 255 ? 255 : R)); RGB[1] = (T)(G < 0 ? 0 : (G > 255 ? 255 : G)); RGB[2] = (T)(B < 0 ? 0 : (B > 255 ? 255 : B)); } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnRGBA(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkLookupTable *lookupTable; const int maxC = inData->GetNumberOfScalarComponents(); double tableRange[2]; lookupTable = dynamic_cast(self->GetLookupTable()); lookupTable->GetTableRange(tableRange); // parameters for RGB level window const double scale = (tableRange[1] - tableRange[0] > 0 ? 255.0 / (tableRange[1] - tableRange[0]) : 0.0); const double bias = tableRange[0] * scale; // parameters for opaque level window const double scaleOpac = (self->GetMaxOpacity() - self->GetMinOpacity() > 0 ? 255.0 / (self->GetMaxOpacity() - self->GetMinOpacity()) : 0.0); const double biasOpac = self->GetMinOpacity() * scaleOpac; int y = outExt[2]; - // Loop through ouput pixels + // Loop through output pixels while (!outputIt.IsAtEnd()) { T *inputSI = inputIt.BeginSpan(); T *outputSI = outputIt.BeginSpan(); T *outputSIEnd = outputIt.EndSpan(); if (y >= clippingBounds[2] && y < clippingBounds[3]) { int x = outExt[0]; while (outputSI != outputSIEnd) { if (x >= clippingBounds[0] && x < clippingBounds[1]) { double rgb[3], alpha, hsi[3]; // level/window mechanism for intensity in HSI space rgb[0] = static_cast(*inputSI); inputSI++; rgb[1] = static_cast(*inputSI); inputSI++; rgb[2] = static_cast(*inputSI); inputSI++; RGBtoHSI(rgb, hsi); hsi[2] = hsi[2] * 255.0 * scale - bias; hsi[2] = (hsi[2] > 255.0 ? 255 : (hsi[2] < 0.0 ? 0 : hsi[2])); hsi[2] /= 255.0; HSItoRGB(hsi, rgb); *outputSI = static_cast(rgb[0]); outputSI++; *outputSI = static_cast(rgb[1]); outputSI++; *outputSI = static_cast(rgb[2]); outputSI++; unsigned char finalAlpha = 255; // RGBA case if (maxC >= 4) { // level/window mechanism for opacity alpha = static_cast(*inputSI); inputSI++; alpha = alpha * scaleOpac - biasOpac; if (alpha > 255.0) { alpha = 255.0; } else if (alpha < 0.0) { alpha = 0.0; } finalAlpha = static_cast(alpha); for (int c = 4; c < maxC; c++) inputSI++; } *outputSI = static_cast(finalAlpha); outputSI++; } else { inputSI += maxC; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; } x++; } } else { while (outputSI != outputSIEnd) { *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalarsFast( vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); double tableRange[2]; // access vtkLookupTable auto *lookupTable = dynamic_cast(self->GetLookupTable()); lookupTable->GetTableRange(tableRange); // access elements of the vtkLookupTable auto *realLookupTable = lookupTable->GetPointer(0); size_t maxIndex = lookupTable->GetNumberOfColors() - 1; const float scale = (tableRange[1] - tableRange[0] > 0 ? (maxIndex + 1) / (tableRange[1] - tableRange[0]) : 0.0); // ensuring that starting point is zero float bias = -tableRange[0] * scale; // due to later conversion to int for rounding bias += 0.5f; - // Loop through ouput pixels + // Loop through output pixels while (!outputIt.IsAtEnd()) { unsigned char *outputSI = outputIt.BeginSpan(); unsigned char *outputSIEnd = outputIt.EndSpan(); T *inputSI = inputIt.BeginSpan(); while (outputSI != outputSIEnd) { // map to an index auto idx = std::min(static_cast(std::max(0, static_cast(*inputSI * scale + bias))), maxIndex) * 4; memcpy(outputSI, &realLookupTable[idx], 4); inputSI++; outputSI += 4; } inputIt.NextSpan(); outputIt.NextSpan(); } } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalars(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkScalarsToColors *lookupTable = self->GetLookupTable(); int y = outExt[2]; - // Loop through ouput pixels + // Loop through output pixels while (!outputIt.IsAtEnd()) { unsigned char *outputSI = outputIt.BeginSpan(); const unsigned char * const outputSIEnd = outputIt.EndSpan(); // do we iterate over the inner vertical clipping bounds if (y >= clippingBounds[2] && y < clippingBounds[3]) { T *inputSI = inputIt.BeginSpan(); int x = outExt[0]; while (outputSI != outputSIEnd) { // is this pixel within horizontal clipping bounds if (x >= clippingBounds[0] && x < clippingBounds[1]) { // fetching original value auto grayValue = static_cast(*inputSI); // applying lookuptable memcpy(outputSI, lookupTable->MapValue(grayValue), 4); } else { // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int memset(outputSI, 0, 4); } inputSI++; outputSI += 4; x++; } } else { // outer vertical clipping bounds - write a transparent RGBA line as ints while (outputSI != outputSIEnd) { *reinterpret_cast(outputSI) = 0; outputSI += 4; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalarsCTF(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); auto *lookupTable = dynamic_cast(self->GetLookupTable()); vtkPiecewiseFunction *opacityFunction = self->GetOpacityPiecewiseFunction(); int y = outExt[2]; - // Loop through ouput pixels + // Loop through output pixels while (!outputIt.IsAtEnd()) { unsigned char *outputSI = outputIt.BeginSpan(); unsigned char *outputSIEnd = outputIt.EndSpan(); // do we iterate over the inner vertical clipping bounds if (y >= clippingBounds[2] && y < clippingBounds[3]) { T *inputSI = inputIt.BeginSpan(); int x = outExt[0]; while (outputSI != outputSIEnd) { // is this pixel within horizontal clipping bounds if (x >= clippingBounds[0] && x < clippingBounds[1]) { // fetching original value auto grayValue = static_cast(*inputSI); // applying directly colortransferfunction // because vtkColorTransferFunction::MapValue is not threadsafe double rgba[4]; lookupTable->GetColor(grayValue, rgba); // RGB mapping rgba[3] = 1.0; if (opacityFunction) rgba[3] = opacityFunction->GetValue(grayValue); // Alpha mapping for (int i = 0; i < 4; ++i) { outputSI[i] = static_cast(255.0 * rgba[i] + 0.5); } } else { // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int *reinterpret_cast(outputSI) = 0; } inputSI++; outputSI += 4; x++; } } else { // outer vertical clipping bounds - write a transparent RGBA line as ints while (outputSI != outputSIEnd) { *reinterpret_cast(outputSI) = 0; outputSI += 4; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } int vtkMitkLevelWindowFilter::RequestInformation(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { vtkInformation *outInfo = outputVector->GetInformationObject(0); // do nothing except copy scalar type info this->CopyInputArrayAttributesToOutput(request, inputVector, outputVector); vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, 4); return 1; } // Method to run the filter in different threads. void vtkMitkLevelWindowFilter::ThreadedExecute(vtkImageData *inData, vtkImageData *outData, int extent[6], int /*id*/) { if (inData->GetNumberOfScalarComponents() > 2) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnRGBA(this, inData, outData, extent, m_ClippingBounds, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else { bool dontClip = extent[2] >= m_ClippingBounds[2] && extent[3] <= m_ClippingBounds[3] && extent[0] >= m_ClippingBounds[0] && extent[1] <= m_ClippingBounds[1]; if (this->GetLookupTable()) this->GetLookupTable()->Build(); auto *vlt = dynamic_cast(this->GetLookupTable()); auto *ctf = dynamic_cast(this->GetLookupTable()); bool linearLookupTable = vlt && vlt->GetScale() == VTK_SCALE_LINEAR; bool useFast = dontClip && linearLookupTable; if (ctf) { switch (inData->GetScalarType()) { vtkTemplateMacro(vtkApplyLookupTableOnScalarsCTF( this, inData, outData, extent, m_ClippingBounds, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else if (useFast) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnScalarsFast(this, inData, outData, extent, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else { switch (inData->GetScalarType()) { vtkTemplateMacro(vtkApplyLookupTableOnScalars( this, inData, outData, extent, m_ClippingBounds, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } } } // void vtkMitkLevelWindowFilter::ExecuteInformation( // vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData)) //{ //} void vtkMitkLevelWindowFilter::SetMinOpacity(double minOpacity) { m_MinOpacity = minOpacity; } inline double vtkMitkLevelWindowFilter::GetMinOpacity() const { return m_MinOpacity; } void vtkMitkLevelWindowFilter::SetMaxOpacity(double maxOpacity) { m_MaxOpacity = maxOpacity; } inline double vtkMitkLevelWindowFilter::GetMaxOpacity() const { return m_MaxOpacity; } void vtkMitkLevelWindowFilter::SetClippingBounds(double *bounds) // TODO does double[4] work?? { for (unsigned int i = 0; i < 4; ++i) m_ClippingBounds[i] = bounds[i]; } diff --git a/Modules/Core/src/Rendering/vtkMitkThickSlicesFilter.cpp b/Modules/Core/src/Rendering/vtkMitkThickSlicesFilter.cpp index 5486b6340d..21bda749cc 100644 --- a/Modules/Core/src/Rendering/vtkMitkThickSlicesFilter.cpp +++ b/Modules/Core/src/Rendering/vtkMitkThickSlicesFilter.cpp @@ -1,416 +1,416 @@ /*============================================================================ 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 "vtkMitkThickSlicesFilter.h" #include "vtkDataArray.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkStreamingDemandDrivenPipeline.h" #include #include vtkStandardNewMacro(vtkMitkThickSlicesFilter); //---------------------------------------------------------------------------- // Construct an instance of vtkMitkThickSlicesFilter filter. vtkMitkThickSlicesFilter::vtkMitkThickSlicesFilter() { this->HandleBoundaries = 1; this->Dimensionality = 2; this->m_CurrentMode = MIP; // by default process active point scalars this->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); } //---------------------------------------------------------------------------- void vtkMitkThickSlicesFilter::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "HandleBoundaries: " << this->HandleBoundaries << "\n"; os << indent << "Dimensionality: " << this->Dimensionality << "\n"; } //---------------------------------------------------------------------------- int vtkMitkThickSlicesFilter::RequestInformation(vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // Get input and output pipeline information. vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); // Get the input whole extent. int extent[6]; inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent); // Reduce 3D to 2D output extent[4] = extent[5] = 0; // Store the new whole extent for the output. outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent, 6); /* - // Set the number of point data componets to the number of + // Set the number of point data components to the number of // components in the gradient vector. vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_DOUBLE, this->Dimensionality); */ return 1; } //---------------------------------------------------------------------------- // This method computes the input extent necessary to generate the output. int vtkMitkThickSlicesFilter::RequestUpdateExtent(vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // Get input and output pipeline information. vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); // Get the input whole extent. int wholeExtent[6]; inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent); // Get the requested update extent from the output. int inUExt[6]; outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inUExt); /*inUExt[4] -= 5; inUExt[5] += 5; if (inUExt[4] < wholeExtent[4]) */ inUExt[4] = wholeExtent[4]; /*if (inUExt[5] > wholeExtent[5]) */ inUExt[5] = wholeExtent[5]; - // Store the update extent needed from the intput. + // Store the update extent needed from the input. inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inUExt, 6); return 1; } //---------------------------------------------------------------------------- // This execute method handles boundaries. // it handles boundaries. Pixels are just replicated to get values // out of extent. template void vtkMitkThickSlicesFilterExecute(vtkMitkThickSlicesFilter *self, vtkImageData *inData, T *inPtr, vtkImageData *outData, T *outPtr, int outExt[6], int /*id*/) { int idxX, idxY; int maxX, maxY; vtkIdType inIncX, inIncY, inIncZ; vtkIdType outIncX, outIncY, outIncZ; // int axesNum; int *inExt = inData->GetExtent(); int *wholeExtent; vtkIdType *inIncs; // int useYMin, useYMax, useXMin, useXMax; // find the region to loop over maxX = outExt[1] - outExt[0]; maxY = outExt[3] - outExt[2]; // maxZ = outExt[5] - outExt[4]; // Get the dimensionality of the gradient. // axesNum = self->GetDimensionality(); // Get increments to march through data inData->GetContinuousIncrements(outExt, inIncX, inIncY, inIncZ); outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); /* // The data spacing is important for computing the gradient. // central differences (2 * ratio). // Negative because below we have (min - max) for dx ... inData->GetSpacing(r); r[0] = -0.5 / r[0]; r[1] = -0.5 / r[1]; r[2] = -0.5 / r[2]; */ // get some other info we need inIncs = inData->GetIncrements(); wholeExtent = inData->GetExtent(); // Move the pointer to the correct starting position. inPtr += (outExt[0] - inExt[0]) * inIncs[0] + (outExt[2] - inExt[2]) * inIncs[1] + (outExt[4] - inExt[4]) * inIncs[2]; - // Loop through ouput pixels + // Loop through output pixels int _minZ = /*-5 + outExt[4]; if( _minZ < wholeExtent[4]) _minZ=*/wholeExtent[4]; int _maxZ = /* 5 + outExt[4]; if( _maxZ > wholeExtent[5]) _maxZ=*/wholeExtent[5]; if (_maxZ < _minZ) return; double invNum = 1.0 / (_maxZ - _minZ + 1); switch (self->GetThickSliceMode()) { default: case vtkMitkThickSlicesFilter::MIP: { // MIP for (idxY = 0; idxY <= maxY; idxY++) { // useYMin = ((idxY + outExt[2]) <= wholeExtent[2]) ? 0 : -inIncs[1]; // useYMax = ((idxY + outExt[2]) >= wholeExtent[3]) ? 0 : inIncs[1]; for (idxX = 0; idxX <= maxX; idxX++) { // useXMin = ((idxX + outExt[0]) <= wholeExtent[0]) ? 0 : -inIncs[0]; // useXMax = ((idxX + outExt[0]) >= wholeExtent[1]) ? 0 : inIncs[0]; T mip = inPtr[_minZ * inIncs[2]]; for (int z = _minZ + 1; z <= _maxZ; z++) { T value = inPtr[z * inIncs[2]]; if (value > mip) mip = value; } // do X axis *outPtr = mip; outPtr++; inPtr++; } outPtr += outIncY; inPtr += inIncY; } } break; case vtkMitkThickSlicesFilter::SUM: { // MIP for (idxY = 0; idxY <= maxY; idxY++) { // useYMin = ((idxY + outExt[2]) <= wholeExtent[2]) ? 0 : -inIncs[1]; // useYMax = ((idxY + outExt[2]) >= wholeExtent[3]) ? 0 : inIncs[1]; for (idxX = 0; idxX <= maxX; idxX++) { // useXMin = ((idxX + outExt[0]) <= wholeExtent[0]) ? 0 : -inIncs[0]; // useXMax = ((idxX + outExt[0]) >= wholeExtent[1]) ? 0 : inIncs[0]; double sum = 0; for (int z = _minZ; z <= _maxZ; z++) { T value = inPtr[z * inIncs[2]]; sum += value; } // do X axis *outPtr = static_cast(invNum * sum); outPtr++; inPtr++; } outPtr += outIncY; inPtr += inIncY; } } break; case vtkMitkThickSlicesFilter::WEIGHTED: { const int size = _maxZ - _minZ; std::vector weights(size); double mean = 0.5 * double(_minZ + _maxZ); double sigma_sq = double(size) / 6.0; sigma_sq *= sigma_sq; double sum = 0; int i = 0; for (int z = _minZ + 1; z <= _maxZ; z++) { double val = exp(-(((double)z - mean) / sigma_sq)); weights[i++] = val; sum += val; } for (i = 0; i < size; i++) { weights[i] /= sum; } for (idxY = 0; idxY <= maxY; idxY++) { // useYMin = ((idxY + outExt[2]) <= wholeExtent[2]) ? 0 : -inIncs[1]; // useYMax = ((idxY + outExt[2]) >= wholeExtent[3]) ? 0 : inIncs[1]; for (idxX = 0; idxX <= maxX; idxX++) { // useXMin = ((idxX + outExt[0]) <= wholeExtent[0]) ? 0 : -inIncs[0]; // useXMax = ((idxX + outExt[0]) >= wholeExtent[1]) ? 0 : inIncs[0]; T mip = inPtr[_minZ * inIncs[2]]; i = 0; double mymip = 0; for (int z = _minZ + 1; z <= _maxZ; z++) { double value = inPtr[z * inIncs[2]]; mymip += value * weights[i++]; } mip = static_cast(mymip); // do X axis *outPtr = mip; outPtr++; inPtr++; } outPtr += outIncY; inPtr += inIncY; } } break; case vtkMitkThickSlicesFilter::MINIP: { for (idxY = 0; idxY <= maxY; idxY++) { for (idxX = 0; idxX <= maxX; idxX++) { T mip = inPtr[_minZ * inIncs[2]]; for (int z = _minZ + 1; z <= _maxZ; z++) { T value = inPtr[z * inIncs[2]]; if (value < mip) mip = value; } // do X axis *outPtr = mip; outPtr++; inPtr++; } outPtr += outIncY; inPtr += inIncY; } } break; case vtkMitkThickSlicesFilter::MEAN: { const int size = _maxZ - _minZ; // MEAN for (idxY = 0; idxY <= maxY; idxY++) { for (idxX = 0; idxX <= maxX; idxX++) { long double sum = 0; for (int z = _minZ; z <= _maxZ; z++) { T value = inPtr[z * inIncs[2]]; sum += value; } T mip = static_cast(sum / size); // do X axis *outPtr = mip; outPtr++; inPtr++; } outPtr += outIncY; inPtr += inIncY; } } break; } } int vtkMitkThickSlicesFilter::RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { if (!this->Superclass::RequestData(request, inputVector, outputVector)) { return 0; } vtkImageData *output = vtkImageData::GetData(outputVector); vtkDataArray *outArray = output->GetPointData()->GetScalars(); std::ostringstream newname; newname << (outArray->GetName() ? outArray->GetName() : "") << "Gradient"; outArray->SetName(newname.str().c_str()); // Why not pass the original array? if (this->GetInputArrayToProcess(0, inputVector)) { output->GetPointData()->AddArray(this->GetInputArrayToProcess(0, inputVector)); } return 1; } //---------------------------------------------------------------------------- // This method contains a switch statement that calls the correct // templated function for the input data type. This method does handle // boundary conditions. void vtkMitkThickSlicesFilter::ThreadedRequestData(vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *, vtkImageData ***inData, vtkImageData **outData, int outExt[6], int threadId) { // Get the input and output data objects. vtkImageData *input = inData[0][0]; vtkImageData *output = outData[0]; - // The ouptut scalar type must be double to store proper gradients. + // The output scalar type must be double to store proper gradients. /* if(output->GetScalarType() != VTK_DOUBLE) { vtkErrorMacro("Execute: output ScalarType is " << output->GetScalarType() << "but must be double."); return; } */ vtkDataArray *inputArray = this->GetInputArrayToProcess(0, inputVector); if (!inputArray) { vtkErrorMacro("No input array was found. Cannot execute"); return; } // Gradient makes sense only with one input component. This is not // a Jacobian filter. if (inputArray->GetNumberOfComponents() != 1) { vtkErrorMacro("Execute: input has more than one component. " "The input to gradient should be a single component image. " "Think about it. If you insist on using a color image then " "run it though RGBToHSV then ExtractComponents to get the V " "components. That's probably what you want anyhow."); return; } void *inPtr = inputArray->GetVoidPointer(0); void *outPtr = output->GetScalarPointerForExtent(outExt); switch (inputArray->GetDataType()) { vtkTemplateMacro(vtkMitkThickSlicesFilterExecute( this, input, static_cast(inPtr), output, static_cast(outPtr), outExt, threadId)); default: vtkErrorMacro("Execute: Unknown ScalarType " << input->GetScalarType()); return; } } diff --git a/Modules/Core/src/mitkCoreObjectFactory.cpp b/Modules/Core/src/mitkCoreObjectFactory.cpp index 4e0574e882..084c19c0b3 100644 --- a/Modules/Core/src/mitkCoreObjectFactory.cpp +++ b/Modules/Core/src/mitkCoreObjectFactory.cpp @@ -1,454 +1,454 @@ /*============================================================================ 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 "mitkCoreObjectFactory.h" #include "mitkConfig.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkEnumerationProperty.h" #include "mitkGeometry3D.h" #include "mitkGeometryData.h" #include "mitkImage.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkPlaneGeometryDataMapper2D.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkPointSet.h" #include "mitkPointSetVtkMapper2D.h" #include "mitkPointSetVtkMapper3D.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "mitkSlicedGeometry3D.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkSurface.h" #include "mitkSurfaceVtkMapper2D.h" #include "mitkSurfaceVtkMapper3D.h" #include "mitkTimeGeometry.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include // Legacy Support: #include #include #include void mitk::CoreObjectFactory::RegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: registering extra factory of type " << factory->GetNameOfClass(); m_ExtraFactories.insert(CoreObjectFactoryBase::Pointer(factory)); // Register Legacy Reader and Writer this->RegisterLegacyReaders(factory); this->RegisterLegacyWriters(factory); } void mitk::CoreObjectFactory::UnRegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: un-registering extra factory of type " << factory->GetNameOfClass(); this->UnRegisterLegacyWriters(factory); this->UnRegisterLegacyReaders(factory); try { m_ExtraFactories.erase(factory); } catch ( const std::exception &e ) { - MITK_ERROR << "Caugt exception while unregistering: " << e.what(); + MITK_ERROR << "Caught exception while unregistering: " << e.what(); } } mitk::CoreObjectFactory::Pointer mitk::CoreObjectFactory::GetInstance() { static mitk::CoreObjectFactory::Pointer instance; if (instance.IsNull()) { instance = mitk::CoreObjectFactory::New(); } return instance; } mitk::CoreObjectFactory::~CoreObjectFactory() { for (auto iter = m_LegacyReaders.begin(); iter != m_LegacyReaders.end(); ++iter) { for (auto &elem : iter->second) { delete elem; } } for (auto iter = m_LegacyWriters.begin(); iter != m_LegacyWriters.end(); ++iter) { for (auto &elem : iter->second) { delete elem; } } } void mitk::CoreObjectFactory::SetDefaultProperties(mitk::DataNode *node) { if (node == nullptr) return; mitk::DataNode::Pointer nodePointer = node; mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { mitk::ImageVtkMapper2D::SetDefaultProperties(node); } mitk::PlaneGeometryData::Pointer planeGeometry = dynamic_cast(node->GetData()); if (planeGeometry.IsNotNull()) { mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(node); } mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); if (surface.IsNotNull()) { mitk::SurfaceVtkMapper2D::SetDefaultProperties(node); mitk::SurfaceVtkMapper3D::SetDefaultProperties(node); } mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNotNull()) { mitk::PointSetVtkMapper2D::SetDefaultProperties(node); mitk::PointSetVtkMapper3D::SetDefaultProperties(node); } for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { (*it)->SetDefaultProperties(node); } } mitk::CoreObjectFactory::CoreObjectFactory() { static bool alreadyDone = false; if (!alreadyDone) { CreateFileExtensionsMap(); // RegisterLegacyReaders(this); // RegisterLegacyWriters(this); alreadyDone = true; } } mitk::Mapper::Pointer mitk::CoreObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::Mapper::Pointer tmpMapper = nullptr; // check whether extra factories provide mapper for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { tmpMapper = (*it)->CreateMapper(node, id); if (tmpMapper.IsNotNull()) newMapper = tmpMapper; } if (newMapper.IsNull()) { mitk::BaseData *data = node->GetData(); if (id == mitk::BaseRenderer::Standard2D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::ImageVtkMapper2D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PlaneGeometryDataMapper2D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::SurfaceVtkMapper2D::New(); // cast because SetDataNode is not virtual auto *castedMapper = dynamic_cast(newMapper.GetPointer()); castedMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PointSetVtkMapper2D::New(); newMapper->SetDataNode(node); } } else if (id == mitk::BaseRenderer::Standard3D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::SurfaceVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PointSetVtkMapper3D::New(); newMapper->SetDataNode(node); } } } return newMapper; } std::string mitk::CoreObjectFactory::GetFileExtensions() { MultimapType aMap; for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { aMap = (*it)->GetFileExtensionsMap(); this->MergeFileExtensions(m_FileExtensionsMap, aMap); } this->CreateFileExtensions(m_FileExtensionsMap, m_FileExtensions); return m_FileExtensions.c_str(); } void mitk::CoreObjectFactory::MergeFileExtensions(MultimapType &fileExtensionsMap, MultimapType inputMap) { std::pair pairOfIter; for (auto it = inputMap.begin(); it != inputMap.end(); ++it) { bool duplicateFound = false; pairOfIter = fileExtensionsMap.equal_range((*it).first); for (auto it2 = pairOfIter.first; it2 != pairOfIter.second; ++it2) { // cout << " [" << (*it).first << ", " << (*it).second << "]" << endl; std::string aString = (*it2).second; if (aString.compare((*it).second) == 0) { // cout << " DUP!! [" << (*it).first << ", " << (*it).second << "]" << endl; duplicateFound = true; break; } } if (!duplicateFound) { fileExtensionsMap.insert(std::pair((*it).first, (*it).second)); } } } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } void mitk::CoreObjectFactory::CreateFileExtensionsMap() { /* m_FileExtensionsMap.insert(std::pair("*.dcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DCM", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.dc3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DC3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.seq", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.seq.gz", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.dcm", "Sets of 2D slices")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "Sets of 2D slices")); */ } std::string mitk::CoreObjectFactory::GetSaveFileExtensions() { MultimapType aMap; for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { aMap = (*it)->GetSaveFileExtensionsMap(); this->MergeFileExtensions(m_SaveFileExtensionsMap, aMap); } this->CreateFileExtensions(m_SaveFileExtensionsMap, m_SaveFileExtensions); return m_SaveFileExtensions.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } mitk::CoreObjectFactory::FileWriterList mitk::CoreObjectFactory::GetFileWriters() { FileWriterList allWriters = m_FileWriters; // sort to merge lists later on typedef std::set FileWriterSet; FileWriterSet fileWritersSet; fileWritersSet.insert(allWriters.begin(), allWriters.end()); // collect all extra factories for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { FileWriterList list2 = (*it)->GetFileWriters(); // add them to the sorted set fileWritersSet.insert(list2.begin(), list2.end()); } // write back to allWriters to return a list allWriters.clear(); allWriters.insert(allWriters.end(), fileWritersSet.begin(), fileWritersSet.end()); return allWriters; } void mitk::CoreObjectFactory::MapEvent(const mitk::Event *, const int) { } std::string mitk::CoreObjectFactory::GetDescriptionForExtension(const std::string &extension) { std::multimap fileExtensionMap = GetSaveFileExtensionsMap(); for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) if (it->first == extension) return it->second; - return ""; // If no matching extension was found, return emtpy string + return ""; // If no matching extension was found, return empty string } void mitk::CoreObjectFactory::RegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory) { // We are not really interested in the string, just call the method since // many readers initialize the map the first time when this method is called factory->GetFileExtensions(); std::map> extensionsByCategories; std::multimap fileExtensionMap = factory->GetFileExtensionsMap(); for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) { std::string extension = it->first; // remove "*." extension = extension.erase(0, 2); extensionsByCategories[it->second].push_back(extension); } for (auto &extensionsByCategorie : extensionsByCategories) { m_LegacyReaders[factory].push_back( new mitk::LegacyFileReaderService(extensionsByCategorie.second, extensionsByCategorie.first)); } } void mitk::CoreObjectFactory::UnRegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory) { auto iter = m_LegacyReaders.find(factory); if (iter != m_LegacyReaders.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyReaders.erase(iter); } } void mitk::CoreObjectFactory::RegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory) { // Get all external Writers mitk::CoreObjectFactory::FileWriterList writers = factory->GetFileWriters(); // We are not really interested in the string, just call the method since // many writers initialize the map the first time when this method is called factory->GetSaveFileExtensions(); MultimapType fileExtensionMap = factory->GetSaveFileExtensionsMap(); for (auto it = writers.begin(); it != writers.end(); ++it) { std::vector extensions = (*it)->GetPossibleFileExtensions(); if (extensions.empty()) continue; std::string description; for (auto ext = extensions.begin(); ext != extensions.end(); ++ext) { if (ext->empty()) continue; std::string extension = *ext; std::string extensionWithStar = extension; if (extension.find_first_of('*') == 0) { // remove "*." extension = extension.substr(0, extension.size() - 2); } else { extensionWithStar.insert(extensionWithStar.begin(), '*'); } for (auto fileExtensionIter = fileExtensionMap.begin(); fileExtensionIter != fileExtensionMap.end(); ++fileExtensionIter) { if (fileExtensionIter->first == extensionWithStar) { description = fileExtensionIter->second; break; } } if (!description.empty()) break; } if (description.empty()) { description = std::string("Legacy ") + (*it)->GetNameOfClass() + " Reader"; } mitk::FileWriter::Pointer fileWriter(it->GetPointer()); mitk::LegacyFileWriterService *lfws = new mitk::LegacyFileWriterService(fileWriter, description); m_LegacyWriters[factory].push_back(lfws); } } void mitk::CoreObjectFactory::UnRegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory) { auto iter = m_LegacyWriters.find(factory); if (iter != m_LegacyWriters.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyWriters.erase(iter); } } diff --git a/Modules/Core/test/DirectOverlayTest.cpp b/Modules/Core/test/DirectOverlayTest.cpp index 79d3ea7406..91157c5226 100644 --- a/Modules/Core/test/DirectOverlayTest.cpp +++ b/Modules/Core/test/DirectOverlayTest.cpp @@ -1,249 +1,249 @@ /*============================================================================ 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 class DirectOverlayTestClass { public: template static void InternalThreshold(const itk::Image *image, mitk::Image::Pointer &output, const double th[]) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::BinaryThresholdImageFilter BinaryThresholdFilterType; typename BinaryThresholdFilterType::Pointer thresholder = BinaryThresholdFilterType::New(); thresholder->SetInput(image); thresholder->SetLowerThreshold(th[0]); thresholder->SetUpperThreshold(th[1]); thresholder->SetInsideValue(255); thresholder->SetOutsideValue(0); thresholder->Update(); output = mitk::ImportItkImage(thresholder->GetOutput()); // mitk::IOUtil::Save( output, "/tmp/out.nii" ); std::cout << "extra line"; } template static void InternalThreshold2(const itk::Image *image, itk::Image::Pointer &output, const double th[]) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::BinaryThresholdImageFilter BinaryThresholdFilterType; typename BinaryThresholdFilterType::Pointer thresholder = BinaryThresholdFilterType::New(); thresholder->SetInput(image); thresholder->SetLowerThreshold(th[0]); thresholder->SetUpperThreshold(th[1]); thresholder->SetInsideValue(255); thresholder->SetOutsideValue(0); thresholder->Update(); output = thresholder->GetOutput(); } static void TestOverlay(mitk::Image::Pointer original, mitk::Image::Pointer truth, const double lower, const double upper) { mitk::Image::Pointer overlayImage; const double th[] = {lower, upper}; typedef itk::Image ImageType; ImageType::Pointer itkOverlayImage = ImageType::New(); AccessByItk_2(original, InternalThreshold, overlayImage, th); /* AccessFixedDimensionByItk_2( original, InternalThreshold2, 3, itkOverlayImage, th ); overlayImage = mitk::ImportItkImage( itkOverlayImage ); */ // mitk::IOUtil::Save(truth, "/tmp/truth_TestOverlay.nii"); try { // mitk::Image::Pointer temp = overlayImage; mitk::IOUtil::Save(overlayImage, "/tmp/overlayImage_TestOverlay.nrrd"); } catch (const itk::ExceptionObject &e) { MITK_ERROR << "Save image: exception : " << e.what(); } typedef itk::Image InputImageType; InputImageType::Pointer overlayItk; try { mitk::CastToItkImage(overlayImage, overlayItk); } catch (const mitk::Exception &e) { - MITK_ERROR << "(CAST) Catched exception while creating accessor " << e.what(); + MITK_ERROR << "(CAST) Caught exception while creating accessor " << e.what(); // MITK_TEST_FAILED_MSG("Exception for ouverlay image"); } /* typedef itk::ImageFileWriter< InputImageType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName("/tmp/overlayITK_TestOverlay.nii"); writer->SetInput(overlayItk); writer->Update(); */ InputImageType::Pointer truthItk; mitk::CastToItkImage(truth, truthItk); bool difference = false; /* try { typedef unsigned int TPixel; itk::ImageRegionConstIteratorWithIndex< InputImageType > iter( truthItk, truthItk->GetLargestPossibleRegion() ); iter.GoToBegin(); mitk::ImagePixelReadAccessor< TPixel, 3 > readAccessor( overlayImage, overlayImage->GetVolumeData(0), mitk::ImageAccessorBase::ExceptionIfLocked ); while( !iter.IsAtEnd() ) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex( iter.GetIndex() ); difference |= ( ref != val ); //if( difference ) //{ std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; //} ++iter; } } catch( const mitk::Exception &e) { - MITK_ERROR << "Catched exception while creating accessor "<< e.what(); + MITK_ERROR << "Caught exception while creating accessor "<< e.what(); //MITK_TEST_FAILED_MSG("Exception for ouverlay image"); } */ /* typedef itk::Testing::ComparisonImageFilter ComparisonImageFilterType; ComparisonImageFilterType::Pointer comp = ComparisonImageFilterType::New(); comp->SetValidInput(truthItk); comp->SetTestInput(overlayItk); try { comp->Update(); } catch( const itk::ExceptionObject& e) { MITK_ERROR << "ITK Exception: " << e.what(); } */ typedef unsigned int TPixel; itk::ImageRegionConstIteratorWithIndex iter(truthItk, truthItk->GetLargestPossibleRegion()); itk::ImageRegionConstIteratorWithIndex iter2(overlayItk, overlayItk->GetLargestPossibleRegion()); iter.GoToBegin(); unsigned int counter = 0; while (!iter.IsAtEnd() && !iter2.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = iter2.Get(); if (ref != val) { counter++; // std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; ++iter2; } std::cout << "Differs in " << counter << "voxels" << std::endl; MITK_TEST_CONDITION_REQUIRED( // comp->GetNumberOfPixelsWithDifferences() == 0, counter == 0, "Comparing overlay with ground truth") } static void TestDirectOverlay(char *in, char *gt, const int lower, const int upper) { mitk::Image::Pointer original = mitk::IOUtil::Load(in); mitk::Image::Pointer truth = mitk::IOUtil::Load(gt); if (original.IsNotNull() && original->GetDimension() == 3 && truth.IsNotNull() && truth->GetDimension() == 3 && upper > lower) { TestOverlay(original, truth, lower, upper); } else { MITK_TEST_FAILED_MSG(<< "Invalid parameters"); } } }; int DirectOverlayTest(int argc, char *argv[]) { MITK_TEST_BEGIN("DirectOverlay") MITK_TEST_CONDITION_REQUIRED(argc >= 5, "File to load has been specified on the command line"); unsigned int lower = 0, upper = 0; try { sscanf(argv[3], "%u", &lower); sscanf(argv[4], "%u", &upper); // lower = boost::lexical_cast(argv[3]); // upper = boost::lexical_cast(argv[4]); MITK_INFO << "Got values: " << lower << " : " << upper; } catch (std::exception &e) { MITK_TEST_FAILED_MSG(<< e.what()); } DirectOverlayTestClass::TestDirectOverlay(argv[1], argv[2], lower, upper); MITK_TEST_END() } diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index 9aa105bf67..1a8abcb9b6 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,198 +1,198 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# - #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more + #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesn't exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFloatToStringTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataIOTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkImageCastTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetLocaleTest.cpp mitkPointSetWriterTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkPropertyPersistenceTest.cpp mitkPropertyPersistenceInfoTest.cpp mitkPropertyRelationRuleBaseTest.cpp mitkPropertyRelationsTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp mitkCompositePixelValueToStringTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkNodePredicateDataPropertyTest.cpp mitkNodePredicateFunctionTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkPropertyKeyPathTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp mitkArbitraryTimeGeometryTest.cpp mitkItkImageIOTest.cpp mitkLevelWindowManagerTest.cpp mitkVectorPropertyTest.cpp mitkTemporoSpatialStringPropertyTest.cpp mitkPropertyNameHelperTest.cpp mitkNodePredicateGeometryTest.cpp mitkNodePredicateSubGeometryTest.cpp mitkPreferenceListReaderOptionsFunctorTest.cpp mitkGenericIDRelationRuleTest.cpp mitkSourceImageRelationRuleTest.cpp mitkTemporalJoinImagesFilterTest.cpp ) set(MODULE_RENDERING_TESTS mitkPointSetDataInteractorTest.cpp mitkSurfaceVtkMapper2DTest.cpp mitkSurfaceVtkMapper2D3DTest.cpp ) # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGE US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACE binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkEventConfigTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkPointSetReaderTest.cpp mitkImageEqualTest.cpp mitkRotatedSlice4DTest.cpp mitkPlaneGeometryDataMapper2DTest.cpp ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) set(MODULE_CUSTOM_TESTS ${MODULE_CUSTOM_TESTS} mitkSurfaceDepthSortingTest.cpp) endif() set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) diff --git a/Modules/Core/test/mitkAbstractTransformGeometryTest.cpp b/Modules/Core/test/mitkAbstractTransformGeometryTest.cpp index ff8defb0c3..01051a9e7e 100644 --- a/Modules/Core/test/mitkAbstractTransformGeometryTest.cpp +++ b/Modules/Core/test/mitkAbstractTransformGeometryTest.cpp @@ -1,241 +1,241 @@ /*============================================================================ 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 "mitkExternAbstractTransformGeometry.h" #include "mitkImage.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" #include #include int mitkAbstractTransformGeometryTest(int /*argc*/, char * /*argv*/ []) { mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM; std::cout << "Initializing an x-/y-plane (xyPlane) as parameter plane by InitializeStandardPlane(rightVector, " "downVector, spacing = nullptr): " << std::endl; mitk::PlaneGeometry::Pointer xyPlane = mitk::PlaneGeometry::New(); width = 100; widthInMM = width; height = 200; heightInMM = height; mitk::ScalarType bounds[6] = {0, width, 0, height, 0, 1}; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); xyPlane->InitializeStandardPlane(right, bottom); xyPlane->SetOrigin(origin); xyPlane->SetSizeInUnits(width, height); std::cout << "Creating AbstractTransformGeometry: " << std::endl; mitk::ExternAbstractTransformGeometry::Pointer abstractgeometry = mitk::ExternAbstractTransformGeometry::New(); std::cout << "Setting xyPlane as parameter plane of AbstractTransformGeometry: " << std::endl; abstractgeometry->SetPlane(xyPlane); std::cout << "Testing whether the bounds of xyPlane and the parametric bounds of AbstractTransformGeometry are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(xyPlane->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(xyPlane->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Testing whether the parametic bounds of AbstractTransformGeometry and the bounds of the plane accessed " + std::cout << "Testing whether the parametric bounds of AbstractTransformGeometry and the bounds of the plane accessed " "from there are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Change parametic bounds of AbstractTransformGeometry and test whether they are equal to the bounds of " + std::cout << "Change parametric bounds of AbstractTransformGeometry and test whether they are equal to the bounds of " "the plane accessed from there: " << std::endl; height = 300; bounds[3] = height; abstractgeometry->SetParametricBounds(bounds); if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Initializing an phi-/theta-plane (sphereParameterPlane) as parameter plane by " "InitializeStandardPlane(rightVector, downVector, spacing = nullptr): " << std::endl; mitk::PlaneGeometry::Pointer sphereParameterPlane = mitk::PlaneGeometry::New(); width = 100; widthInMM = 2 * vnl_math::pi; height = 200; heightInMM = vnl_math::pi; mitk::ScalarType radiusInMM = 2.5; mitk::FillVector3D(origin, radiusInMM, 0, widthInMM); mitk::FillVector3D(right, 0, 0, -widthInMM); mitk::FillVector3D(bottom, 0, heightInMM, 0); sphereParameterPlane->InitializeStandardPlane(right, bottom); sphereParameterPlane->SetOrigin(origin); sphereParameterPlane->SetSizeInUnits(width, height); std::cout << "Creating an vtkSphericalTransform (sphericalTransform) to use with sphereParameterPlane: " << std::endl; vtkSphericalTransform *sphericalTransform = vtkSphericalTransform::New(); std::cout << "Setting sphereParameterPlane as parameter plane and sphericalTransform as transform of " "AbstractTransformGeometry: " << std::endl; abstractgeometry->SetPlane(sphereParameterPlane); abstractgeometry->SetVtkAbstractTransform(sphericalTransform); std::cout << "Testing whether the bounds of sphereParameterPlane and the parametric bounds of " "AbstractTransformGeometry are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(sphereParameterPlane->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(sphereParameterPlane->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Testing whether the parametic bounds of AbstractTransformGeometry and the bounds of the plane accessed " + std::cout << "Testing whether the parametric bounds of AbstractTransformGeometry and the bounds of the plane accessed " "from there are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mapping Map(pt2d_mm(phi=Pi,theta=Pi/2.0), pt3d_mm) and compare with expected (-radius, 0, 0): "; mitk::Point2D pt2d_mm; mitk::Point3D pt3d_mm, expected_pt3d_mm; pt2d_mm[0] = vnl_math::pi; pt2d_mm[1] = vnl_math::pi_over_2; mitk::FillVector3D(expected_pt3d_mm, -radiusInMM, 0, 0); abstractgeometry->Map(pt2d_mm, pt3d_mm); if (mitk::Equal(pt3d_mm, expected_pt3d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected: "; mitk::Point2D testpt2d_mm; abstractgeometry->Map(pt3d_mm, testpt2d_mm); if (mitk::Equal(pt2d_mm, testpt2d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; mitk::Point2D pt2d_units; pt2d_units[0] = width / 2.0; pt2d_units[1] = height / 2.0; pt2d_mm[0] = widthInMM / 2.0; pt2d_mm[1] = heightInMM / 2.0; abstractgeometry->IndexToWorld(pt2d_units, testpt2d_mm); if (mitk::Equal(pt2d_mm, testpt2d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Change parametic bounds of AbstractTransformGeometry and test whether they are equal to the bounds of " + std::cout << "Change parametric bounds of AbstractTransformGeometry and test whether they are equal to the bounds of " "the plane accessed from there: " << std::endl; height = 300; bounds[3] = height; abstractgeometry->SetParametricBounds(bounds); if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; pt2d_units[0] = width / 2.0; pt2d_units[1] = height / 2.0; pt2d_mm[0] = widthInMM / 2.0; pt2d_mm[1] = heightInMM / 2.0; abstractgeometry->IndexToWorld(pt2d_units, testpt2d_mm); if (mitk::Equal(pt2d_mm, testpt2d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; // std::cout << "Testing availability and type (PlaneGeometry) of first geometry in the SlicedGeometry3D: "; // mitk::PlaneGeometry* accessedplanegeometry3 = // dynamic_cast(slicedWorldGeometry->GetGeometry2D(0)); // if(accessedplanegeometry3==nullptr) //{ // std::cout<<"[FAILED]"<GetAxisVector(0), planegeometry3->GetAxisVector(0))==false) || // (mitk::Equal(accessedplanegeometry3->GetAxisVector(1), planegeometry3->GetAxisVector(1))==false) || // (mitk::Equal(accessedplanegeometry3->GetAxisVector(2), planegeometry3->GetAxisVector(2))==false)) //{ // std::cout<<"[FAILED]"<Delete(); std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } diff --git a/Modules/Core/test/mitkAbstractTransformPlaneGeometryTest.cpp b/Modules/Core/test/mitkAbstractTransformPlaneGeometryTest.cpp index fdd87e7df8..d23d7c4f80 100644 --- a/Modules/Core/test/mitkAbstractTransformPlaneGeometryTest.cpp +++ b/Modules/Core/test/mitkAbstractTransformPlaneGeometryTest.cpp @@ -1,242 +1,242 @@ /*============================================================================ 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 "mitkImage.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkAbstractTransformPlaneGeometry.h" #include #include #include #include int mitkVtkAbstractTransformPlaneGeometryTest(int argc, char *argv[]) { mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM; std::cout << "Initializing an x-/y-plane (xyPlane) as parameter plane by InitializeStandardPlane(rightVector, " "downVector, spacing = nullptr): " << std::endl; mitk::PlaneGeometry::Pointer xyPlane = mitk::PlaneGeometry::New(); width = 100; widthInMM = width; height = 200; heightInMM = height; mitk::ScalarType bounds[6] = {0, width, 0, height, 0, 1}; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); xyPlane->InitializeStandardPlane(right, bottom); xyPlane->SetOrigin(origin); xyPlane->SetSizeInUnits(width, height); std::cout << "Creating VtkAbstractTransformPlaneGeometry: " << std::endl; mitk::VtkAbstractTransformPlaneGeometry::Pointer abstractgeometry = mitk::VtkAbstractTransformPlaneGeometry::New(); std::cout << "Setting xyPlane as parameter plane of VtkAbstractTransformPlaneGeometry: " << std::endl; abstractgeometry->SetPlane(xyPlane); std::cout << "Testing whether the bounds of xyPlane and the parametric bounds of VtkAbstractTransformPlaneGeometry " "are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(xyPlane->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(xyPlane->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Testing whether the parametic bounds of VtkAbstractTransformPlaneGeometry and the bounds of the plane " + std::cout << "Testing whether the parametric bounds of VtkAbstractTransformPlaneGeometry and the bounds of the plane " "accessed from there are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Change parametic bounds of VtkAbstractTransformPlaneGeometry and test whether they are equal to the " + std::cout << "Change parametric bounds of VtkAbstractTransformPlaneGeometry and test whether they are equal to the " "bounds of the plane accessed from there: " << std::endl; height = 300; bounds[3] = height; abstractgeometry->SetParametricBounds(bounds); if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Initializing an phi-/theta-plane (sphereParameterPlane) as parameter plane by " "InitializeStandardPlane(rightVector, downVector, spacing = nullptr): " << std::endl; mitk::PlaneGeometry::Pointer sphereParameterPlane = mitk::PlaneGeometry::New(); width = 100; widthInMM = 2 * vnl_math::pi; height = 200; heightInMM = vnl_math::pi; mitk::ScalarType radiusInMM = 2.5; mitk::FillVector3D(origin, radiusInMM, 0, widthInMM); mitk::FillVector3D(right, 0, 0, -widthInMM); mitk::FillVector3D(bottom, 0, heightInMM, 0); sphereParameterPlane->InitializeStandardPlane(right, bottom); sphereParameterPlane->SetOrigin(origin); sphereParameterPlane->SetSizeInUnits(width, height); std::cout << "Creating an vtkSphericalTransform (sphericalTransform) to use with sphereParameterPlane: " << std::endl; vtkSphericalTransform *sphericalTransform = vtkSphericalTransform::New(); std::cout << "Setting sphereParameterPlane as parameter plane and sphericalTransform as transform of " "VtkAbstractTransformPlaneGeometry: " << std::endl; abstractgeometry->SetPlane(sphereParameterPlane); abstractgeometry->SetVtkAbstractTransform(sphericalTransform); std::cout << "Testing whether the bounds of sphereParameterPlane and the parametric bounds of " "VtkAbstractTransformPlaneGeometry are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(sphereParameterPlane->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(sphereParameterPlane->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Testing whether the parametic bounds of VtkAbstractTransformPlaneGeometry and the bounds of the plane " + std::cout << "Testing whether the parametric bounds of VtkAbstractTransformPlaneGeometry and the bounds of the plane " "accessed from there are equal: "; if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mapping Map(pt2d_mm(phi=Pi,theta=Pi/2.0), pt3d_mm) and compare with expected (-radius, 0, 0): "; mitk::Point2D pt2d_mm; mitk::Point3D pt3d_mm, expected_pt3d_mm; pt2d_mm[0] = vnl_math::pi; pt2d_mm[1] = vnl_math::pi_over_2; mitk::FillVector3D(expected_pt3d_mm, -radiusInMM, 0, 0); abstractgeometry->Map(pt2d_mm, pt3d_mm); if (mitk::Equal(pt3d_mm, expected_pt3d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected: "; mitk::Point2D testpt2d_mm; abstractgeometry->Map(pt3d_mm, testpt2d_mm); if (mitk::Equal(pt2d_mm, testpt2d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; mitk::Point2D pt2d_units; pt2d_units[0] = width / 2.0; pt2d_units[1] = height / 2.0; pt2d_mm[0] = widthInMM / 2.0; pt2d_mm[1] = heightInMM / 2.0; abstractgeometry->IndexToWorld(pt2d_units, testpt2d_mm); if (mitk::Equal(pt2d_mm, testpt2d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; - std::cout << "Change parametic bounds of VtkAbstractTransformPlaneGeometry and test whether they are equal to the " + std::cout << "Change parametric bounds of VtkAbstractTransformPlaneGeometry and test whether they are equal to the " "bounds of the plane accessed from there: " << std::endl; height = 300; bounds[3] = height; abstractgeometry->SetParametricBounds(bounds); if ((mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMinimum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMinimum()) == false) || (mitk::Equal(const_cast(abstractgeometry->GetParametricBoundingBox())->GetMaximum(), const_cast(abstractgeometry->GetPlane()->GetBoundingBox())->GetMaximum()) == false)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; pt2d_units[0] = width / 2.0; pt2d_units[1] = height / 2.0; pt2d_mm[0] = widthInMM / 2.0; pt2d_mm[1] = heightInMM / 2.0; abstractgeometry->IndexToWorld(pt2d_units, testpt2d_mm); if (mitk::Equal(pt2d_mm, testpt2d_mm) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; // std::cout << "Testing availability and type (PlaneGeometry) of first geometry in the SlicedGeometry3D: "; // mitk::PlaneGeometry* accessedplanegeometry3 = // dynamic_cast(slicedWorldGeometry->GetGeometry2D(0)); // if(accessedplanegeometry3==nullptr) //{ // std::cout<<"[FAILED]"<GetAxisVector(0), planegeometry3->GetAxisVector(0))==false) || // (mitk::Equal(accessedplanegeometry3->GetAxisVector(1), planegeometry3->GetAxisVector(1))==false) || // (mitk::Equal(accessedplanegeometry3->GetAxisVector(2), planegeometry3->GetAxisVector(2))==false)) //{ // std::cout<<"[FAILED]"<SetElement(i, j, i + j * j); // just some not trivial value // set the expected homogenous matrix result expectedHomogenousMatrix = vtkMatrix4x4::New(); expectedHomogenousMatrix->Zero(); expectedHomogenousMatrix->SetElement(0, 1, -1); expectedHomogenousMatrix->SetElement(1, 0, 1); expectedHomogenousMatrix->SetElement(0, 3, 2); expectedHomogenousMatrix->SetElement(1, 3, 3); expectedHomogenousMatrix->SetElement(2, 3, 4); expectedHomogenousMatrix->SetElement(3, 3, 1); } static void TearDown() { if (homogenMatrix) homogenMatrix->Delete(); if (expectedHomogenousMatrix) expectedHomogenousMatrix->Delete(); } /** * This first test basically assures that we understand the usage of AffineTransform3D correct. * Meaning that the rotation is set by SetMatrix and the translation is set by SetOffset */ static void testIfPointIsTransformedAsExpected(void) { Setup(); /** construct the transformation */ AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetOffset(offset); transform->SetMatrix(rotation); TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); /** Let a point be transformed by the AffineTransform3D */ Point3D pointTransformedByAffineTransform3D = transform->TransformPoint(originalPoint); /** assert that the transformation was successful */ bool pointCorrect = true; for (int i = 0; i < 3; i++) // only first three since no homogenous coordinates pointCorrect &= Equal(pointTransformedByAffineTransform3D[i], expectedPointAfterTransformation[i]); - MITK_TEST_CONDITION(pointCorrect, "Point has been correctly transformed by AffineTranform3D") + MITK_TEST_CONDITION(pointCorrect, "Point has been correctly transformed by AffineTransform3D") TearDown(); } /** * This test ensures that the function TransferItkTransformToVtkMatrix translates the AffineTransform3D * correctly to a VtkMatrix4x4 */ static void testTransferItkTransformToVtkMatrix(void) { Setup(); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetOffset(offset); transform->SetMatrix(rotation); TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); bool allElementsEqual = true; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) allElementsEqual &= Equal(homogenMatrix->GetElement(i, j), expectedHomogenousMatrix->GetElement(i, j)); MITK_TEST_CONDITION(allElementsEqual, "Homogenous Matrix is set as expected") TearDown(); } /** * This test basically is just a sanity check and should be PASSED exactly when both * testTransferItkTransformToVtkMatrix and testIfPointIsTransformedAsExpected are PASSED. * Tests if we get the same * result by using the AffineTransform3D to transform a point or the vtkMatrix4x4 which we got by applying * the TransferItkTransformToVtkMatrix function. This test e.g. ensures we made no mistake in our * reference results. * */ static void testIfBothTransformationsProduceSameResults(void) { Setup(); /** * create both a AffineTransform3D * and let this AffineTransform describe also a homogenous 4x4 Matrix vtkMatrix * by using our transfer method */ AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetOffset(offset); transform->SetMatrix(rotation); TransferItkTransformToVtkMatrix(transform.GetPointer(), homogenMatrix); /** Let a point be transformed by the AffineTransform3D and by homogenMatrix */ Point3D pointTransformedByAffineTransform3D = transform->TransformPoint(originalPoint); double *pointTransformedByHomogenous = homogenMatrix->MultiplyDoublePoint(originalPointDouble); /* check if results match */ bool pointsMatch = true; for (int i = 0; i < 3; i++) // only first three since no homogenous coordinates pointsMatch &= Equal(pointTransformedByAffineTransform3D[i], pointTransformedByHomogenous[i]); bool homogenousComponentCorrect = Equal(1, pointTransformedByHomogenous[3]); MITK_TEST_CONDITION(pointsMatch && homogenousComponentCorrect, "Point transformed by AffineTransform and homogenous coordinates match") TearDown(); } /** * This test shall ensure and document the basic functionality of the * itk AffineTransformation functionality and test some basic transformation functionalities provided by mitk. */ int mitkAffineTransformBaseTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("AffineTransformationBaseTest"); testIfPointIsTransformedAsExpected(); testTransferItkTransformToVtkMatrix(); testIfBothTransformationsProduceSameResults(); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp index 68f3ed89c5..fa2987808b 100644 --- a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp +++ b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp @@ -1,531 +1,531 @@ /*============================================================================ 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 "mitkArbitraryTimeGeometry.h" #include "mitkGeometry3D.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkArbitraryTimeGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkArbitraryTimeGeometryTestSuite); // Test the append method MITK_TEST(CountTimeSteps); MITK_TEST(GetMinimumTimePoint); MITK_TEST(GetMaximumTimePoint); MITK_TEST(GetTimeBounds); MITK_TEST(IsValidTimePoint); MITK_TEST(TimeStepToTimePoint); MITK_TEST(TimePointToTimeStep); MITK_TEST(GetGeometryCloneForTimeStep); MITK_TEST(GetGeometryForTimeStep); MITK_TEST(GetGeometryForTimePoint); MITK_TEST(IsValid); MITK_TEST(Expand); MITK_TEST(ReplaceTimeStepGeometries); MITK_TEST(ClearAllGeometries); MITK_TEST(AppendNewTimeStep); MITK_TEST(HasCollapsedFinalTimeStep); CPPUNIT_TEST_SUITE_END(); private: mitk::Geometry3D::Pointer m_Geometry1; mitk::Geometry3D::Pointer m_Geometry2; mitk::Geometry3D::Pointer m_Geometry3; mitk::Geometry3D::Pointer m_Geometry3_5; mitk::Geometry3D::Pointer m_Geometry4; mitk::Geometry3D::Pointer m_Geometry5; mitk::Geometry3D::Pointer m_InvalidGeometry; mitk::Geometry3D::Pointer m_NewGeometry; mitk::TimePointType m_Geometry1MinTP; mitk::TimePointType m_Geometry2MinTP; mitk::TimePointType m_Geometry3MinTP; mitk::TimePointType m_Geometry3_5MinTP; mitk::TimePointType m_Geometry4MinTP; mitk::TimePointType m_Geometry5MinTP; mitk::TimePointType m_NewGeometryMinTP; mitk::TimePointType m_Geometry1MaxTP; mitk::TimePointType m_Geometry2MaxTP; mitk::TimePointType m_Geometry3MaxTP; mitk::TimePointType m_Geometry3_5MaxTP; mitk::TimePointType m_Geometry4MaxTP; mitk::TimePointType m_Geometry5MaxTP; mitk::TimePointType m_NewGeometryMaxTP; mitk::ArbitraryTimeGeometry::Pointer m_emptyTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_initTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_12345TimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedEnd; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedInterim; public: void setUp() override { m_Geometry1 = mitk::Geometry3D::New(); m_Geometry2 = mitk::Geometry3D::New(); m_Geometry3 = mitk::Geometry3D::New(); m_Geometry3_5 = mitk::Geometry3D::New(); m_Geometry4 = mitk::Geometry3D::New(); m_Geometry5 = mitk::Geometry3D::New(); m_Geometry1MinTP = 1; m_Geometry2MinTP = 2; m_Geometry3MinTP = 3; m_Geometry3_5MinTP = 3.5; m_Geometry4MinTP = 4; m_Geometry5MinTP = 5; m_Geometry1MaxTP = 1.9; m_Geometry2MaxTP = 2.9; m_Geometry3MaxTP = 3.9; m_Geometry3_5MaxTP = 3.9; m_Geometry4MaxTP = 4.9; m_Geometry5MaxTP = 5.9; m_NewGeometry = mitk::Geometry3D::New(); m_NewGeometryMinTP = 20; m_NewGeometryMaxTP = 21.9; mitk::Point3D origin(42); m_NewGeometry->SetOrigin(origin); m_emptyTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_emptyTimeGeometry->ClearAllGeometries(); m_initTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_initTimeGeometry->Initialize(); m_12345TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_12345TimeGeometry->ClearAllGeometries(); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP); m_123TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometry->ClearAllGeometries(); m_123TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_123TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); m_123TimeGeometryWithCollapsedEnd = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometryWithCollapsedEnd->ClearAllGeometries(); m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MinTP); m_123TimeGeometryWithCollapsedInterim = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometryWithCollapsedInterim->ClearAllGeometries(); m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MinTP); m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); } void tearDown() override {} void CountTimeSteps() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 0, "Testing CountTimeSteps with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->CountTimeSteps() == 1, "Testing CountTimeSteps with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing CountTimeSteps with m_12345TimeGeometry"); } void GetMinimumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 1.0, "Testing GetMinimumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_initTimeGeometry"); /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed/reevaluated as soon as T28262 is solved and we know + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed/reevaluated as soon as T28262 is solved and we know // how time geometries should behave in the future! MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 3.0, "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry"); // Deactivated falling original test // MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 2.9, // "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry"); - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// } void GetMaximumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 0.0, "Testing GetMaximumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint() == 1.0, "Testing GetMaximumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 5.9, "Testing GetMaximumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); } void GetTimeBounds() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[0] == 1.0, "Testing GetTimeBounds lower part with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[1] == 0.0, "Testing GetTimeBounds with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[1] == 1.0, "Testing GetTimeBounds with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[1] == 5.9, "Testing GetTimeBounds with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_initTimeGeometry"); /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed/reevaluated as soon as T28262 is solved and we know + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed/reevaluated as soon as T28262 is solved and we know // how time geometries should behave in the future! MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 4.0, "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry"); // Deactivated falling original test // MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 3.9, // "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry"); - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[1] == 4.9, "Testing GetTimeBounds(3) with m_12345TimeGeometry"); } void IsValidTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(0) == true, "Testing IsValidTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(1) == true, "Testing IsValidTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(2.5) == true, "Testing IsValidTimePoint(2.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(5.89) == true, "Testing IsValidTimePoint(5.89) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(0) == false, "Testing IsValidTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(1) == true, "Testing IsValidTimeStep(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_12345TimeGeometry"); //checked collapsed cases MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->IsValidTimePoint(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == false, - "Testing that m_123TimeGeometryWithCollapsedInterim does not inclued the max bound in validity"); + "Testing that m_123TimeGeometryWithCollapsedInterim does not included the max bound in validity"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->IsValidTimePoint(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == true, - "Testing that m_123TimeGeometryWithCollapsedEnd does inclued the max bound in validity, because it has an collapsed final time step. (see also T27259)"); + "Testing that m_123TimeGeometryWithCollapsedEnd does included the max bound in validity, because it has an collapsed final time step. (see also T27259)"); } void TimeStepToTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(0) == 1.0, "Testing TimeStepToTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(1) == 2.0, "Testing TimeStepToTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_12345TimeGeometry"); } void TimePointToTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(3.5) == 0, "Testing TimePointToTimeStep(3.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(3.5) == m_initTimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(3.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(3.5) == 2, "Testing TimePointToTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(5.8) == 0, "Testing TimePointToTimeStep(5.8) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(5.8) == m_initTimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(5.8) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.8) == 4, "Testing TimePointToTimeStep(5.8) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.9) == m_12345TimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(5.9) with m_12345TimeGeometry"); //checked collapsed cases MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->TimePointToTimeStep(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == m_123TimeGeometryWithCollapsedInterim->CountTimeSteps(), - "Testing m_123TimeGeometryWithCollapsedInterim does not map the max time poit."); + "Testing m_123TimeGeometryWithCollapsedInterim does not map the max time point."); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->TimePointToTimeStep(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == 2, "Testing that m_123TimeGeometryWithCollapsedEnd does map the max bound, because it has an collapsed final time step. (see also T27259)"); } void GetGeometryCloneForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryCloneForTimeStep(0).IsNull(), "Testing GetGeometryCloneForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_12345TimeGeometry"); } void GetGeometryForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimeStep(0).IsNull(), "Testing GetGeometryForTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(0).IsNotNull(), "Testing GetGeometryForTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(1).IsNull(), "Testing GetGeometryForTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(3).GetPointer() == m_Geometry4.GetPointer(), "Testing GetGeometryForTimePoint(3) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4).GetPointer() == m_Geometry5.GetPointer(), "Testing GetGeometryForTimePoint(4) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimeStep(5).IsNull(), "Testing GetGeometryForTimePoint(5) with m_12345TimeGeometry"); } void GetGeometryForTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(0).IsNotNull(), "Testing GetGeometryForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(1.5).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimeStep(1.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(3.5).GetPointer() == m_Geometry3.GetPointer(), "Testing GetGeometryForTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(5.9).IsNull(), "Testing GetGeometryForTimeStep(5.9) with m_12345TimeGeometry"); } void IsValid() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValid() == false, "Testing IsValid() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValid() == true, "Testing IsValid() with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValid() == true, "Testing IsValid() with m_12345TimeGeometry"); } void Expand() { m_12345TimeGeometry->Expand(3); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing Expand(3) doesn't change m_12345TimeGeometry"); m_12345TimeGeometry->Expand(7); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 7, "Testing Expand(7) with m_12345TimeGeometry"); } void ReplaceTimeStepGeometries() { // Test replace time step geometries m_12345TimeGeometry->ReplaceTimeStepGeometries(m_NewGeometry); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing ReplaceTimeStepGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if first geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if last geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); } void ClearAllGeometries() { // Test clear all geometries m_12345TimeGeometry->ClearAllGeometries(); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); } void AppendNewTimeStep() { // Test append MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(nullptr, 0, 1)); MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry3_5,m_Geometry3_5MinTP,m_Geometry3_5MaxTP)); MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MaxTP, m_Geometry4MinTP)); //valid but inverted bounds m_emptyTimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 1, "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 4, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 4.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 3, "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 3.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 4, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 4.9, "Testing AppendNewTimeStep() with m_123TimeGeometry"); /////////////////////////////////////// - // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. - // This workarround should be removed/reevaluated as soon as T28262 is solved and we know + // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. + // This workaround should be removed/reevaluated as soon as T28262 is solved and we know // how time geometries should behave in the future! MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 4.0, "Testing AppendNewTimeStep() with m_123TimeGeometry"); // Deactivated falling original test // MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 3.9, // "Testing AppendNewTimeStep() with m_123TimeGeometry"); - // End of workarround for T27883 + // End of workaround for T27883 ////////////////////////////////////// } void HasCollapsedFinalTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->HasCollapsedFinalTimeStep() == true, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedEnd"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedInterim"); } }; MITK_TEST_SUITE_REGISTRATION(mitkArbitraryTimeGeometry) diff --git a/Modules/Core/test/mitkBaseGeometryTest.cpp b/Modules/Core/test/mitkBaseGeometryTest.cpp index b975f48661..dad9c06c3a 100644 --- a/Modules/Core/test/mitkBaseGeometryTest.cpp +++ b/Modules/Core/test/mitkBaseGeometryTest.cpp @@ -1,1680 +1,1680 @@ /*============================================================================ 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 "mitkTestingMacros.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; typedef itk::BoundingBox BoundingBox; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // Dummy instance of abstract base class class DummyTestClass : public mitk::BaseGeometry { public: DummyTestClass(){}; DummyTestClass(const DummyTestClass &other) : BaseGeometry(other){}; ~DummyTestClass() override{}; mitkClassMacro(DummyTestClass, mitk::BaseGeometry); itkNewMacro(Self); mitkNewMacro1Param(Self, const Self &); itk::LightObject::Pointer InternalClone() const override { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } protected: void PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const override{}; //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use {}. //## If this class is inherited from a subclass of BaseGeometry, call {Superclass::Pre...();};, example: // SlicedGeometry3D class void PreSetSpacing(const mitk::Vector3D &/*aSpacing*/) override{}; }; class mitkBaseGeometryTestSuite : public mitk::TestFixture { // List of Tests CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); // Constructor MITK_TEST(TestConstructors); MITK_TEST(TestInitialize); // Set MITK_TEST(TestSetOrigin); MITK_TEST(TestSetBounds); MITK_TEST(TestSetFloatBounds); MITK_TEST(TestSetFloatBoundsDouble); MITK_TEST(TestSetFrameOfReferenceID); MITK_TEST(TestSetIndexToWorldTransform); MITK_TEST(TestSetIndexToWorldTransformWithoutChangingSpacing); MITK_TEST(TestSetIndexToWorldTransform_WithPointerToSameTransform); MITK_TEST(TestSetSpacing); MITK_TEST(TestTransferItkToVtkTransform); MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); MITK_TEST(TestSetIdentity); MITK_TEST(TestSetImageGeometry); // Equal MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_InputIsNull_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); MITK_TEST(Equal_Transforms_MinorDifferences_And_Eps); // other Functions MITK_TEST(TestComposeTransform); MITK_TEST(TestComposeVtkMatrix); MITK_TEST(TestTranslate); MITK_TEST(TestIndexToWorld); MITK_TEST(TestExecuteOperation); MITK_TEST(TestCalculateBoundingBoxRelToTransform); // MITK_TEST(TestSetTimeBounds); MITK_TEST(TestIs2DConvertable); MITK_TEST(TestGetCornerPoint); MITK_TEST(TestExtentInMM); MITK_TEST(TestGetAxisVector); MITK_TEST(TestGetCenter); MITK_TEST(TestGetDiagonalLength); MITK_TEST(TestGetExtent); MITK_TEST(TestIsInside); MITK_TEST(TestGetMatrixColumn); // test IsSubGeometry MITK_TEST(IsSubGeometry_Spacing); MITK_TEST(IsSubGeometry_TransformMatrix); MITK_TEST(IsSubGeometry_Bounds_Image); MITK_TEST(IsSubGeometry_Bounds_NoneImage); MITK_TEST(IsSubGeometry_Grid_Image); MITK_TEST(IsSubGeometry_Grid_NoneImage); MITK_TEST(IsSubGeometry_Bounds_Oblique_Image); MITK_TEST(IsSubGeometry_Bounds_Oblique_NoneImage); MITK_TEST(IsSubGeometry_Grid_Oblique_Image); MITK_TEST(IsSubGeometry_Grid_Oblique_NoneImage); CPPUNIT_TEST_SUITE_END(); // Used Variables private: mitk::Point3D aPoint; float aFloatSpacing[3]; mitk::Vector3D aSpacing; mitk::AffineTransform3D::Pointer aTransform; BoundingBoxPointer aBoundingBox; mitk::AffineTransform3D::MatrixType aMatrix; mitk::Point3D anotherPoint; mitk::Vector3D anotherSpacing; BoundingBoxPointer anotherBoundingBox; BoundingBoxPointer aThirdBoundingBox; mitk::AffineTransform3D::Pointer anotherTransform; mitk::AffineTransform3D::Pointer aThirdTransform; mitk::AffineTransform3D::MatrixType anotherMatrix; mitk::AffineTransform3D::MatrixType aThirdMatrix; DummyTestClass::Pointer aDummyGeometry; DummyTestClass::Pointer anotherDummyGeometry; DummyTestClass::Pointer aDummyGeometryOblique; public: // Set up for variables void setUp() override { mitk::FillVector3D(aFloatSpacing, 1, 1, 1); mitk::FillVector3D(aSpacing, 1, 1, 1); mitk::FillVector3D(aPoint, 0, 0, 0); // Transform aTransform = mitk::AffineTransform3D::New(); aTransform->SetIdentity(); aMatrix.SetIdentity(); anotherTransform = mitk::AffineTransform3D::New(); anotherMatrix.SetIdentity(); anotherMatrix(1, 1) = 2; anotherTransform->SetMatrix(anotherMatrix); aThirdTransform = mitk::AffineTransform3D::New(); aThirdMatrix.SetIdentity(); aThirdMatrix(1, 1) = 7; aThirdTransform->SetMatrix(aThirdMatrix); // Bounding Box float bounds[6] = { 0, 1, 0, 1, 0, 1 }; mitk::BoundingBox::BoundsArrayType b; const float* input = bounds; int j = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6; ++j) *it++ = (mitk::ScalarType) * input++; aBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < 3; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } aBoundingBox->SetPoints(pointscontainer); aBoundingBox->ComputeBoundingBox(); anotherBoundingBox = BoundingBoxType::New(); p[0] = 11; p[1] = 12; p[2] = 13; pointscontainer->InsertElement(1, p); anotherBoundingBox->SetPoints(pointscontainer); anotherBoundingBox->ComputeBoundingBox(); aThirdBoundingBox = BoundingBoxType::New(); p[0] = 22; p[1] = 23; p[2] = 24; pointscontainer->InsertElement(1, p); aThirdBoundingBox->SetPoints(pointscontainer); aThirdBoundingBox->ComputeBoundingBox(); mitk::FillVector3D(anotherPoint, 2, 3, 4); mitk::FillVector3D(anotherSpacing, 5, 6.5, 7); aDummyGeometry = DummyTestClass::New(); aDummyGeometry->Initialize(); anotherDummyGeometry = aDummyGeometry->Clone(); aDummyGeometryOblique = DummyTestClass::New(); aDummyGeometryOblique->Initialize(); auto newBounds = aDummyGeometryOblique->GetBounds(); newBounds[0] = 0; newBounds[1] = 5; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 30; newBounds[5] = 40; aDummyGeometryOblique->SetBounds(newBounds); aDummyGeometryOblique->GetMatrixColumn(0); auto obliqueTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::OutputVectorType rotationAxis(0.); rotationAxis[1] = 1.; obliqueTransform->Rotate3D(rotationAxis, 0.6); mitk::AffineTransform3D::OutputVectorType translation; translation[0] = 100.; translation[1] = -50.; translation[2] = -150.; obliqueTransform->SetTranslation(translation); aDummyGeometryOblique->SetIndexToWorldTransform(obliqueTransform); } void tearDown() override { aDummyGeometry = nullptr; anotherDummyGeometry = nullptr; } // Test functions void TestSetOrigin() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // undo changes, new and changed object need to be the same! dummy->SetOrigin(aPoint); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetOrigin"); } void TestSetImageGeometry() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetImageGeometry(true); CPPUNIT_ASSERT(dummy->GetImageGeometry()); // undo changes, new and changed object need to be the same! dummy->SetImageGeometry(false); CPPUNIT_ASSERT(dummy->GetImageGeometry() == false); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetImageGeometry"); } void TestSetFloatBounds() { float bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox equality"); // Wrong bounds, test needs to fail bounds[1] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox not equal"); // undo changes, new and changed object need to be the same! float originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo and equal"); } void TestSetBounds() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetBounds(anotherBoundingBox->GetBounds()); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting bounds"); // Test needs to fail now dummy->SetBounds(aThirdBoundingBox->GetBounds()); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting unequal bounds"); // undo changes, new and changed object need to be the same! dummy->SetBounds(aBoundingBox->GetBounds()); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set bounds"); } void TestSetFloatBoundsDouble() { double bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds"); // Test needs to fail now bounds[3] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds unequal"); // undo changes, new and changed object need to be the same! double originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set float bounds"); } void TestSetFrameOfReferenceID() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFrameOfReferenceID(5); CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID() == 5); // undo changes, new and changed object need to be the same! dummy->SetFrameOfReferenceID(0); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set frame of reference"); } void TestSetIndexToWorldTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 1"); // Test needs to fail now dummy->SetIndexToWorldTransform(aThirdTransform); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 2"); // undo changes, new and changed object need to be the same! dummy->SetIndexToWorldTransform(aTransform); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransform 3"); } void TestSetIndexToWorldTransformWithoutChangingSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformWithoutChangingSpacing(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing(), mitk::eps, true)); // calculate a new version of anotherTransform, so that the spacing should be the same as the original spacing of // aTransform. mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = anotherTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0).as_ref(); col.normalize(); col *= aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1).as_ref(); col.normalize(); col *= aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2).as_ref(); col.normalize(); col *= aSpacing[2]; vnlmatrix.set_column(2, col); mitk::Matrix3D matrix; matrix = vnlmatrix; anotherTransform->SetMatrix(matrix); CPPUNIT_ASSERT(mitk::Equal(*anotherTransform, *(dummy->GetIndexToWorldTransform()), mitk::eps, true)); } void TestSetIndexToWorldTransform_WithPointerToSameTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); mitk::AffineTransform3D::Pointer testTransfrom = dummy->GetIndexToWorldTransform(); mitk::Vector3D modifiedPoint = anotherPoint.GetVectorFromOrigin() * 2.; testTransfrom->SetOffset(modifiedPoint); dummy->SetIndexToWorldTransform(testTransfrom); CPPUNIT_ASSERT(mitk::Equal(modifiedPoint, dummy->GetOrigin().GetVectorFromOrigin())); } void TestSetIndexToWorldTransformByVtkMatrix() { vtkMatrix4x4 *vtkmatrix; vtkmatrix = vtkMatrix4x4::New(); vtkmatrix->Identity(); vtkmatrix->SetElement(1, 1, 2); DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 1"); // test needs to fail now vtkmatrix->SetElement(1, 1, 7); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 2"); // undo changes, new and changed object need to be the same! vtkmatrix->SetElement(1, 1, 1); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); vtkmatrix->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransformByVtkMatrix 3"); } void TestSetIdentity() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Change IndextoWorldTransform and Origin dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetOrigin(anotherPoint); // Set Identity should reset ITWT and Origin dummy->SetIdentity(); MITK_ASSERT_EQUAL( aTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Test set identity 1"); CPPUNIT_ASSERT(mitk::Equal(aPoint, dummy->GetOrigin())); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing())); // new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Test set identity 2"); } void TestSetSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); // undo changes, new and changed object need to be the same! dummy->SetSpacing(aSpacing); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy spacing"); } void TestTransferItkToVtkTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); // calls TransferItkToVtkTransform mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); CPPUNIT_ASSERT(mitk::MatrixEqualElementWise(anotherMatrix, dummyTransform->GetMatrix())); } void TestConstructors() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); bool test = dummy1->IsValid(); CPPUNIT_ASSERT(test == true); CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetSpacing(), aSpacing)); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetOrigin(), aPoint)); CPPUNIT_ASSERT(dummy1->GetImageGeometry() == false); MITK_ASSERT_EQUAL( - mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Contructor test 1"); + mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Constructor test 1"); MITK_ASSERT_EQUAL( mitk::BaseGeometry::BoundingBoxType::ConstPointer(dummy1->GetBoundingBox()), aBoundingBox, "Constructor test 2"); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); float bounds[6] = {0, 11, 0, 12, 0, 13}; dummy2->SetFloatBounds(bounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2); - MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy contructor"); + MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy constructor"); } // Equal Tests void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Clone test"); } void Equal_DifferentOrigin_ReturnsFalse() { anotherDummyGeometry->SetOrigin(anotherPoint); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different origin test"); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different index to world"); } void Equal_DifferentSpacing_ReturnsFalse() { anotherDummyGeometry->SetSpacing(anotherSpacing); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different spacing"); } void Equal_InputIsNull_ReturnsFalse() { DummyTestClass::Pointer geometryNull = nullptr; CPPUNIT_ASSERT_THROW(MITK_ASSERT_EQUAL(geometryNull, anotherDummyGeometry, "Input is null"), mitk::Exception); } void Equal_DifferentBoundingBox_ReturnsFalse() { // create different bounds to make the comparison false mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; anotherDummyGeometry->SetBounds(bounds); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different bounding box"); } void Equal_Transforms_MinorDifferences_And_Eps() { // Verifies that the eps parameter is evaluated properly // when comparing two mitk::BaseGeometry::TransformTypes aMatrix.SetIdentity(); anotherMatrix.SetIdentity(); aMatrix(0, 1) = 0.0002; aTransform->SetMatrix(aMatrix); anotherMatrix(0, 1) = 0.0002; anotherTransform->SetMatrix(anotherMatrix); anotherTransform->SetMatrix(aMatrix); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=mitk::eps", mitk::Equal(*aTransform, *anotherTransform, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=vnl_math::eps", mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); anotherMatrix(0, 1) = 0.0002 + mitk::eps; anotherTransform->SetMatrix(anotherMatrix); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=vnl_math::eps", !mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=mitk::eps-1%", !mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 0.99, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps _are_ mitk::Equal() for eps=mitk::eps+1%", mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 1.01, true)); } void TestComposeTransform() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 mitk::AffineTransform3D::Pointer transform2; transform2 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix2; matrix2.SetIdentity(); matrix2(1, 1) = 2; transform2->SetMatrix(matrix2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 mitk::AffineTransform3D::Pointer transform4; transform4 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix4; matrix4.SetIdentity(); matrix4(1, 1) = 0.25; transform4->SetMatrix(matrix4); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(transform2); // Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose transform 2"); // 4=4 // undo changes, new and changed object need to be the same! dummy->Compose(transform4); // Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose transform 3"); // 1=1 } void TestComposeVtkMatrix() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 vtkMatrix4x4 *vtkmatrix2; vtkmatrix2 = vtkMatrix4x4::New(); vtkmatrix2->Identity(); vtkmatrix2->SetElement(1, 1, 2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 vtkMatrix4x4 *vtkmatrix4; vtkmatrix4 = vtkMatrix4x4::New(); vtkmatrix4->Identity(); vtkmatrix4->SetElement(1, 1, 0.25); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(vtkmatrix2); // Spacing = 4 vtkmatrix2->Delete(); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose vtk matrix"); // 4=4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); // undo changes, new and changed object need to be the same! dummy->Compose(vtkmatrix4); // Spacing = 1 vtkmatrix4->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose vtk"); // 1=1 } void TestTranslate() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); // compute ground truth mitk::Point3D tmpResult = anotherPoint + translationVector; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), tmpResult)); // undo changes translationVector *= -1; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), anotherPoint)); // undo changes, new and changed object need to be the same! translationVector.SetElement(0, -1 * anotherPoint[0]); translationVector.SetElement(1, -1 * anotherPoint[1]); translationVector.SetElement(2, -1 * anotherPoint[2]); dummy->Translate(translationVector); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Translate test"); } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems mitk::Point3D origin = dummyGeometry->GetOrigin(); mitk::Point3D dummyPoint; // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(origin, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, origin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); mitk::Point3D originContinuousIndex; dummyGeometry->WorldToIndex(origin, originContinuousIndex); CPPUNIT_ASSERT(mitk::EqualArray(originContinuousIndex, globalOrigin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, itk::Index)==(0,0,0) itk::Index<3> itkindex; dummyGeometry->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, global111, 3, mitk::eps, true)); // Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter mitk::Point3D center = dummyGeometry->GetCenter(); mitk::Point3D centerContIndex; dummyGeometry->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); CPPUNIT_ASSERT(mitk::Equal(centerContIndex, centerBounds)); // Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) center = dummyGeometry->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); CPPUNIT_ASSERT(mitk::Equal(center, centerBoundsInWorldCoords)); // Test using random point, // Testing consistency of index and world coordinate systems mitk::Point3D point; mitk::FillVector3D(point, 3.5, -2, 4.6); // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(point, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, point, 3, mitk::eps, true)); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems for vectors mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Point3D p, pIndex, origin; origin = dummyGeometry->GetOrigin(); p[0] = xAxisMM[0] + origin[0]; p[1] = xAxisMM[1] + origin[1]; p[2] = xAxisMM[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(xAxisMM, xAxisContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], p[2] - origin[2])); // Test consictency for random vector mitk::Vector3D vector; mitk::FillVector3D(vector, 2.5, -3.2, 8.1); mitk::Vector3D vectorContinuousIndex; p[0] = vector[0] + origin[0]; p[1] = vector[1] + origin[1]; p[2] = vector[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(vector, vectorContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(vectorContinuousIndex, vectorContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(vectorContinuousIndex == vector); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], p[2] - origin[2])); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; - // check for constistency + // check for consistency mitk::Point3D point; dummyGeometry->IndexToWorld(itkIndex2, point); dummyGeometry->WorldToIndex(point, itkIndex2b); CPPUNIT_ASSERT(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1]))); // Testing itk::index<2> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex3, point); dummyGeometry->WorldToIndex(point, itkIndex3b); CPPUNIT_ASSERT( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2]))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex4, point); dummyGeometry->WorldToIndex(point, itkIndex4b); CPPUNIT_ASSERT(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(mitkIndex, point); dummyGeometry->WorldToIndex(point, mitkIndexb); CPPUNIT_ASSERT( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2]))); // Testing mitk::Index for IndexToWorld/WorldToIndex consistency return EXIT_SUCCESS; } void TestIndexToWorld() { DummyTestClass::Pointer dummy = DummyTestClass::New(); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); // Geometry must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy index to world"); // Test with other geometries dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetIndexToWorldTransform(anotherTransform); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetSpacing(anotherSpacing); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); } void TestExecuteOperation() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Do same Operations with new Dummy and compare DummyTestClass::Pointer newDummy = DummyTestClass::New(); // Test operation Nothing auto opN = new mitk::Operation(mitk::OpNOTHING); dummy->ExecuteOperation(opN); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 1"); // Test operation Move auto opP = new mitk::PointOperation(mitk::OpMOVE, anotherPoint); dummy->ExecuteOperation(opP); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); newDummy->SetOrigin(anotherPoint); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 2"); // Test operation Scale, Scale sets spacing to scale+1 mitk::Point3D spacing; spacing[0] = anotherSpacing[0] - 1.; spacing[1] = anotherSpacing[1] - 1.; spacing[2] = anotherSpacing[2] - 1.; auto opS = new mitk::ScaleOperation(mitk::OpSCALE, spacing, anotherPoint); dummy->ExecuteOperation(opS); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); newDummy->SetSpacing(anotherSpacing); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 3"); // change Geometry to test more cases dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); // Testing a rotation of the geometry double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D(rotationVector, 1, 0, 0); mitk::Point3D center = dummy->GetCenter(); auto opR = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle); dummy->ExecuteOperation(opR); mitk::Matrix3D rotation; mitk::GetRotation(dummy, rotation); mitk::Vector3D voxelStep = rotation * anotherSpacing; mitk::Vector3D voxelStepIndex; dummy->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex)); delete opR; delete opN; delete opS; delete opP; } void TestCalculateBoundingBoxRelToTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 15); dummy->SetExtentInMM(1, 20); dummy->SetExtentInMM(2, 8); mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); anotherTransform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(dummy->GetCornerPoint(i))); mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); MITK_ASSERT_EQUAL(result, dummyBoundingBox, "BBox rel to transform"); // dummy still needs to be unchanged, except for extend DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetExtentInMM(0, 15); newDummy->SetExtentInMM(1, 20); newDummy->SetExtentInMM(2, 8); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy BBox"); } // void TestSetTimeBounds(){ // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // DummyTestClass::Pointer dummy = DummyTestClass::New(); // dummy->SetTimeBounds(timeBounds); // mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); // CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); // CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); // //undo changes, new and changed object need to be the same! // timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); // timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); // DummyTestClass::Pointer newDummy = DummyTestClass::New(); // CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //} void TestIs2DConvertable() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // new initialized geometry is 2D convertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Spacing needs to fail dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetSpacing(aSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Origin needs to fail dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetOrigin(aPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // third dimension must not be transformed mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType dummyMatrix; dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); dummy->SetIndexToWorldTransform(dummyTransform); // identity matrix is 2DConvertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); dummyMatrix(0, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(1, 2) = 0.4; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 1) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 0) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo changes, new and changed object need to be the same! dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Is 2D convertable"); } void TestGetCornerPoint() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D corner, refCorner; // Corner 0 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(0); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 1 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(1); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 2 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(2); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 3 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(3); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 4 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(4); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 5 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(5); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 6 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(6); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 7 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(7); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Wrong Corner needs to fail CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20), itk::ExceptionObject); // dummy geometry must not have changed! DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Corner point"); } void TestExtentInMM() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 50); CPPUNIT_ASSERT(mitk::Equal(50., dummy->GetExtentInMM(0))); // Vnl Matrix has changed. The next line only works because the spacing is 1! CPPUNIT_ASSERT( mitk::Equal(50., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); // Smaller extent than original dummy->SetExtentInMM(0, 5); CPPUNIT_ASSERT(mitk::Equal(5., dummy->GetExtentInMM(0))); CPPUNIT_ASSERT( mitk::Equal(5., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); dummy->SetExtentInMM(1, 4); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtentInMM(1))); CPPUNIT_ASSERT( mitk::Equal(4., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude())); dummy->SetExtentInMM(2, 2.5); CPPUNIT_ASSERT(mitk::Equal(2.5, dummy->GetExtentInMM(2))); CPPUNIT_ASSERT( mitk::Equal(2.5, dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude())); } void TestGetAxisVector() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Vector3D vector; mitk::FillVector3D(vector, bounds[1], 0, 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(0), vector)); mitk::FillVector3D(vector, 0, bounds[3], 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(1), vector)); mitk::FillVector3D(vector, 0, 0, bounds[5]); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(2), vector)); } void TestGetCenter() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 2, 12, 1, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D refCenter; for (int i = 0; i < 3; i++) refCenter.SetElement(i, (bounds[2 * i] + bounds[2 * i + 1]) / 2.0); dummy->IndexToWorld(refCenter, refCenter); CPPUNIT_ASSERT(mitk::Equal(dummy->GetCenter(), refCenter)); } void TestGetDiagonalLength() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); // 3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 double expectedLength = sqrt(29.); CPPUNIT_ASSERT(mitk::Equal(expectedLength, dummy->GetDiagonalLength(), mitk::eps, true)); CPPUNIT_ASSERT(mitk::Equal(29., dummy->GetDiagonalLength2(), mitk::eps, true)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Diagonal length"); } void TestGetExtent() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(2., dummy->GetExtent(0))); CPPUNIT_ASSERT(mitk::Equal(3., dummy->GetExtent(1))); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtent(2))); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Extend"); } void TestIsInside() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); mitk::Point3D insidePoint; mitk::Point3D outsidePoint; mitk::FillVector3D(insidePoint, 2, 6, 7.6); mitk::FillVector3D(outsidePoint, 0, 9, 8.2); CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsIndexInside(outsidePoint)); dummy->IndexToWorld(insidePoint, insidePoint); dummy->IndexToWorld(outsidePoint, outsidePoint); CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsInside(outsidePoint)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Is inside"); } void TestInitialize() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); dummy2->SetBounds(anotherBoundingBox->GetBounds()); // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // dummy2->SetTimeBounds(timeBounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); dummy1->InitializeGeometry(dummy2); MITK_ASSERT_EQUAL(dummy1, dummy2, "Initialize 1"); dummy1->Initialize(); DummyTestClass::Pointer dummy3 = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy3, dummy1, "Initialize 2"); } void TestGetMatrixColumn() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); mitk::Vector3D testVector, refVector; testVector.SetVnlVector(dummy->GetMatrixColumn(0)); mitk::FillVector3D(refVector, 1, 0, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(1)); mitk::FillVector3D(refVector, 0, 2, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(2)); mitk::FillVector3D(refVector, 0, 0, 1); CPPUNIT_ASSERT(testVector == refVector); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(dummy, newDummy, "GetMatrixColumn"); } void IsSubGeometry_Spacing() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] -= mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_TransformMatrix() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { itk::Matrix wrongMatrix = aDummyGeometry->GetIndexToWorldTransform()->GetMatrix(); wrongMatrix[i][j] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } } void IsSubGeometry_Bounds_NoneImage() { IsSubGeometry_Bounds_internal(false); } void IsSubGeometry_Bounds_Image() { IsSubGeometry_Bounds_internal(true); } void IsSubGeometry_Bounds_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 10; newBounds[1] = 20; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 10; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometry->Clone(); legalGeometry->SetBounds(legalBounds); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = aDummyGeometry->GetBounds(); if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetBounds(wrongBounds); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Grid_Image() { IsSubGeometry_Grid_internal(true); } void IsSubGeometry_Grid_NoneImage() { IsSubGeometry_Grid_internal(false); } void IsSubGeometry_Grid_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometry->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] += smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Bounds_Oblique_NoneImage() { IsSubGeometry_Bounds_Oblique_internal(false); } void IsSubGeometry_Bounds_Oblique_Image() { IsSubGeometry_Bounds_Oblique_internal(true); } void IsSubGeometry_Bounds_Oblique_internal(bool isImage) { auto newBounds = aDummyGeometryOblique->GetBounds(); aDummyGeometryOblique->SetImageGeometry(isImage); //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION to compensate rounding errors that //are interoduced when transforming points/indeces due to the oblique geometry. CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometryOblique, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometryOblique->Clone(); legalGeometry->SetBounds(legalBounds); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = newBounds; if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometryOblique->Clone(); wrongGeometry->SetBounds(wrongBounds); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } } void IsSubGeometry_Grid_Oblique_NoneImage() { IsSubGeometry_Grid_Oblique_internal(false); } void IsSubGeometry_Grid_Oblique_Image() { IsSubGeometry_Grid_Oblique_internal(true); } void IsSubGeometry_Grid_Oblique_internal(bool isImage) { auto newBounds = aDummyGeometryOblique->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometryOblique->SetBounds(newBounds); aDummyGeometryOblique->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometryOblique->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION in the following checks //to compensate rounding errors that are interoduced when transforming points/indeces //due to the oblique geometry. //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); mitk::Point3D index; smallerGeometry->WorldToIndex(legalOrigin, index); index[i] -= 1; smallerGeometry->IndexToWorld(index, legalOrigin); auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); mitk::Point3D index; smallerGeometry->WorldToIndex(legalOrigin, index); index[i] += 1; smallerGeometry->IndexToWorld(index, legalOrigin); auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } } }; // end class mitkBaseGeometryTestSuite MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp index 3afe051d94..65f234967d 100644 --- a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp +++ b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp @@ -1,795 +1,795 @@ /*============================================================================ 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 "mitkTestingMacros.h" #include #include "mitkClippedSurfaceBoundsCalculator.h" #include "mitkGeometry3D.h" #include "mitkNumericTypes.h" #include "mitkPlaneGeometry.h" static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); // Check planes which are only on one slice: // Slice 0 mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceZero, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); // Slice 3 origin[2] = 3; mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New(); planeOnSliceThree->InitializePlane(origin, normal); planeOnSliceThree->SetImageGeometry(false); calculator->SetInput(planeOnSliceThree, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3"); // Slice 17 origin[2] = 17; mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceSeventeen, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17"); // Slice 20 origin[2] = 19; mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New(); planeOnSliceTwenty->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceTwenty, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19"); delete calculator; } static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); // Check planes which are only on one slice: // Slice 0 mitk::Point3D origin; origin[0] = 510; // Set to 511.9 so that the intersection point is inside the bounding box origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New(); planeSagittalOne->InitializePlane(origin, normal); calculator->SetInput(planeSagittalOne, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); // Slice 3 origin[0] = 256; MITK_INFO << "Case1 origin: " << origin; mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New(); planeSagittalTwo->InitializePlane(origin, normal); MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal(); MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin(); calculator->SetInput(planeSagittalTwo, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); // Slice 17 origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput(planeOnSliceSeventeen, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); // Crooked planes: origin[0] = 0; origin[1] = 507; origin[2] = 0; normal[0] = 1; normal[1] = -1; normal[2] = 1; mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New(); planeCrookedOne->InitializePlane(origin, normal); calculator->SetInput(planeCrookedOne, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 5, "Check if plane is from slice 0 to slice 5 with inclined plane"); origin[0] = 512; origin[1] = 0; origin[2] = 16; mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New(); planeCrookedTwo->InitializePlane(origin, normal); calculator->SetInput(planeCrookedTwo, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 16 && minMax.second == 19, "Check if plane is from slice 16 to slice 19 with inclined plane"); origin[0] = 510; origin[1] = 0; origin[2] = 0; normal[1] = 0; normal[2] = 0.04; mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New(); planeCrookedThree->InitializePlane(origin, normal); calculator->SetInput(planeCrookedThree, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19 with inclined plane"); delete calculator; } static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are outside of the bounding box mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); // In front of the bounding box mitk::Point3D origin; origin[0] = 510; origin[1] = 0; origin[2] = -5; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New(); planeInFront->InitializePlane(origin, normal); calculator->SetInput(planeInFront, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Behind the bounding box origin[2] = 515; mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New(); planeBehind->InitializePlane(origin, normal); calculator->SetInput(planeBehind, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Above origin[1] = 515; mitk::FillVector3D(normal, 0, 1, 0); mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New(); planeAbove->InitializePlane(origin, normal); calculator->SetInput(planeAbove, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Below origin[1] = -5; mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New(); planeBelow->InitializePlane(origin, normal); calculator->SetInput(planeBelow, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Left side origin[0] = -5; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New(); planeLeftSide->InitializePlane(origin, normal); calculator->SetInput(planeLeftSide, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); // Right side origin[1] = 515; mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New(); planeRightSide->InitializePlane(origin, normal); calculator->SetInput(planeRightSide, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); delete calculator; } static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D, mitk::BaseGeometry::Pointer secondGeometry3D) { mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer firstImage = mitk::Image::New(); firstImage->Initialize(mitk::MakePixelType(), *(firstGeometry3D.GetPointer())); calculator->SetInput(secondGeometry3D, firstImage); calculator->Update(); auto minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); delete calculator; } static void CheckIntersectionWithPointCloud(mitk::BaseGeometry::Pointer geometry3D) { // Check planes which are inside the bounding box mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(geometry3D.GetPointer())); { mitk::Point3D pnt1, pnt2; pnt1[0] = 3; pnt1[1] = 5; pnt1[2] = 3; pnt2[0] = 8; pnt2[1] = 3; pnt2[2] = 8; mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back(pnt1); pointlist.push_back(pnt2); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput(pointlist, image); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8, "Check if points span from slice 3 to slice 8 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5, "Check if points span from slice 3 to slice 5 in sagittal"); } { mitk::Point3D pnt1, pnt2; pnt1.Fill(-3); pnt2.Fill(600); mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back(pnt1); pointlist.push_back(pnt2); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput(pointlist, image); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19, "Check if points are correctly clipped to slice 0 and slice 19 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511, "Check if points are correctly clipped to slice 0 and slice 511 in sagittal"); } } static void CheckIntersectionWithRotatedGeometry() { // Define origin for second Geometry3D; mitk::Point3D origin3; origin3[0] = -45.25; origin3[1] = -113.22; origin3[2] = 0.62; // Define normal: mitk::Vector3D normal3; normal3[0] = -0.02; normal3[1] = -0.18; normal3[2] = 2.99; mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 1.43; spacing[2] = 3.0; // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New(); planeGeometry3->InitializePlane(origin3, normal3); planeGeometry3->SetSpacing(spacing); mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds(); moreBounds[0] = 0; moreBounds[1] = 64; moreBounds[2] = 0; moreBounds[3] = 140; moreBounds[4] = 0; moreBounds[5] = 1; planeGeometry3->SetBounds(moreBounds); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New(); rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry3.GetPointer()), 25); rotatedSlicedGeometry3D->SetImageGeometry(true); mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix; matrix[0][0] = 1.43; matrix[0][1] = -0.01; matrix[0][2] = -0.02; matrix[0][0] = 0.01; matrix[1][1] = 1.43; matrix[2][2] = -0.18; matrix[0][0] = 0.01; matrix[1][1] = 0.08; matrix[2][2] = 2.99; geom->SetMatrix(matrix); mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer())); // Check planes which are only on one slice: { // last Slice mitk::Point3D origin; origin[0] = -47.79; origin[1] = 81.41; origin[2] = 84.34; mitk::Vector3D normal; mitk::FillVector3D(normal, 0.02, 0.18, -2.99); mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 1.43; spacing[2] = 3.0; mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); planeOnSliceZero->SetSpacing(spacing); calculator->SetInput(planeOnSliceZero, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 24 && minMax.second == 24, "Check if plane is on slice 24"); } { // Slice 0-24 mitk::Point3D origin; origin[0] = -45; origin[1] = -105; origin[2] = 35; mitk::Vector3D normal; mitk::FillVector3D(normal, 1.43, 0.01, 0.01); mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 3.0; spacing[2] = 1.43; mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); planeOnSliceZero->SetSpacing(spacing); calculator->SetInput(planeOnSliceZero, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first != minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 24, "Check if plane is on slices 0-24"); } delete calculator; } static void CheckIntersectionWithRotatedGeometry90() { // Define origin for second Geometry3D; mitk::Point3D origin3; origin3[0] = 0.0; origin3[1] = 0.0; origin3[2] = 0.0; // Define normal: mitk::Vector3D normal3; normal3[0] = 0; normal3[1] = 0; normal3[2] = 1; mitk::Vector3D spacing; spacing[0] = 1.0; spacing[1] = 2.0; spacing[2] = 1.0; // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New(); planeGeometry3->InitializePlane(origin3, normal3); planeGeometry3->SetSpacing(spacing); mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds(); moreBounds[0] = 0; moreBounds[1] = 50; moreBounds[2] = 0; moreBounds[3] = 50; moreBounds[4] = 0; moreBounds[5] = 1; planeGeometry3->SetBounds(moreBounds); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New(); rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry3.GetPointer()), 50); rotatedSlicedGeometry3D->SetImageGeometry(true); // Set the rotation to zero (identity times spacing matrix) because the default makes a rotation mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix; matrix[0][0] = spacing[0]; matrix[0][1] = 0.0; matrix[0][2] = 0.0; matrix[1][0] = 0.0; matrix[1][1] = spacing[1]; matrix[1][2] = 0.0; matrix[2][0] = 0.0; matrix[2][1] = 0.0; matrix[2][2] = spacing[2]; geom->SetMatrix(matrix); mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize(mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer())); // construct the planes in y direction { // first Slice in Y direction mitk::Point3D originFirstSlice; originFirstSlice[0] = 0.0; originFirstSlice[1] = -1.0; originFirstSlice[2] = 0.0; mitk::Vector3D normalFitrstSlice; mitk::FillVector3D(normalFitrstSlice, 0.0, 1.0, 0.0); mitk::Vector3D spacingFirstSlice; spacingFirstSlice[0] = 1.0; spacingFirstSlice[1] = 1.0; spacingFirstSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New(); planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice); planeOnFirstSlice->SetSpacing(spacingFirstSlice); // last Slice in Y Direction mitk::Point3D originLastSlice; originLastSlice[0] = 0.0; originLastSlice[1] = 99.0; originLastSlice[2] = 0.0; mitk::Vector3D normalLastSlice; mitk::FillVector3D(normalLastSlice, 0.0, 1.0, 0.0); mitk::Vector3D spacingLastSlice; spacingLastSlice[0] = 1.0; spacingLastSlice[1] = 1.0; spacingLastSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New(); planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice); planeOnLastSlice->SetSpacing(spacingLastSlice); // calculate the intersection on the last slice calculator->SetInput(planeOnLastSlice, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; // calculate the intersection on the first slice calculator->SetInput(planeOnFirstSlice, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; } - // now we make a rotation of the Image about 270 degrees around Z Axis to get an aligment on the positiv x axis + // now we make a rotation of the Image about 270 degrees around Z Axis to get an alignment on the positive x axis double angleInDegrees = 270 * itk::Math::pi / 180; mitk::AffineTransform3D::MatrixType matrix2; matrix2[0][0] = cos(angleInDegrees); matrix2[0][1] = -sin(angleInDegrees); matrix2[0][2] = 0.0; matrix2[1][0] = sin(angleInDegrees); matrix2[1][1] = cos(angleInDegrees); matrix2[1][2] = 0.0; matrix2[2][0] = 0.0; matrix2[2][1] = 0.0; matrix2[2][2] = 1.0; // multiplie the identity with the transformation matrix mitk::AffineTransform3D::MatrixType TransformationMatrix = matrix2 * matrix; // initialize the image with the new rotation Matrix geom->SetMatrix(TransformationMatrix); image->Initialize(mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer())); { // first Slice in X Diection mitk::Point3D originFirstSlice; originFirstSlice[0] = -1.0; originFirstSlice[1] = 0.0; originFirstSlice[2] = 0.0; mitk::Vector3D normalFitrstSlice; mitk::FillVector3D(normalFitrstSlice, 1.0, 0.0, 0.0); mitk::Vector3D spacingFirstSlice; spacingFirstSlice[0] = 1.0; spacingFirstSlice[1] = 1.0; spacingFirstSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New(); planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice); planeOnFirstSlice->SetSpacing(spacingFirstSlice); // last Slice in X Direction mitk::Point3D originLastSlice; originLastSlice[0] = 99.0; originLastSlice[1] = 0.0; originLastSlice[2] = 0.0; mitk::Vector3D normalLastSlice; mitk::FillVector3D(normalLastSlice, 1.0, 0.0, 0.0); mitk::Vector3D spacingLastSlice; spacingLastSlice[0] = 1.0; spacingLastSlice[1] = 1.0; spacingLastSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New(); planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice); planeOnLastSlice->SetSpacing(spacingLastSlice); calculator->SetInput(planeOnLastSlice, image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; calculator->SetInput(planeOnFirstSlice, image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; } delete calculator; } int mitkClippedSurfaceBoundsCalculatorTest(int, char *[]) { // always start with this! MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator"); /** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a * Geometry3D. * This unit test checks if the correct min and max values for the three spatial directions (x, y, z) * are calculated. To test this we define artificial PlaneGeometries and Geometry3Ds and test different * scenarios: * * 1. planes which are inside the bounding box of a 3D geometry but only on one slice * 2. planes which are outside of the bounding box * 3. planes which are inside the bounding box but over more than one slice * * Note: Currently rotated geometries are not tested! */ /********************* Define Geometry3D ***********************/ // Define origin: mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; // Define normal: mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializePlane(origin, normal); // Set Bounds: mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds(); bounds[0] = 0; bounds[1] = 512; bounds[2] = 0; bounds[3] = 512; bounds[4] = 0; bounds[5] = 1; planeGeometry->SetBounds(bounds); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New(); slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry.GetPointer()), 20); mitk::BaseGeometry::Pointer geometry3D = dynamic_cast(slicedGeometry3D.GetPointer()); geometry3D->SetImageGeometry(true); // Define origin for second Geometry3D; mitk::Point3D origin2; origin2[0] = 511; origin2[1] = 60; origin2[2] = 0; // Define normal: mitk::Vector3D normal2; mitk::FillVector3D(normal2, 0, 1, 0); // Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New(); planeGeometry2->InitializePlane(origin2, normal2); // Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New(); secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry2.GetPointer()), 20); mitk::BaseGeometry::Pointer secondGeometry3D = dynamic_cast(secondSlicedGeometry3D.GetPointer()); secondGeometry3D->SetImageGeometry(true); /***************************************************************/ CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D); CheckPlanesOutsideOfBoundingBox(geometry3D); CheckPlanesInsideBoundingBox(geometry3D); CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D); CheckIntersectionWithPointCloud(geometry3D); CheckIntersectionWithRotatedGeometry(); CheckIntersectionWithRotatedGeometry90(); /** ToDo: * test also rotated 3D geometry! */ MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkDataStorageTest.cpp b/Modules/Core/test/mitkDataStorageTest.cpp index ac24c8ea2f..428fd93240 100644 --- a/Modules/Core/test/mitkDataStorageTest.cpp +++ b/Modules/Core/test/mitkDataStorageTest.cpp @@ -1,874 +1,874 @@ /*============================================================================ 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 "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkReferenceCountWatcher.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkDataStorage.h" #include "mitkIOUtil.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateData.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateSource.h" #include "mitkStandaloneDataStorage.h" //#include "mitkPicFileReader.h" #include "mitkTestingMacros.h" void TestDataStorage(mitk::DataStorage *ds, std::string filename); namespace mitk { class TestStandaloneDataStorage : public StandaloneDataStorage { public: mitkClassMacro(TestStandaloneDataStorage, mitk::DataStorage); itkFactorylessNewMacro(Self) itkCloneMacro(Self) std::map GetModifiedObserverTags() const { return m_NodeModifiedObserverTags; } std::map GetDeletedObserverTags() const { return m_NodeDeleteObserverTags; } protected: TestStandaloneDataStorage() {} }; } class DSEventReceiver // Helper class for event testing { public: const mitk::DataNode *m_NodeAdded; const mitk::DataNode *m_NodeRemoved; DSEventReceiver() : m_NodeAdded(nullptr), m_NodeRemoved(nullptr) {} void OnAdd(const mitk::DataNode *node) { m_NodeAdded = node; } void OnRemove(const mitk::DataNode *node) { m_NodeRemoved = node; } }; /// /// \brief a class for checking if the datastorage is really thread safe /// /// Therefore it listens to a node contained in the datastorage. when this node /// gets removed and deleted, this class gets informed by calling OnObjectDelete(). /// in OnObjectDelete() an empty node gets added. this must not cause a deadlock /// struct ItkDeleteEventListener { ItkDeleteEventListener(mitk::DataStorage *ds) : m_Node(nullptr), m_DataStorage(ds), m_DeleteObserverTag(0) {} void SetNode(mitk::DataNode *_Node) { if (m_Node) return; m_Node = _Node; itk::MemberCommand::Pointer onObjectDelete = itk::MemberCommand::New(); onObjectDelete->SetCallbackFunction(this, &ItkDeleteEventListener::OnObjectDelete); m_DeleteObserverTag = m_Node->AddObserver(itk::DeleteEvent(), onObjectDelete); } void OnObjectDelete(const itk::Object * /*caller*/, const itk::EventObject &) { mitk::DataNode::Pointer node = mitk::DataNode::New(); m_DataStorage->Add(node); // SHOULD NOT CAUSE A DEADLOCK! m_DataStorage->Remove(node); // tidy up: remove the empty node again m_Node = nullptr; } protected: mitk::DataNode *m_Node; mitk::DataStorage::Pointer m_DataStorage; unsigned int m_DeleteObserverTag; }; //## Documentation //## main testing method //## NOTE: the current Singleton implementation of DataTreeStorage will lead to crashes if a testcase fails //## and therefore mitk::DataStorage::ShutdownSingleton() is not called. int mitkDataStorageTest(int argc, char *argv[]) { MITK_TEST_BEGIN("DataStorageTest"); // muellerm: test observer tag remove mitk::TestStandaloneDataStorage::Pointer testDS = mitk::TestStandaloneDataStorage::New(); mitk::DataNode::Pointer n1 = mitk::DataNode::New(); testDS->Add(n1); MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 1, "Testing if modified" " observer was added."); MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 1, "Testing if delete" " observer was added."); testDS->Remove(n1); MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 0, "Testing if modified" " observer was removed."); MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 0, "Testing if delete" " observer was removed."); /* Create StandaloneDataStorage */ MITK_TEST_OUTPUT(<< "Create StandaloneDataStorage : "); mitk::StandaloneDataStorage::Pointer sds; try { sds = mitk::StandaloneDataStorage::New(); MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instatiation"); } catch (...) { MITK_TEST_FAILED_MSG(<< "Exception during creation of StandaloneDataStorage"); } MITK_TEST_OUTPUT(<< "Testing StandaloneDataStorage: "); MITK_TEST_CONDITION_REQUIRED(argc > 1, "Testing correct test invocation"); TestDataStorage(sds, argv[1]); // TODO: Add specific StandaloneDataStorage Tests here sds = nullptr; MITK_TEST_END(); } //##Documentation //## @brief Test for the DataStorage class and its associated classes (e.g. the predicate classes) //## This method will be called once for each subclass of DataStorage void TestDataStorage(mitk::DataStorage *ds, std::string filename) { /* DataStorage valid? */ MITK_TEST_CONDITION_REQUIRED(ds != nullptr, "DataStorage valid?"); // Take the ItkImageFile Reader for the .nrrd data format. // (was previously pic which is now deprecated format) mitk::Image::Pointer image = mitk::IOUtil::Load(filename); // create some DataNodes to fill the ds mitk::DataNode::Pointer n1 = mitk::DataNode::New(); // node with image and name property // mitk::Image::Pointer image = mitk::Image::New(); // unsigned int imageDimensions[] = { 10, 10, 10, 10 }; // mitk::PixelType pt(typeid(int)); // image->Initialize( pt, 4, imageDimensions ); n1->SetData(image); n1->SetProperty("name", mitk::StringProperty::New("Node 1 - Image Node")); mitk::DataStorage::SetOfObjects::Pointer parents1 = mitk::DataStorage::SetOfObjects::New(); mitk::DataNode::Pointer n2 = mitk::DataNode::New(); // node with surface and name and color properties mitk::Surface::Pointer surface = mitk::Surface::New(); n2->SetData(surface); n2->SetProperty("name", mitk::StringProperty::New("Node 2 - Surface Node")); mitk::Color color; color.Set(1.0f, 1.0f, 0.0f); n2->SetColor(color); n2->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents2 = mitk::DataStorage::SetOfObjects::New(); parents2->InsertElement(0, n1); // n1 (image node) is source of n2 (surface node) mitk::DataNode::Pointer n3 = mitk::DataNode::New(); // node without data but with name property n3->SetProperty("name", mitk::StringProperty::New("Node 3 - Empty Node")); n3->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New()); n3->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents3 = mitk::DataStorage::SetOfObjects::New(); parents3->InsertElement(0, n2); // n2 is source of n3 mitk::DataNode::Pointer n4 = mitk::DataNode::New(); // node without data but with color property n4->SetColor(color); n4->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents4 = mitk::DataStorage::SetOfObjects::New(); parents4->InsertElement(0, n2); parents4->InsertElement(1, n3); // n2 and n3 are sources of n4 mitk::DataNode::Pointer n5 = mitk::DataNode::New(); // extra node n5->SetProperty("name", mitk::StringProperty::New("Node 5")); try /* adding objects */ { /* Add an object */ ds->Add(n1, parents1); MITK_TEST_CONDITION_REQUIRED((ds->GetAll()->Size() == 1) && (ds->GetAll()->GetElement(0) == n1), "Testing Adding a new object"); /* Check exception on adding the same object again */ MITK_TEST_OUTPUT(<< "Check exception on adding the same object again: "); MITK_TEST_FOR_EXCEPTION(std::exception, ds->Add(n1, parents1)); MITK_TEST_CONDITION(ds->GetAll()->Size() == 1, "Test if object count is correct after exception"); /* Add an object that has a source object */ ds->Add(n2, parents2); MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 2, "Testing Adding an object that has a source object"); /* Add some more objects needed for further tests */ ds->Add(n3, parents3); // n3 object that has name property and one parent ds->Add(n4, parents4); // n4 object that has color property ds->Add(n5); // n5 has no parents MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 5, "Adding some more objects needed for further tests"); } catch (...) { - MITK_TEST_FAILED_MSG(<< "Exeption during object creation"); + MITK_TEST_FAILED_MSG(<< "Exception during object creation"); } try /* object retrieval methods */ { /* Requesting all Objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll(); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((stlAll.size() == 5) // check if all tree nodes are in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()), "Testing GetAll()"); } /* Requesting a named object */ { mitk::NodePredicateProperty::Pointer predicate( mitk::NodePredicateProperty::New("name", mitk::StringProperty::New("Node 2 - Surface Node"))); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting a named object"); } /* Requesting objects of specific data type */ { mitk::NodePredicateDataType::Pointer predicate(mitk::NodePredicateDataType::New("Image")); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific data type") } /* Requesting objects of specific dimension */ { mitk::NodePredicateDimension::Pointer predicate(mitk::NodePredicateDimension::New(4)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific dimension") } /* Requesting objects with specific data object */ { mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(image)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects with specific data object") } /* Requesting objects with nullptr data */ { mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(nullptr)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n3) != all->end()) && (std::find(all->begin(), all->end(), n4) != all->end()) && (std::find(all->begin(), all->end(), n5) != all->end()), "Requesting objects with nullptr data"); } /* Requesting objects that meet a conjunction criteria */ { mitk::NodePredicateDataType::Pointer p1 = mitk::NodePredicateDataType::New("Surface"); mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color)); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(p1); predicate->AddPredicate(p2); // objects must be of datatype "Surface" and have red color (= n2) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting objects that meet a conjunction criteria"); } /* Requesting objects that meet a disjunction criteria */ { mitk::NodePredicateDataType::Pointer p1(mitk::NodePredicateDataType::New("Image")); mitk::NodePredicateProperty::Pointer p2( mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color))); mitk::NodePredicateOr::Pointer predicate = mitk::NodePredicateOr::New(); predicate->AddPredicate(p1); predicate->AddPredicate(p2); // objects must be of datatype "Surface" or have red color (= n1, n2, n4) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n1) != all->end()) && (std::find(all->begin(), all->end(), n2) != all->end()) && (std::find(all->begin(), all->end(), n4) != all->end()), "Requesting objects that meet a disjunction criteria"); } /* Requesting objects that do not meet a criteria */ { mitk::ColorProperty::Pointer cp = mitk::ColorProperty::New(color); mitk::NodePredicateProperty::Pointer proppred(mitk::NodePredicateProperty::New("color", cp)); mitk::NodePredicateNot::Pointer predicate(mitk::NodePredicateNot::New(proppred)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 3) // check if correct objects are in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()), "Requesting objects that do not meet a criteria"); } /* Requesting *direct* source objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n3, nullptr, true); // Get direct parents of n3 (=n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Requesting *direct* source objects"); } /* Requesting *all* source objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n3, nullptr, false); // Get all parents of n3 (= n1 + n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Requesting *all* source objects"); // check if n1 and n2 are the resultset } /* Requesting *all* sources of object with multiple parents */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, nullptr, false); // Get all parents of n4 (= n1 + n2 + n3) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset , "Requesting *all* sources of object with multiple parents"); } /* Requesting *direct* derived objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, nullptr, true); // Get direct childs of n1 (=n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) // check if n1 is the resultset , "Requesting *direct* derived objects"); } ///* Requesting *direct* derived objects with multiple parents/derivations */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n2, nullptr, true); // Get direct childs of n2 (=n3 + n4) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n3 is the resultset && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) // check if n4 is the resultset , "Requesting *direct* derived objects with multiple parents/derivations"); } //* Requesting *all* derived objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, nullptr, false); // Get all childs of n1 (=n2, n3, n4) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()), "Requesting *all* derived objects"); } /* Checking for circular source relationships */ { parents1->InsertElement(0, n4); // make n1 derived from n4 (which is derived from n2, which is derived from n1) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources( n4, nullptr, false); // Get all parents of n4 (= n1 + n2 + n3, not n4 itself and not multiple versions of the nodes!) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset , "Checking for circular source relationships"); } ///* Checking for circular derivation relationships can not be performed, because the internal derivations /// datastructure // can not be accessed from the outside. (Therefore it should not be possible to create these circular relations // */ //* Checking GroupTagProperty */ { mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New(); mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 1", tp)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n3 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()), "Checking GroupTagProperty"); } /* Checking GroupTagProperty 2 */ { mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New(); mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 2", tp)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) // check if n3 and n4 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()), "Checking GroupTagProperty 2"); } /* Checking direct sources with condition */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Surface"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, true); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "checking direct sources with condition"); } /* Checking all sources with condition */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Image"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) // check if n1 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()), "Checking all sources with condition"); } /* Checking all sources with condition with empty resultset */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("VesselTree"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false); MITK_TEST_CONDITION(all->Size() == 0, "Checking all sources with condition with empty resultset"); // check if resultset is empty } /* Checking direct derivations with condition */ { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, true); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Checking direct derivations with condition"); } /* Checking all derivations with condition */ { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, false); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n4 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()), "Checking direct derivations with condition"); } /* Checking named node method */ MITK_TEST_CONDITION(ds->GetNamedNode("Node 2 - Surface Node") == n2, "Checking named node method"); MITK_TEST_CONDITION(ds->GetNamedNode(std::string("Node 2 - Surface Node")) == n2, "Checking named node(std::string) method"); /* Checking named node method with wrong name */ MITK_TEST_CONDITION(ds->GetNamedNode("This name does not exist") == nullptr, "Checking named node method with wrong name"); /* Checking named object method */ MITK_TEST_CONDITION(ds->GetNamedObject("Node 1 - Image Node") == image, "Checking named object method"); MITK_TEST_CONDITION(ds->GetNamedObject(std::string("Node 1 - Image Node")) == image, "Checking named object(std::string) method"); /* Checking named object method with wrong DataType */ MITK_TEST_CONDITION(ds->GetNamedObject("Node 1 - Image Node") == nullptr, "Checking named object method with wrong DataType"); /* Checking named object method with wrong name */ MITK_TEST_CONDITION(ds->GetNamedObject("This name does not exist") == nullptr, "Checking named object method with wrong name"); /* Checking GetNamedDerivedNode with valid name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 2 - Surface Node", n1, true) == n2, "Checking GetNamedDerivedNode with valid name & direct derivation only"); /* Checking GetNamedDerivedNode with invalid Name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("wrong name", n1, true) == nullptr, "Checking GetNamedDerivedNode with invalid name & direct derivation only"); /* Checking GetNamedDerivedNode with invalid Name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, false) == n3, "Checking GetNamedDerivedNode with invalid name & direct derivation only"); /* Checking GetNamedDerivedNode with valid Name but direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, true) == nullptr, "Checking GetNamedDerivedNode with valid Name but direct derivation only"); /* Checking GetNode with valid predicate */ { mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("Image")); MITK_TEST_CONDITION(ds->GetNode(p) == n1, "Checking GetNode with valid predicate"); } /* Checking GetNode with invalid predicate */ { mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("PointSet")); MITK_TEST_CONDITION(ds->GetNode(p) == nullptr, "Checking GetNode with invalid predicate"); } } // object retrieval methods catch (...) { - MITK_TEST_FAILED_MSG(<< "Exeption during object retrieval (GetXXX() Methods)"); + MITK_TEST_FAILED_MSG(<< "Exception during object retrieval (GetXXX() Methods)"); } try /* object removal methods */ { /* Checking removal of a node without relations */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra); MITK_TEST_CONDITION(ds->GetNamedNode("extra") == extra, "Adding extra node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()), "Checking removal of a node without relations"); extra = nullptr; } /* Checking removal of a node with a parent */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra, n1); // n1 is parent of extra MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 , "Adding extra node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1), "Checking removal of a node with a parent"); extra = nullptr; } /* Checking removal of a node with two parents */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n2); ds->Add(extra, p); // n1 and n2 are parents of extra MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 && (ds->GetDerivations(n2)->Size() == 3), "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1 && (ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2 , "Checking removal of a node with two parents"); extra = nullptr; } /* Checking removal of a node with two derived nodes */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra); mitk::DataNode::Pointer d1 = mitk::DataNode::New(); d1->SetProperty("name", mitk::StringProperty::New("d1")); ds->Add(d1, extra); mitk::DataNode::Pointer d2 = mitk::DataNode::New(); d2->SetProperty("name", mitk::StringProperty::New("d2")); ds->Add(d2, extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1") == d1) && (ds->GetNamedNode("d2") == d2) && (ds->GetSources(d1)->Size() == 1) // extra should be source of d1 && (ds->GetSources(d2)->Size() == 1) // extra should be source of d2 && (ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1") == d1) && (ds->GetNamedNode("d2") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore && (ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore , "Checking removal of a node with two derived nodes"); extra = nullptr; } /* Checking removal of a node with two parents and two derived nodes */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); mitk::ReferenceCountWatcher::Pointer n1watcher = new mitk::ReferenceCountWatcher(n1); int refCountbeforeDS = watcher->GetReferenceCount(); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n2); ds->Add(extra, p); // n1 and n2 are parents of extra mitk::DataNode::Pointer d1 = mitk::DataNode::New(); d1->SetProperty("name", mitk::StringProperty::New("d1x")); ds->Add(d1, extra); mitk::DataNode::Pointer d2 = mitk::DataNode::New(); d2->SetProperty("name", mitk::StringProperty::New("d2x")); ds->Add(d2, extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) && (ds->GetSources(d1)->Size() == 1) // extra should be source of d1 && (ds->GetSources(d2)->Size() == 1) // extra should be source of d2 && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 && (ds->GetDerivations(n2)->Size() == 3) // n3, n4 and extra should be derived from n2 && (ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1 && (ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2 && (ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore && (ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore , "Checking removal of a node with two parents and two derived nodes"); extra = nullptr; } } catch (...) { - MITK_TEST_FAILED_MSG(<< "Exeption during object removal methods"); + MITK_TEST_FAILED_MSG(<< "Exception during object removal methods"); } /* Checking for node is it's own parent exception */ { MITK_TEST_FOR_EXCEPTION_BEGIN(std::exception); mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(extra); // extra is parent of extra!!! ds->Add(extra, p); MITK_TEST_FOR_EXCEPTION_END(std::exception); } /* Checking reference count of node after add and remove */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n3); ds->Add(extra, p); extra = nullptr; ds->Remove(ds->GetNamedNode("extra")); MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Checking reference count of node after add and remove"); } /* Checking removal of a node with two derived nodes [ dataStorage->GetDerivations( rootNode )] see bug #3426 */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); ds->Add(extra); mitk::DataNode::Pointer d1y = mitk::DataNode::New(); d1y->SetProperty("name", mitk::StringProperty::New("d1y")); mitk::ReferenceCountWatcher::Pointer watcherD1y = new mitk::ReferenceCountWatcher(d1y); int refCountbeforeDS = watcherD1y->GetReferenceCount(); ds->Add(d1y, extra); mitk::DataNode::Pointer d2y = mitk::DataNode::New(); d2y->SetProperty("name", mitk::StringProperty::New("d2y")); ds->Add(d2y, extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == d1y) && (ds->GetNamedNode("d2y") == d2y) && (ds->GetSources(d1y)->Size() == 1) // extra should be source of d1y && (ds->GetSources(d2y)->Size() == 1) // extra should be source of d2y && (ds->GetDerivations(extra)->Size() == 2) // d1y and d2y should be derived from extra , "add extra node"); ds->Remove(ds->GetDerivations(extra)); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == nullptr) // d1y should be nullptr now && (ds->GetNamedNode("d2y") == nullptr) // d2y should be nullptr now && (refCountbeforeDS == watcherD1y->GetReferenceCount()), "Checking removal of subset of two derived nodes from one parent node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr), "Checking removal of a parent node"); extra = nullptr; } /* Checking GetGrouptags() */ { const std::set groupTags = ds->GetGroupTags(); MITK_TEST_CONDITION((groupTags.size() == 2) && (std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 1") != groupTags.end()) && (std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 2") != groupTags.end()), "Checking GetGrouptags()"); } /* Checking Event handling */ DSEventReceiver listener; try { ds->AddNodeEvent += mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent += mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); mitk::DataNode::Pointer extra = mitk::DataNode::New(); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); ds->Add(extra); MITK_TEST_CONDITION(listener.m_NodeAdded == extra.GetPointer(), "Checking AddEvent"); ds->Remove(extra); MITK_TEST_CONDITION(listener.m_NodeRemoved == extra.GetPointer(), "Checking RemoveEvent"); /* RemoveListener */ ds->AddNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); listener.m_NodeAdded = nullptr; listener.m_NodeRemoved = nullptr; ds->Add(extra); ds->Remove(extra); MITK_TEST_CONDITION((listener.m_NodeRemoved == nullptr) && (listener.m_NodeAdded == nullptr), "Checking RemoveListener"); std::cout << "Pointer handling after event handling: " << std::flush; extra = nullptr; // delete reference to the node. its memory should be freed now MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Pointer handling after event handling"); } catch (...) { /* cleanup */ ds->AddNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); MITK_TEST_FAILED_MSG(<< "Exception during object removal methods"); } // Checking ComputeBoundingGeometry3D method*/ const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll(); auto geometry = ds->ComputeBoundingGeometry3D(); MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4, "Test for number or time steps with ComputeBoundingGeometry()"); mitk::TimeBounds timebounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4), "Test for timebounds with ComputeBoundingGeometry()"); for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++) { mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); mitk::TimeBounds bounds = geometry->GetTimeBounds(i); MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1), "Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()"); } geometry = ds->ComputeBoundingGeometry3D(all); MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4, "Test for number or time steps with ComputeBoundingGeometry(allNodes)"); timebounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4), "Test for timebounds with ComputeBoundingGeometry(allNodes)"); for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++) { mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); mitk::TimeBounds bounds = geometry->GetTimeBounds(i); MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1), "Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()"); } // test for thread safety of DataStorage try { mitk::StandaloneDataStorage::Pointer standaloneDataStorage = mitk::StandaloneDataStorage::New(); ItkDeleteEventListener listener(standaloneDataStorage); { mitk::DataNode::Pointer emptyNode = mitk::DataNode::New(); mitk::DataNode *pEmptyNode = emptyNode; listener.SetNode(emptyNode); standaloneDataStorage->Add(emptyNode); emptyNode = nullptr; // emptyNode is still alive because standaloneDataStorage // owns it standaloneDataStorage->Remove(pEmptyNode); // this should not freeze the whole thing } } catch (...) { MITK_TEST_FAILED_MSG(<< "Exception during testing DataStorage thread safe"); } /* Clear DataStorage */ ds->Remove(ds->GetAll()); MITK_TEST_CONDITION(ds->GetAll()->Size() == 0, "Checking Clear DataStorage"); } diff --git a/Modules/Core/test/mitkEventConfigTest.cpp b/Modules/Core/test/mitkEventConfigTest.cpp index 10687260f9..35eeb3a5ce 100644 --- a/Modules/Core/test/mitkEventConfigTest.cpp +++ b/Modules/Core/test/mitkEventConfigTest.cpp @@ -1,169 +1,169 @@ /*============================================================================ 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 "mitkEventConfig.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventConst.h" #include "mitkInteractionKeyEvent.h" #include "mitkMouseMoveEvent.h" #include "mitkMousePressEvent.h" #include "mitkMouseReleaseEvent.h" #include "mitkMouseWheelEvent.h" #include "mitkPropertyList.h" #include "mitkTestingMacros.h" #include "usGetModuleContext.h" #include "usModule.h" #include #include #include int mitkEventConfigTest(int argc, char *argv[]) { MITK_TEST_BEGIN("EventConfig") if (argc != 2) { MITK_ERROR << "Test needs configuration test file as parameter."; return -1; } /* * Loads a test a Config file and test if Config is build up correctly, * and if mapping from mitkEvents to EventVariant names works properly. * Indirectly this also tests the EventFactory Class, since we also test if the events have been constructed properly. * * The configuration object is constructed in three different ways, * each one is tested here. */ - // Construction using compiled-in resrouces: + // Construction using compiled-in resources: us::Module *module = us::GetModuleContext()->GetModule(); mitk::EventConfig newConfig("StatemachineConfigTest.xml", module); MITK_TEST_CONDITION_REQUIRED(newConfig.IsValid() == true, "01 Check if file can be loaded and is valid"); /* * Test the global properties: * Test if stored values match the ones in the test config file. */ mitk::PropertyList::Pointer properties = newConfig.GetAttributes(); std::string prop1, prop2; MITK_TEST_CONDITION_REQUIRED(properties->GetStringProperty("property1", prop1) && prop1 == "yes" && properties->GetStringProperty("scrollModus", prop2) && prop2 == "leftright", "02 Check Global Properties"); /* * Check if Events get mapped to the proper Variants */ mitk::Point2D pos; mitk::MousePressEvent::Pointer mpe1 = mitk::MousePressEvent::New(nullptr, pos, mitk::InteractionEvent::MiddleMouseButton | mitk::InteractionEvent::LeftMouseButton, mitk::InteractionEvent::ControlKey | mitk::InteractionEvent::AltKey, mitk::InteractionEvent::LeftMouseButton); mitk::MousePressEvent::Pointer standard1 = mitk::MousePressEvent::New(nullptr, pos, mitk::InteractionEvent::LeftMouseButton, mitk::InteractionEvent::NoKey, mitk::InteractionEvent::LeftMouseButton); mitk::MouseMoveEvent::Pointer mme1 = mitk::MouseMoveEvent::New(nullptr, pos, mitk::InteractionEvent::RightMouseButton | mitk::InteractionEvent::LeftMouseButton, mitk::InteractionEvent::ShiftKey); mitk::MouseMoveEvent::Pointer mme2 = mitk::MouseMoveEvent::New(nullptr, pos, mitk::InteractionEvent::RightMouseButton, mitk::InteractionEvent::ShiftKey); mitk::MouseWheelEvent::Pointer mwe1 = mitk::MouseWheelEvent::New( nullptr, pos, mitk::InteractionEvent::RightMouseButton, mitk::InteractionEvent::ShiftKey, -2); mitk::InteractionKeyEvent::Pointer ke = mitk::InteractionKeyEvent::New(nullptr, "l", mitk::InteractionEvent::NoKey); MITK_TEST_CONDITION_REQUIRED(newConfig.GetMappedEvent(mpe1.GetPointer()) == "Variant1" && newConfig.GetMappedEvent(standard1.GetPointer()) == "Standard1" && newConfig.GetMappedEvent(mme1.GetPointer()) == "Move2" && newConfig.GetMappedEvent(ke.GetPointer()) == "Key1" && newConfig.GetMappedEvent(mme2.GetPointer()) == "" // does not exist in file , "03 Check Mouse- and Key-Events "); // Construction providing a input stream std::ifstream configStream(argv[1]); mitk::EventConfig newConfig2(configStream); MITK_TEST_CONDITION_REQUIRED(newConfig2.IsValid() == true, "01 Check if file can be loaded and is valid"); /* * Test the global properties: * Test if stored values match the ones in the test config file. */ properties = newConfig2.GetAttributes(); MITK_TEST_CONDITION_REQUIRED(properties->GetStringProperty("property1", prop1) && prop1 == "yes" && properties->GetStringProperty("scrollModus", prop2) && prop2 == "leftright", "02 Check Global Properties"); /* * Check if Events get mapped to the proper Variants */ MITK_TEST_CONDITION_REQUIRED(newConfig2.GetMappedEvent(mpe1.GetPointer()) == "Variant1" && newConfig2.GetMappedEvent(standard1.GetPointer()) == "Standard1" && newConfig2.GetMappedEvent(mme1.GetPointer()) == "Move2" && newConfig2.GetMappedEvent(ke.GetPointer()) == "Key1" && newConfig2.GetMappedEvent(mme2.GetPointer()) == "" // does not exist in file , "03 Check Mouse- and Key-Events "); // always end with this! // Construction providing a property list std::vector configDescription; mitk::PropertyList::Pointer propertyList1 = mitk::PropertyList::New(); propertyList1->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass().c_str(), "MousePressEvent"); propertyList1->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant().c_str(), "MousePressEventVariant"); propertyList1->SetStringProperty("Modifiers", "CTRL,ALT"); configDescription.push_back(propertyList1); mitk::PropertyList::Pointer propertyList2 = mitk::PropertyList::New(); propertyList2->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass().c_str(), "MOUSERELEASEEVENT"); propertyList2->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant().c_str(), "MouseReleaseEventVariant"); propertyList2->SetStringProperty("Modifiers", "SHIFT"); configDescription.push_back(propertyList2); mitk::PropertyList::Pointer propertyList3 = mitk::PropertyList::New(); propertyList3->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass().c_str(), "MOUSERELEASEEVENT"); propertyList3->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant().c_str(), "MouseReleaseEventVariant"); propertyList3->SetStringProperty("Modifiers", "ALT"); configDescription.push_back(propertyList3); mitk::EventConfig newConfig3(configDescription); mitk::MousePressEvent::Pointer mousePress1 = mitk::MousePressEvent::New(nullptr, pos, mitk::InteractionEvent::NoButton, mitk::InteractionEvent::AltKey | mitk::InteractionEvent::ControlKey, mitk::InteractionEvent::NoButton); mitk::MouseReleaseEvent::Pointer mouseRelease1 = mitk::MouseReleaseEvent::New( nullptr, pos, mitk::InteractionEvent::NoButton, mitk::InteractionEvent::ShiftKey, mitk::InteractionEvent::NoButton); // create a second event with the same name but different modifiers... mitk::MouseReleaseEvent::Pointer mouseRelease2 = mitk::MouseReleaseEvent::New( nullptr, pos, mitk::InteractionEvent::NoButton, mitk::InteractionEvent::AltKey, mitk::InteractionEvent::NoButton); MITK_TEST_CONDITION_REQUIRED(newConfig3.GetMappedEvent(mousePress1.GetPointer()) == "MousePressEventVariant" && newConfig3.GetMappedEvent(mouseRelease1.GetPointer()) == "MouseReleaseEventVariant" && newConfig3.GetMappedEvent(mouseRelease2.GetPointer()) == "MouseReleaseEventVariant", "04 Check Mouseevents from PropertyLists"); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkExceptionTest.cpp b/Modules/Core/test/mitkExceptionTest.cpp index ca22e0a12f..416d085c1c 100644 --- a/Modules/Core/test/mitkExceptionTest.cpp +++ b/Modules/Core/test/mitkExceptionTest.cpp @@ -1,365 +1,365 @@ /*============================================================================ 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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include "mitkException.h" #include "mitkExceptionMacro.h" #include "mitkTestingMacros.h" #include // ITK includes #include #include // VTK includes #include class SpecializedTestException : public mitk::Exception { public: mitkExceptionClassMacro(SpecializedTestException, mitk::Exception); }; class mitkExceptionTestSuite : public itk::Object, public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkExceptionTestSuite); MITK_TEST(TestExceptionConstructor_Success); MITK_TEST(TestSpecializedExceptionConstructor_Success); MITK_TEST(TestExceptionMessageStreamAddingString_Success); MITK_TEST(TestExceptionMessageStreamAddingSingleChars_Success); MITK_TEST(TestExceptionMessageStreamAddingObject_Success); MITK_TEST(TestSpecializedExceptionMessageStreamAddingString); MITK_TEST(TestExceptionMessageStreamThrowing_Success); MITK_TEST(TestMitkThrowMacroThrowing_Success); MITK_TEST(TestMitkThrowMacroMessage_Success); MITK_TEST(TestMitkThrowMacroException_Success); MITK_TEST(TestMitkThrowMacroSpezcializedException); MITK_TEST(TestGetNumberOfRethrows_Success); MITK_TEST(TestGetRethrowDataWithNegativNumber_Success); MITK_TEST(TestGetRethrowDataWithNumberZero_Success); MITK_TEST(TestGetRethrowDataWithNumberOne_Success); MITK_TEST(TestAddRethrowData_Success); MITK_TEST(TestFirstRethrowDataAreStoredProperly_Success); MITK_TEST(TestSecondRethrowDataAreStoredProperly_Success); MITK_TEST(TestRethrowMacro_Success); CPPUNIT_TEST_SUITE_END(); private: bool m_ExceptionThrown; std::string m_MessageText; std::string m_Message; std::string m_File; int m_Line; mitk::Exception m_E = mitk::Exception("test.cpp", 155, "", ""); mitk::Exception m_MyException = mitk::Exception("testfile.cpp", 111, "testmessage"); public: mitkClassMacroItkParent(mitkExceptionTestSuite, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) void throwExceptionManually() // this method is ONLY to test the constructor and no code example // normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("test.cpp", 155, "", ""); } void throwSpecializedExceptionManually() // this method is ONLY to test the constructor and no code example // normally exceptions should only be thrown by using the exception macro! { throw SpecializedTestException("test.cpp", 155, "", ""); } void throwExceptionManually(std::string message1, std::string message2) // this method is ONLY to test methods of mitk::Exception and no code example // normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("testfile.cpp", 155, message1.c_str(), "") << message2; } - void throwExceptionWithThrowMacro() { mitkThrow() << "TEST EXCEPION THROWING WITH mitkThrow()"; } + void throwExceptionWithThrowMacro() { mitkThrow() << "TEST EXCEPTION THROWING WITH mitkThrow()"; } void throwExceptionWithThrowMacro(std::string message) { mitkThrow() << message.c_str(); } void throwSpecializedExceptionWithThrowMacro(std::string message) { mitkThrowException(mitk::Exception) << message; } void throwSpecializedExceptionWithThrowMacro2(std::string message) { mitkThrowException(SpecializedTestException) << message; } void reThrowExceptionWithReThrowMacro(std::string messageThrow, std::string messageReThrow) { try { throwExceptionWithThrowMacro(messageThrow); } catch (mitk::Exception &e) { mitkReThrow(e) << messageReThrow; } } void setUp() override { m_ExceptionThrown = false; m_MessageText = ""; m_Message = "invalid"; m_File = "invalid"; m_Line = -1; } void tearDown() override { m_ExceptionThrown = false; m_MessageText = ""; m_Message = ""; m_File = ""; m_Line = 0; } void TestExceptionConstructor_Success() { try { this->throwExceptionManually(); } catch (const mitk::Exception &) { m_ExceptionThrown = true; } CPPUNIT_ASSERT_MESSAGE("Testing constructor of mitkException", m_ExceptionThrown); } void TestSpecializedExceptionConstructor_Success() { try { this->throwSpecializedExceptionManually(); } catch (const SpecializedTestException &) { m_ExceptionThrown = true; } CPPUNIT_ASSERT_MESSAGE("Testing constructor specialized exception (deriving from mitkException)", m_ExceptionThrown); } //##### this methods are ONLY to test the streaming operators of the exceptions and //##### NO code example. Please do not instantiate exceptions by yourself in normal code! //##### Normally exceptions should only be thrown by using the exception macro! void TestExceptionMessageStreamAddingString_Success() { m_MyException << " and additional stream"; CPPUNIT_ASSERT_MESSAGE("Testing mitkException message stream (adding std::string)", m_MyException.GetDescription() == std::string("testmessage and additional stream")); } void TestExceptionMessageStreamAddingSingleChars_Success() { m_MyException.SetDescription("testmessage2"); m_MyException << ' ' << 'a' << 'n' << 'd' << ' ' << 'c' << 'h' << 'a' << 'r' << 's'; CPPUNIT_ASSERT_MESSAGE("Testing mitkException message stream (adding single chars)", m_MyException.GetDescription() == std::string("testmessage2 and chars")); } void TestExceptionMessageStreamAddingObject_Success() { m_MyException.SetDescription("testmessage3"); m_MyException << m_MyException; // adding the object itself makes no sense but should work CPPUNIT_ASSERT_MESSAGE("Testing mitkException message stream (adding object)", m_MyException.GetDescription() != std::string("")); } void TestSpecializedExceptionMessageStreamAddingString() { SpecializedTestException mySpecializedException = SpecializedTestException("testfile.cpp", 111, "testmessage", "test"); mySpecializedException << " and additional stream"; CPPUNIT_ASSERT_MESSAGE("Testing specialized exception message stream (adding std::string)", mySpecializedException.GetDescription() == std::string("testmessage and additional stream")); } void TestExceptionMessageStreamThrowing_Success() { std::string thrownMessage = ""; try { this->throwExceptionManually("message1", " and message2"); } catch (const mitk::Exception &e) { thrownMessage = e.GetDescription(); m_ExceptionThrown = true; } CPPUNIT_ASSERT_MESSAGE("Testing throwing and streaming of mitk::Exception together.", m_ExceptionThrown && (thrownMessage == std::string("message1 and message2"))); } void TestMitkThrowMacroThrowing_Success() { // case 1: test throwing try { this->throwExceptionWithThrowMacro(); } catch (const mitk::Exception &) { m_ExceptionThrown = true; } CPPUNIT_ASSERT_MESSAGE("Testing mitkThrow()", m_ExceptionThrown); } void TestMitkThrowMacroMessage_Success() { // case 2: test message text try { this->throwExceptionWithThrowMacro("test123"); } catch (const mitk::Exception &e) { m_ExceptionThrown = true; m_MessageText = e.GetDescription(); } CPPUNIT_ASSERT_MESSAGE("Testing message test of mitkThrow()", (m_ExceptionThrown && (m_MessageText == "test123"))); } void TestMitkThrowMacroException_Success() { // case 3: specialized exception / command mitkThrow(mitk::Exception) try { this->throwSpecializedExceptionWithThrowMacro("test123"); } catch (const mitk::Exception &e) { m_ExceptionThrown = true; m_MessageText = e.GetDescription(); } CPPUNIT_ASSERT_MESSAGE("Testing special exception with mitkThrow(mitk::Exception)", m_ExceptionThrown && m_MessageText == "test123"); } void TestMitkThrowMacroSpezcializedException() { // case 4: specialized exception / command mitkThrow(mitk::SpecializedException) try { this->throwSpecializedExceptionWithThrowMacro2("test123"); } catch (const SpecializedTestException &e) { m_ExceptionThrown = true; m_MessageText = e.GetDescription(); } CPPUNIT_ASSERT_MESSAGE("Testing special exception with mitkThrow(mitk::SpecializedException)", m_ExceptionThrown && m_MessageText == "test123"); } //##### this methods are ONLY to test methods of mitk::Exception and no code example //##### normally exceptions should only be instantiated and thrown by using the exception macros! void TestGetNumberOfRethrows_Success() { // first: testing rethrow information methods, when no information is stored // case 1.1: method GetNumberOfRethrows() CPPUNIT_ASSERT_MESSAGE("Testing GetNumberOfRethrows() with empty rethrow information", m_E.GetNumberOfRethrows() == 0); } void TestGetRethrowDataWithNegativNumber_Success() { // case 1.2: GetRethrowData() with negative number m_E.GetRethrowData(-1, m_File, m_Line, m_Message); CPPUNIT_ASSERT_MESSAGE("Testing GetRethrowData() with invalid rethrow number (negative).", ((m_File == "") && (m_Line == 0) && (m_Message == ""))); } void TestGetRethrowDataWithNumberZero_Success() { // case 1.3: GetRethrowData() with number 0 m_E.GetRethrowData(0, m_File, m_Line, m_Message); CPPUNIT_ASSERT_MESSAGE("Testing GetRethrowData() with non-existing rethrow number (0).", ((m_File == "") && (m_Line == 0) && (m_Message == ""))); } void TestGetRethrowDataWithNumberOne_Success() { // case 1.4: GetRethrowData() with number 1 m_E.GetRethrowData(1, m_File, m_Line, m_Message); CPPUNIT_ASSERT_MESSAGE("Testing GetRethrowData() with non-existing rethrow number (1).", ((m_File == "") && (m_Line == 0) && (m_Message == ""))); } void TestAddRethrowData_Success() { // second: add rethrow data m_E.AddRethrowData("test2.cpp", 10, "Rethrow one"); CPPUNIT_ASSERT_MESSAGE("Testing adding of rethrow data.", m_E.GetNumberOfRethrows() == 1); m_E.AddRethrowData("test3.cpp", 15, "Rethrow two"); CPPUNIT_ASSERT_MESSAGE("Testing adding of more rethrow data.", m_E.GetNumberOfRethrows() == 2); } void TestFirstRethrowDataAreStoredProperly_Success() { // third: test if this rethrow data was stored properly m_E.AddRethrowData("test2.cpp", 10, "Rethrow one"); m_E.GetRethrowData(0, m_File, m_Line, m_Message); CPPUNIT_ASSERT_MESSAGE("Testing stored information of first rethrow.", ((m_File == "test2.cpp") && (m_Line == 10) && (m_Message == "Rethrow one"))); } void TestSecondRethrowDataAreStoredProperly_Success() { m_E.AddRethrowData("test2.cpp", 10, "Rethrow one"); m_E.AddRethrowData("test3.cpp", 15, "Rethrow two"); m_E.GetRethrowData(1, m_File, m_Line, m_Message); CPPUNIT_ASSERT_MESSAGE("Testing stored information of second rethrow.", ((m_File == "test3.cpp") && (m_Line == 15) && (m_Message == "Rethrow two"))); } void TestRethrowMacro_Success() { // case 1: test throwing try { this->reThrowExceptionWithReThrowMacro("Test original message.", "Test rethrow message."); } catch (const mitk::Exception &e) { m_Message = e.GetDescription(); m_ExceptionThrown = true; } CPPUNIT_ASSERT_MESSAGE("Testing mitkReThrow()", m_ExceptionThrown); CPPUNIT_ASSERT_MESSAGE("Testing message/descriprion after rethrow.", m_Message == "Test original message.Test rethrow message."); } }; MITK_TEST_SUITE_REGISTRATION(mitkException) diff --git a/Modules/Core/test/mitkExtractSliceFilterTest.cpp b/Modules/Core/test/mitkExtractSliceFilterTest.cpp index 6bb27c7e6b..3dc6ef8139 100644 --- a/Modules/Core/test/mitkExtractSliceFilterTest.cpp +++ b/Modules/Core/test/mitkExtractSliceFilterTest.cpp @@ -1,1069 +1,1069 @@ /*============================================================================ 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 #include #include #include #include #include #include #include #include #include #include #include #include // use this to create the test volume on the fly #define CREATE_VOLUME // use this to save the created volume //#define SAVE_VOLUME // use this to calculate the error from the sphere mathematical model to our pixel based one //#define CALC_TESTFAILURE_DEVIATION // use this to render an oblique slice through a specified image //#define SHOW_SLICE_IN_RENDER_WINDOW // use this to have infos printed in mbilog //#define EXTRACTOR_DEBUG /*these are the deviations calculated by the function CalcTestFailureDeviation (see for details)*/ #define Testfailure_Deviation_Mean_128 0.853842 #define Testfailure_Deviation_Volume_128 0.145184 #define Testfailure_Deviation_Diameter_128 1.5625 #define Testfailure_Deviation_Mean_256 0.397693 #define Testfailure_Deviation_Volume_256 0.0141357 #define Testfailure_Deviation_Diameter_256 0.78125 #define Testfailure_Deviation_Mean_512 0.205277 #define Testfailure_Deviation_Volume_512 0.01993 #define Testfailure_Deviation_Diameter_512 0.390625 class mitkExtractSliceFilterTestClass { public: static void TestSlice(mitk::PlaneGeometry *planeGeometry, std::string testname) { TestPlane = planeGeometry; TestName = testname; mitk::ScalarType centerCoordValue = TestvolumeSize / 2.0; mitk::ScalarType center[3] = {centerCoordValue, centerCoordValue, centerCoordValue}; mitk::Point3D centerIndex(center); double radius = TestvolumeSize / 4.0; if (TestPlane->Distance(centerIndex) >= radius) return; // outside sphere // feed ExtractSliceFilter mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(TestVolume); slicer->SetWorldGeometry(TestPlane); slicer->Update(); MITK_TEST_CONDITION_REQUIRED(slicer->GetOutput() != nullptr, "Extractor returned a slice"); mitk::Image::Pointer reslicedImage = slicer->GetOutput(); AccessFixedDimensionByItk(reslicedImage, TestSphereRadiusByItk, 2); AccessFixedDimensionByItk(reslicedImage, TestSphereAreaByItk, 2); /* double devArea, devDiameter; if(TestvolumeSize == 128.0){ devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128; } else if(TestvolumeSize == 256.0){devArea = Testfailure_Deviation_Volume_256; devDiameter = Testfailure_Deviation_Diameter_256;} else if (TestvolumeSize == 512.0){devArea = Testfailure_Deviation_Volume_512; devDiameter = Testfailure_Deviation_Diameter_512;} else{devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128;} */ std::string areatestName = TestName.append(" area"); std::string diametertestName = TestName.append(" testing diameter"); // TODO think about the deviation, 1% makes no sense at all MITK_TEST_CONDITION(std::abs(100 - testResults.percentageAreaCalcToPixel) < 1, areatestName); MITK_TEST_CONDITION(std::abs(100 - testResults.percentageRadiusToPixel) < 1, diametertestName); #ifdef EXTRACTOR_DEBUG MITK_INFO << TestName << " >>> " << "planeDistanceToSphereCenter: " << testResults.planeDistanceToSphereCenter; MITK_INFO << "area in pixels: " << testResults.areaInPixel << " <-> area in mm: " << testResults.areaCalculated << " = " << testResults.percentageAreaCalcToPixel << "%"; MITK_INFO << "calculated diameter: " << testResults.diameterCalculated << " <-> diameter in mm: " << testResults.diameterInMM << " <-> diameter in pixel: " << testResults.diameterInPixel << " = " << testResults.percentageRadiusToPixel << "%"; #endif } /* * get the radius of the slice of a sphere based on pixel distance from edge to edge of the circle. */ template static void TestSphereRadiusByItk(itk::Image *inputImage) { typedef itk::Image InputImageType; // set the index to the middle of the image's edge at x and y axis typename InputImageType::IndexType currentIndexX; currentIndexX[0] = (int)(TestvolumeSize / 2.0); currentIndexX[1] = 0; typename InputImageType::IndexType currentIndexY; currentIndexY[0] = 0; currentIndexY[1] = (int)(TestvolumeSize / 2.0); // remember the last pixel value double lastValueX = inputImage->GetPixel(currentIndexX); double lastValueY = inputImage->GetPixel(currentIndexY); // storage for the index marks std::vector indicesX; std::vector indicesY; /*Get four indices on the edge of the circle*/ while (currentIndexX[1] < TestvolumeSize && currentIndexX[0] < TestvolumeSize) { // move x direction currentIndexX[1] += 1; // move y direction currentIndexY[0] += 1; if (inputImage->GetPixel(currentIndexX) > lastValueX) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1]; indicesX.push_back(markIndex); } else if (inputImage->GetPixel(currentIndexX) < lastValueX) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1] - 1; // value inside the sphere indicesX.push_back(markIndex); } if (inputImage->GetPixel(currentIndexY) > lastValueY) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1]; indicesY.push_back(markIndex); } else if (inputImage->GetPixel(currentIndexY) < lastValueY) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1] - 1; // value inside the sphere indicesY.push_back(markIndex); } // found both marks? if (indicesX.size() == 2 && indicesY.size() == 2) break; // the new 'last' values lastValueX = inputImage->GetPixel(currentIndexX); lastValueY = inputImage->GetPixel(currentIndexY); } /* *If we are here we found the four marks on the edge of the circle. *For the case our plane is rotated and shifted, we have to calculate the center of the circle, *else the center is the intersection of both straight lines between the marks. *When we have the center, the diameter of the circle will be checked to the reference value(math!). */ // each distance from the first mark of each direction to the center of the straight line between the marks double distanceToCenterX = std::abs(indicesX[0][1] - indicesX[1][1]) / 2.0; // double distanceToCenterY = std::abs(indicesY[0][0] - indicesY[1][0]) / 2.0; // the center of the straight lines typename InputImageType::IndexType centerX; // typename InputImageType::IndexType centerY; centerX[0] = indicesX[0][0]; centerX[1] = indicesX[0][1] + distanceToCenterX; // TODO think about implicit cast to int. this is not the real center of the image, which could be between two // pixels // centerY[0] = indicesY[0][0] + distanceToCenterY; // centerY[1] = inidcesY[0][1]; typename InputImageType::IndexType currentIndex(centerX); lastValueX = inputImage->GetPixel(currentIndex); long sumpixels = 0; std::vector diameterIndices; // move up while (currentIndex[1] < TestvolumeSize) { currentIndex[1] += 1; if (inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1] - 1; diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } currentIndex[1] -= sumpixels; // move back to center to go in the other direction lastValueX = inputImage->GetPixel(currentIndex); // move down while (currentIndex[1] >= 0) { currentIndex[1] -= 1; if (inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1]; // outside sphere because we want to calculate the distance from edge to edge diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } /* *Now sumpixels should be the apromximate diameter of the circle. This is checked with the calculated diameter from *the plane transformation(math). */ mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize / 4.0; // calculate the radius of the circle cut from the sphere by the plane double diameter = 2.0 * std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2)); double percentageRadiusToPixel = 100 / diameter * sumpixels; /* *calculate the radius in mm by the both marks of the center line by using the world coordinates */ // get the points as 3D coordinates mitk::Vector3D diameterPointRight, diameterPointLeft; diameterPointRight[2] = diameterPointLeft[2] = 0.0; diameterPointLeft[0] = diameterIndices[0][0]; diameterPointLeft[1] = diameterIndices[0][1]; diameterPointRight[0] = diameterIndices[1][0]; diameterPointRight[1] = diameterIndices[1][1]; // transform to worldcoordinates TestVolume->GetGeometry()->IndexToWorld(diameterPointLeft, diameterPointLeft); TestVolume->GetGeometry()->IndexToWorld(diameterPointRight, diameterPointRight); // euklidian distance double diameterInMM = ((diameterPointLeft * -1.0) + diameterPointRight).GetNorm(); testResults.diameterInMM = diameterInMM; testResults.diameterCalculated = diameter; testResults.diameterInPixel = sumpixels; testResults.percentageRadiusToPixel = percentageRadiusToPixel; testResults.planeDistanceToSphereCenter = planeDistanceToSphereCenter; } /*brute force the area pixel by pixel*/ template static void TestSphereAreaByItk(itk::Image *inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageIterator imageIterator(inputImage, inputImage->GetLargestPossibleRegion()); imageIterator.GoToBegin(); int sumPixelsInArea = 0; while (!imageIterator.IsAtEnd()) { if (inputImage->GetPixel(imageIterator.GetIndex()) == pixelValueSet) sumPixelsInArea++; ++imageIterator; } mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize / 4.0; // calculate the radius of the circle cut from the sphere by the plane double radius = std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2)); double areaInMM = 3.14159265358979 * std::pow(radius, 2); testResults.areaCalculated = areaInMM; testResults.areaInPixel = sumPixelsInArea; testResults.percentageAreaCalcToPixel = 100 / areaInMM * sumPixelsInArea; } /* * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel vaues of the voxel * in the volume with the pixel value in the resliced image. * there are some indice shifting problems which causes the test to fail for oblique planes. seems like the chosen - * worldcoordinate is not corrresponding to the index in the 2D image. and so the pixel values are not the same as + * worldcoordinate is not corresponding to the index in the 2D image. and so the pixel values are not the same as * expected. */ static void PixelvalueBasedTest() { /* setup itk image */ typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = 32; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator(image, image->GetLargestPossibleRegion()); imageIterator.GoToBegin(); unsigned short pixelValue = 0; // fill the image with distinct values while (!imageIterator.IsAtEnd()) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer imageInMitk; CastToMitkImage(image, imageInMitk); /*mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(imageInMitk); std::string file = "C:\\Users\\schroedt\\Desktop\\cube.nrrd"; writer->SetFileName(file); writer->Update();*/ PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Coronal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Sagittal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Axial); } static void PixelvalueBasedTestByPlane(mitk::Image *imageInMitk, mitk::PlaneGeometry::PlaneOrientation orientation) { typedef itk::Image ImageType; // set the seed of the rand function srand((unsigned)time(nullptr)); /* setup a random orthogonal plane */ int sliceindex = 17; // rand() % 32; bool isFrontside = true; bool isRotated = false; if (orientation == mitk::PlaneGeometry::Axial) { /*isFrontside = false; isRotated = true;*/ } mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(imageInMitk->GetGeometry(), orientation, sliceindex, isFrontside, isRotated); mitk::Point3D origin = plane->GetOrigin(); mitk::Vector3D normal; normal = plane->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(origin); // we dont need this any more, because we are only testing orthogonal planes /*mitk::Vector3D rotationVector; rotationVector[0] = randFloat(); rotationVector[1] = randFloat(); rotationVector[2] = randFloat(); float degree = randFloat() * 180.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, plane->GetCenter(), rotationVector, degree); plane->ExecuteOperation(op); delete op;*/ /* end setup plane */ /* define a point in the 3D volume. * add the two axis vectors of the plane (each multiplied with a * random number) to the origin. now the two random numbers * become our index coordinates in the 2D image, because the * length of the axis vectors is 1. */ mitk::Point3D planeOrigin = plane->GetOrigin(); mitk::Vector3D axis0, axis1; axis0 = plane->GetAxisVector(0); axis1 = plane->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); unsigned char n1 = 7; // rand() % 32; unsigned char n2 = 13; // rand() % 32; mitk::Point3D testPoint3DInWorld; testPoint3DInWorld = planeOrigin + (axis0 * n1) + (axis1 * n2); // get the index of the point in the 3D volume ImageType::IndexType testPoint3DInIndex; imageInMitk->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint3DInIndex); itk::Index<3> testPoint2DInIndex; /* end define a point in the 3D volume.*/ // do reslicing at the plane mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(imageInMitk); slicer->SetWorldGeometry(plane); slicer->Update(); mitk::Image::Pointer slice = slicer->GetOutput(); // Get TestPoiont3D as Index in Slice slice->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint2DInIndex); mitk::Point3D p, sliceIndexToWorld, imageIndexToWorld; p[0] = testPoint2DInIndex[0]; p[1] = testPoint2DInIndex[1]; p[2] = testPoint2DInIndex[2]; slice->GetGeometry()->IndexToWorld(p, sliceIndexToWorld); p[0] = testPoint3DInIndex[0]; p[1] = testPoint3DInIndex[1]; p[2] = testPoint3DInIndex[2]; imageInMitk->GetGeometry()->IndexToWorld(p, imageIndexToWorld); itk::Index<2> testPoint2DIn2DIndex; testPoint2DIn2DIndex[0] = testPoint2DInIndex[0]; testPoint2DIn2DIndex[1] = testPoint2DInIndex[1]; typedef mitk::ImagePixelReadAccessor VolumeReadAccessorType; typedef mitk::ImagePixelReadAccessor SliceReadAccessorType; VolumeReadAccessorType VolumeReadAccessor(imageInMitk); SliceReadAccessorType SliceReadAccessor(slice); // compare the pixelvalues of the defined point in the 3D volume with the value of the resliced image unsigned short valueAt3DVolume = VolumeReadAccessor.GetPixelByIndex(testPoint3DInIndex); unsigned short valueAtSlice = SliceReadAccessor.GetPixelByIndex(testPoint2DIn2DIndex); // valueAt3DVolume == valueAtSlice is not always working. because of rounding errors // indices are shifted MITK_TEST_CONDITION(valueAt3DVolume == valueAtSlice, "comparing pixelvalues for orthogonal plane"); vtkSmartPointer imageInVtk = imageInMitk->GetVtkImageData(); vtkSmartPointer sliceInVtk = slice->GetVtkImageData(); double PixelvalueByMitkOutput = sliceInVtk->GetScalarComponentAsDouble(n1, n2, 0, 0); // double valueVTKinImage = imageInVtk->GetScalarComponentAsDouble(testPoint3DInIndex[0], testPoint3DInIndex[1], // testPoint3DInIndex[2], 0); /* Test that everything is working equally if vtkoutput is used instead of the default output * from mitk ImageToImageFilter */ mitk::ExtractSliceFilter::Pointer slicerWithVtkOutput = mitk::ExtractSliceFilter::New(); slicerWithVtkOutput->SetInput(imageInMitk); slicerWithVtkOutput->SetWorldGeometry(plane); slicerWithVtkOutput->SetVtkOutputRequest(true); slicerWithVtkOutput->Update(); vtkSmartPointer vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput(); double PixelvalueByVtkOutput = vtkImageByVtkOutput->GetScalarComponentAsDouble(n1, n2, 0, 0); MITK_TEST_CONDITION(PixelvalueByMitkOutput == PixelvalueByVtkOutput, - "testing convertion of image output vtk->mitk by reslicer"); + "testing conversion of image output vtk->mitk by reslicer"); /*================ mbilog outputs ===========================*/ #ifdef EXTRACTOR_DEBUG MITK_INFO << "\n" << "TESTINFO index: " << sliceindex << " orientation: " << orientation << " frontside: " << isFrontside << " rotated: " << isRotated; MITK_INFO << "\n" << "slice index to world: " << sliceIndexToWorld; MITK_INFO << "\n" << "image index to world: " << imageIndexToWorld; MITK_INFO << "\n" << "vtk: slice: " << PixelvalueByMitkOutput << ", image: " << valueVTKinImage; MITK_INFO << "\n" << "testPoint3D InWorld" << testPoint3DInWorld << " is " << testPoint2DInIndex << " in 2D"; MITK_INFO << "\n" << "randoms: " << ((int)n1) << ", " << ((int)n2); MITK_INFO << "\n" << "point is inside plane: " << plane->IsInside(testPoint3DInWorld) << " and volume: " << imageInMitk->GetGeometry()->IsInside(testPoint3DInWorld); MITK_INFO << "\n" << "volume idx: " << testPoint3DInIndex << " = " << valueAt3DVolume; MITK_INFO << "\n" << "volume world: " << testPoint3DInWorld << " = " << valueAt3DVolumeByWorld; MITK_INFO << "\n" << "slice idx: " << testPoint2DInIndex << " = " << valueAtSlice; itk::Index<3> curr; curr[0] = curr[1] = curr[2] = 0; for (int i = 0; i < 32; ++i) { for (int j = 0; j < 32; ++j) { ++curr[1]; if (SliceReadAccessor.GetPixelByIndex(curr) == valueAt3DVolume) { MITK_INFO << "\n" << valueAt3DVolume << " MATCHED mitk " << curr; } } curr[1] = 0; ++curr[0]; } typedef itk::Image Image2DType; Image2DType::Pointer img = Image2DType::New(); CastToItkImage(slice, img); typedef itk::ImageRegionConstIterator Iterator2D; Iterator2D iter(img, img->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { if (img->GetPixel(iter.GetIndex()) == valueAt3DVolume) MITK_INFO << "\n" << valueAt3DVolume << " MATCHED itk " << iter.GetIndex(); ++iter; } #endif // EXTRACTOR_DEBUG } /* random a float value */ static float randFloat() { return (((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) + (((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) / ((float)RAND_MAX + 1.0); } /* create a sphere with the size of the given testVolumeSize*/ static void InitializeTestVolume() { #ifdef CREATE_VOLUME // do sphere creation ItkVolumeGeneration(); #ifdef SAVE_VOLUME // save in file mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(TestVolume); std::string file; std::ostringstream filename; filename << "C:\\home\\schroedt\\MITK\\Modules\\ImageExtraction\\Testing\\Data\\sphere_"; filename << TestvolumeSize; filename << ".nrrd"; file = filename.str(); writer->SetFileName(file); writer->Update(); #endif // SAVE_VOLUME #endif #ifndef CREATE_VOLUME // read from file mitk::StandardFileLocations::Pointer locator = mitk::StandardFileLocations::GetInstance(); std::string filename = locator->FindFile("sphere_512.nrrd.mhd", "Modules/ImageExtraction/Testing/Data"); TestVolume = mitk::IOUtil::Load(filename); #endif #ifdef CALC_TESTFAILURE_DEVIATION // get the TestFailureDeviation in % AccessFixedDimensionByItk(TestVolume, CalcTestFailureDeviation, 3); #endif } // the test result of the sphere reslice struct SliceProperties { double planeDistanceToSphereCenter; double diameterInMM; double diameterInPixel; double diameterCalculated; double percentageRadiusToPixel; double areaCalculated; double areaInPixel; double percentageAreaCalcToPixel; }; static mitk::Image::Pointer TestVolume; static double TestvolumeSize; static mitk::PlaneGeometry::Pointer TestPlane; static std::string TestName; static unsigned char pixelValueSet; static SliceProperties testResults; static double TestFailureDeviation; private: /* * Generate a sphere with a radius of TestvolumeSize / 4.0 */ static void ItkVolumeGeneration() { typedef itk::Image TestVolumeType; typedef itk::ImageRegionConstIterator ImageIterator; TestVolumeType::Pointer sphereImage = TestVolumeType::New(); TestVolumeType::IndexType start; start[0] = start[1] = start[2] = 0; TestVolumeType::SizeType size; size[0] = size[1] = size[2] = TestvolumeSize; TestVolumeType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); sphereImage->SetRegions(imgRegion); sphereImage->SetSpacing(1.0); sphereImage->Allocate(); sphereImage->FillBuffer(0); mitk::Vector3D center; center[0] = center[1] = center[2] = TestvolumeSize / 2.0; double radius = TestvolumeSize / 4.0; double pixelValue = pixelValueSet; ImageIterator imageIterator(sphereImage, sphereImage->GetLargestPossibleRegion()); imageIterator.GoToBegin(); mitk::Vector3D currentVoxelInIndex; while (!imageIterator.IsAtEnd()) { currentVoxelInIndex[0] = imageIterator.GetIndex()[0]; currentVoxelInIndex[1] = imageIterator.GetIndex()[1]; currentVoxelInIndex[2] = imageIterator.GetIndex()[2]; double distanceToCenter = (center + (currentVoxelInIndex * -1.0)).GetNorm(); // if distance to center is smaller then the radius of the sphere if (distanceToCenter < radius) { sphereImage->SetPixel(imageIterator.GetIndex(), pixelValue); } ++imageIterator; } CastToMitkImage(sphereImage, TestVolume); } - /* calculate the devation of the voxel object to the mathematical sphere object. + /* calculate the deviation of the voxel object to the mathematical sphere object. * this is use to make a statement about the accuracy of the resliced image, eg. the circle's diameter or area. */ template static void CalcTestFailureDeviation(itk::Image *inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageIterator iterator(inputImage, inputImage->GetLargestPossibleRegion()); iterator.GoToBegin(); int volumeInPixel = 0; while (!iterator.IsAtEnd()) { if (inputImage->GetPixel(iterator.GetIndex()) == pixelValueSet) volumeInPixel++; ++iterator; } double diameter = TestvolumeSize / 2.0; double volumeCalculated = (1.0 / 6.0) * 3.14159265358979 * std::pow(diameter, 3); double volumeDeviation = std::abs(100 - (100 / volumeCalculated * volumeInPixel)); typename InputImageType::IndexType index; index[0] = index[1] = TestvolumeSize / 2.0; index[2] = 0; int sumpixels = 0; while (index[2] < TestvolumeSize) { if (inputImage->GetPixel(index) == pixelValueSet) sumpixels++; index[2] += 1; } double diameterDeviation = std::abs(100 - (100 / diameter * sumpixels)); #ifdef DEBUG MITK_INFO << "volume deviation: " << volumeDeviation << " diameter deviation:" << diameterDeviation; #endif mitkExtractSliceFilterTestClass::TestFailureDeviation = (volumeDeviation + diameterDeviation) / 2.0; } }; /*================ #END class ================*/ -/*================#BEGIN Instanciation of members ================*/ +/*================#BEGIN Instantiation of members ================*/ mitk::Image::Pointer mitkExtractSliceFilterTestClass::TestVolume = mitk::Image::New(); double mitkExtractSliceFilterTestClass::TestvolumeSize = 256.0; mitk::PlaneGeometry::Pointer mitkExtractSliceFilterTestClass::TestPlane = mitk::PlaneGeometry::New(); std::string mitkExtractSliceFilterTestClass::TestName = ""; unsigned char mitkExtractSliceFilterTestClass::pixelValueSet = 255; mitkExtractSliceFilterTestClass::SliceProperties mitkExtractSliceFilterTestClass::testResults = { -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0}; double mitkExtractSliceFilterTestClass::TestFailureDeviation = 0.0; -/*================ #END Instanciation of members ================*/ +/*================ #END Instantiation of members ================*/ /*================ #BEGIN test main ================*/ int mitkExtractSliceFilterTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkExtractSliceFilterTest") // pixelvalue based testing mitkExtractSliceFilterTestClass::PixelvalueBasedTest(); // initialize sphere test volume mitkExtractSliceFilterTestClass::InitializeTestVolume(); mitk::Vector3D spacing = mitkExtractSliceFilterTestClass::TestVolume->GetGeometry()->GetSpacing(); // the center of the sphere = center of image double sphereCenter = mitkExtractSliceFilterTestClass::TestvolumeSize / 2.0; double planeSize = mitkExtractSliceFilterTestClass::TestvolumeSize; /* axial plane */ mitk::PlaneGeometry::Pointer geometryAxial = mitk::PlaneGeometry::New(); geometryAxial->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Axial, sphereCenter, false, true); geometryAxial->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin = geometryAxial->GetOrigin(); mitk::Vector3D normal; normal = geometryAxial->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometryAxial->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryAxial, "Testing axial plane"); /* end axial plane */ /* sagittal plane */ mitk::PlaneGeometry::Pointer geometrySagittal = mitk::PlaneGeometry::New(); geometrySagittal->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); geometrySagittal->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagittal->GetOrigin(); normal = geometrySagittal->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometrySagittal->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagittal, "Testing sagittal plane"); /* sagittal plane */ /* sagittal shifted plane */ mitk::PlaneGeometry::Pointer geometrySagittalShifted = mitk::PlaneGeometry::New(); geometrySagittalShifted->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, (sphereCenter - 14), true, false); geometrySagittalShifted->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagittalShifted->GetOrigin(); normal = geometrySagittalShifted->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometrySagittalShifted->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagittalShifted, "Testing sagittal plane shifted"); /* end sagittal shifted plane */ /* coronal plane */ mitk::PlaneGeometry::Pointer geometryCoronal = mitk::PlaneGeometry::New(); geometryCoronal->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Coronal, sphereCenter, true, false); geometryCoronal->ChangeImageGeometryConsideringOriginOffset(true); origin = geometryCoronal->GetOrigin(); normal = geometryCoronal->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometryCoronal->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryCoronal, "Testing coronal plane"); /* end coronal plane */ /* oblique plane */ mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); obliquePlane->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); obliquePlane->ChangeImageGeometryConsideringOriginOffset(true); origin = obliquePlane->GetOrigin(); normal = obliquePlane->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // obliquePlane->SetOrigin(origin); mitk::Vector3D rotationVector; rotationVector[0] = 0.2; rotationVector[1] = 0.4; rotationVector[2] = 0.62; float degree = 37.0; mitk::RotationOperation *op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); obliquePlane->ExecuteOperation(op); delete op; mitkExtractSliceFilterTestClass::TestSlice(obliquePlane, "Testing oblique plane"); /* end oblique plane */ #ifdef SHOW_SLICE_IN_RENDER_WINDOW /*================ #BEGIN vtk render code ================*/ // set reslicer for renderwindow mitk::Image::Pointer pic = mitk::IOUtil::Load(filename); vtkSmartPointer slicer = vtkSmartPointer::New(); slicer->SetInput(pic->GetVtkImageData()); mitk::PlaneGeometry::Pointer obliquePl = mitk::PlaneGeometry::New(); obliquePl->InitializeStandardPlane( pic->GetGeometry(), mitk::PlaneGeometry::Sagittal, pic->GetGeometry()->GetCenter()[0], true, false); obliquePl->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin2 = obliquePl->GetOrigin(); mitk::Vector3D n; n = obliquePl->GetNormal(); n.Normalize(); origin2 += n * 0.5; // pixelspacing is 1, so half the spacing is 0.5 obliquePl->SetOrigin(origin2); mitk::Vector3D rotation; rotation[0] = 0.534307; rotation[1] = 0.000439605; rotation[2] = 0.423017; MITK_INFO << rotation; mitk::RotationOperation *operation = new mitk::RotationOperation(mitk::OpROTATE, obliquePl->GetCenter(), rotationVector, degree); obliquePl->ExecuteOperation(operation); delete operation; double origin[3]; origin[0] = obliquePl->GetOrigin()[0]; origin[1] = obliquePl->GetOrigin()[1]; origin[2] = obliquePl->GetOrigin()[2]; slicer->SetResliceAxesOrigin(origin); mitk::Vector3D right, bottom, normal; right = obliquePl->GetAxisVector(0); bottom = obliquePl->GetAxisVector(1); normal = obliquePl->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); double cosines[9]; mitk::vnl2vtk(right.GetVnlVector(), cosines); // x mitk::vnl2vtk(bottom.GetVnlVector(), cosines + 3); // y mitk::vnl2vtk(normal.GetVnlVector(), cosines + 6); // n slicer->SetResliceAxesDirectionCosines(cosines); slicer->SetOutputDimensionality(2); slicer->Update(); // set vtk renderwindow vtkSmartPointer vtkPlane = vtkSmartPointer::New(); vtkPlane->SetOrigin(0.0, 0.0, 0.0); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and sagittal) afterwards. vtkPlane->SetPoint1(1.0, 0.0, 0.0); // P1: (xMax, yMin, depth) vtkPlane->SetPoint2(0.0, 1.0, 0.0); // P2: (xMin, yMax, depth) // these are not the correct values for all slices, only a square plane by now vtkSmartPointer imageMapper = vtkSmartPointer::New(); imageMapper->SetInputConnection(vtkPlane->GetOutputPort()); vtkSmartPointer lookupTable = vtkSmartPointer::New(); // built a default lookuptable lookupTable->SetRampToLinear(); lookupTable->SetSaturationRange(0.0, 0.0); lookupTable->SetHueRange(0.0, 0.0); lookupTable->SetValueRange(0.0, 1.0); lookupTable->Build(); // map all black values to transparent lookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0); lookupTable->SetRange(-255.0, 255.0); // lookupTable->SetRange(-1022.0, 1184.0);//pic3D range vtkSmartPointer texture = vtkSmartPointer::New(); texture->SetInput(slicer->GetOutput()); texture->SetLookupTable(lookupTable); texture->SetMapColorScalarsThroughLookupTable(true); vtkSmartPointer imageActor = vtkSmartPointer::New(); imageActor->SetMapper(imageMapper); imageActor->SetTexture(texture); // Setup renderers vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->AddActor(imageActor); // Setup render window vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->AddRenderer(renderer); // Setup render window interactor vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); vtkSmartPointer style = vtkSmartPointer::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interaction renderWindowInteractor->SetRenderWindow(renderWindow); // renderer->AddViewProp(imageActor); renderWindow->Render(); renderWindowInteractor->Start(); // always end with this! /*================ #END vtk render code ================*/ #endif // SHOW_SLICE_IN_RENDER_WINDOW MITK_TEST_END() } diff --git a/Modules/Core/test/mitkGeometry3DEqualTest.cpp b/Modules/Core/test/mitkGeometry3DEqualTest.cpp index 345b7637c8..858bcc6967 100644 --- a/Modules/Core/test/mitkGeometry3DEqualTest.cpp +++ b/Modules/Core/test/mitkGeometry3DEqualTest.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 "mitkGeometry3D.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" class mitkGeometry3DEqualTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkGeometry3DEqualTestSuite); MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_DifferentImageGeometry_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Geometry3D::Pointer m_Geometry3D; mitk::Geometry3D::Pointer m_AnotherGeometry3D; public: /** -* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Geometry3D = mitk::Geometry3D::New(); m_Geometry3D->Initialize(); m_AnotherGeometry3D = m_Geometry3D->Clone(); } void tearDown() override { m_Geometry3D = nullptr; m_AnotherGeometry3D = nullptr; } void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(m_Geometry3D, m_AnotherGeometry3D, "A clone should be equal to its original."); } void Equal_DifferentOrigin_ReturnsFalse() { mitk::Point3D origin; origin[0] = 0.0; origin[1] = 0.0; origin[2] = 1.0 + 2 * mitk::eps; m_AnotherGeometry3D->SetOrigin(origin); MITK_ASSERT_NOT_EQUAL(m_Geometry3D, m_AnotherGeometry3D, "Origin was modified. Result should be false."); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { // Create another index to world transform and make it different somehow mitk::AffineTransform3D::Pointer differentIndexToWorldTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType differentMatrix; differentMatrix.SetIdentity(); differentMatrix(1, 1) = 2; differentIndexToWorldTransform->SetMatrix(differentMatrix); m_AnotherGeometry3D->SetIndexToWorldTransform(differentIndexToWorldTransform); MITK_ASSERT_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "IndexToWorldTransform was modified. Result should be false."); } void Equal_DifferentSpacing_ReturnsFalse() { mitk::Vector3D differentSpacing; differentSpacing[0] = 1.0; differentSpacing[1] = 1.0 + 2 * mitk::eps; differentSpacing[2] = 1.0; m_AnotherGeometry3D->SetSpacing(differentSpacing); MITK_ASSERT_NOT_EQUAL(m_Geometry3D, m_AnotherGeometry3D, "Spacing was modified. Result should be false."); } void Equal_DifferentImageGeometry_ReturnsFalse() { m_AnotherGeometry3D->SetImageGeometry(true); MITK_ASSERT_NOT_EQUAL( m_Geometry3D, m_AnotherGeometry3D, "One Geometry is image, the other is not. Result should be false."); } void Equal_DifferentBoundingBox_ReturnsFalse() { // create different bounds to make the comparison false mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; m_AnotherGeometry3D->SetBounds(bounds); MITK_ASSERT_NOT_EQUAL(m_Geometry3D, m_AnotherGeometry3D, "Bounds are different. Result should be false."); } }; MITK_TEST_SUITE_REGISTRATION(mitkGeometry3DEqual) diff --git a/Modules/Core/test/mitkGeometry3DTest.cpp b/Modules/Core/test/mitkGeometry3DTest.cpp index ae6f8592be..caa5fc4d1a 100644 --- a/Modules/Core/test/mitkGeometry3DTest.cpp +++ b/Modules/Core/test/mitkGeometry3DTest.cpp @@ -1,622 +1,622 @@ /*============================================================================ 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 "mitkGeometry3D.h" #include #include #include "mitkInteractionConst.h" #include "mitkRotationOperation.h" #include #include #include "mitkTestingMacros.h" #include #include bool testGetAxisVectorVariants(mitk::Geometry3D *geometry) { int direction; for (direction = 0; direction < 3; ++direction) { mitk::Vector3D frontToBack(0.); switch (direction) { case 0: frontToBack = geometry->GetCornerPoint(false, false, false) - geometry->GetCornerPoint(true, false, false); break; // 7-3 case 1: frontToBack = geometry->GetCornerPoint(false, false, false) - geometry->GetCornerPoint(false, true, false); break; // 7-5 case 2: frontToBack = geometry->GetCornerPoint(false, false, false) - geometry->GetCornerPoint(false, false, true); break; // 7-2 } std::cout << "Testing GetAxisVector(int) vs GetAxisVector(bool, bool, bool): "; if (mitk::Equal(geometry->GetAxisVector(direction), frontToBack) == false) { std::cout << "[FAILED]" << std::endl; return false; } std::cout << "[PASSED]" << std::endl; } return true; } bool testGetAxisVectorExtent(mitk::Geometry3D *geometry) { int direction; for (direction = 0; direction < 3; ++direction) { if (mitk::Equal(geometry->GetAxisVector(direction).GetNorm(), geometry->GetExtentInMM(direction)) == false) { std::cout << "[FAILED]" << std::endl; return false; } std::cout << "[PASSED]" << std::endl; } return true; } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(mitk::Geometry3D *geometry3d) { MITK_TEST_OUTPUT(<< "Testing consistency of index and world coordinate systems: "); mitk::Point3D origin = geometry3d->GetOrigin(); mitk::Point3D dummy; MITK_TEST_OUTPUT(<< " Testing index->world->index conversion consistency"); geometry3d->WorldToIndex(origin, dummy); geometry3d->IndexToWorld(dummy, dummy); MITK_TEST_CONDITION_REQUIRED(dummy == origin, ""); MITK_TEST_OUTPUT(<< " Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0)"); mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); mitk::Point3D originContinuousIndex; geometry3d->WorldToIndex(origin, originContinuousIndex); MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); MITK_TEST_OUTPUT(<< " Testing WorldToIndex(origin, itk::Index)==(0,0,0)"); itk::Index<3> itkindex; geometry3d->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT(<< " Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0)"); mitk::Vector3D halfSpacingStep = geometry3d->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; geometry3d->WorldToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT(<< " Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; geometry3d->WorldToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT(<< " Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); geometry3d->WorldToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); MITK_TEST_OUTPUT(<< " Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter: "); mitk::Point3D center = geometry3d->GetCenter(); mitk::Point3D centerContIndex; geometry3d->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = geometry3d->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); // itk assumes corner based geometry. If our geometry is center based (imageGoe == true), everything needs to be // shifted if (geometry3d->GetImageGeometry()) { centerBounds[0] -= 0.5; centerBounds[1] -= 0.5; centerBounds[2] -= 0.5; } MITK_TEST_CONDITION_REQUIRED(mitk::Equal(centerContIndex, centerBounds), ""); MITK_TEST_OUTPUT(<< " Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter): "); center = geometry3d->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; geometry3d->IndexToWorld(centerBounds, centerBoundsInWorldCoords); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(center, centerBoundsInWorldCoords), ""); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(mitk::Geometry3D *geometry3d) { MITK_TEST_OUTPUT(<< "Testing consistency of index and world coordinate systems for vectors: "); mitk::Vector3D xAxisMM = geometry3d->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Vector3D xAxisContinuousIndexDeprecated; mitk::Point3D p, pIndex, origin; origin = geometry3d->GetOrigin(); p[0] = xAxisMM[0]; p[1] = xAxisMM[1]; p[2] = xAxisMM[2]; geometry3d->WorldToIndex(p, pIndex); geometry3d->WorldToIndex(xAxisMM, xAxisContinuousIndexDeprecated); geometry3d->WorldToIndex(xAxisMM, xAxisContinuousIndex); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == pIndex[0], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == pIndex[1], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == pIndex[2], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == xAxisContinuousIndexDeprecated[0], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == xAxisContinuousIndexDeprecated[1], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == xAxisContinuousIndexDeprecated[2], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == pIndex[0], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == pIndex[1], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == pIndex[2], ""); geometry3d->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex); geometry3d->IndexToWorld(xAxisContinuousIndexDeprecated, xAxisContinuousIndexDeprecated); geometry3d->IndexToWorld(pIndex, p); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex == xAxisMM, ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == p[0], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == p[1], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == p[2], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated == xAxisMM, ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == p[0], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == p[1], ""); MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == p[2], ""); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(mitk::Geometry3D *geometry3d) { MITK_TEST_OUTPUT(<< "Testing consistency of index and world coordinate systems: "); // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; - // check for constistency + // check for consistency mitk::Point3D point; geometry3d->IndexToWorld(itkIndex2, point); geometry3d->WorldToIndex(point, itkIndex2b); MITK_TEST_CONDITION_REQUIRED(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1])), "Testing itk::index<2> for IndexToWorld/WorldToIndex consistency"); geometry3d->IndexToWorld(itkIndex3, point); geometry3d->WorldToIndex(point, itkIndex3b); MITK_TEST_CONDITION_REQUIRED( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2])), "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); geometry3d->IndexToWorld(itkIndex4, point); geometry3d->WorldToIndex(point, itkIndex4b); MITK_TEST_CONDITION_REQUIRED(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0)), "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); geometry3d->IndexToWorld(mitkIndex, point); geometry3d->WorldToIndex(point, mitkIndexb); MITK_TEST_CONDITION_REQUIRED( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2])), "Testing mitk::Index for IndexToWorld/WorldToIndex consistency"); return EXIT_SUCCESS; } #include int testItkImageIsCenterBased() { MITK_TEST_OUTPUT(<< "Testing whether itk::Image coordinates are center-based."); typedef itk::Image ItkIntImage3D; ItkIntImage3D::Pointer itkintimage = ItkIntImage3D::New(); ItkIntImage3D::SizeType size; size.Fill(10); mitk::Point3D origin; mitk::FillVector3D(origin, 2, 3, 7); itkintimage->Initialize(); itkintimage->SetRegions(size); itkintimage->SetOrigin(origin); std::cout << "[PASSED]" << std::endl; MITK_TEST_OUTPUT(<< " Testing itk::Image::TransformPhysicalPointToContinuousIndex(origin)==(0,0,0)"); mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); itk::ContinuousIndex originContinuousIndex; itkintimage->TransformPhysicalPointToContinuousIndex(origin, originContinuousIndex); MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); MITK_TEST_OUTPUT(<< " Testing itk::Image::TransformPhysicalPointToIndex(origin)==(0,0,0)"); itk::Index<3> itkindex; itkintimage->TransformPhysicalPointToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT(<< " Testing itk::Image::TransformPhysicalPointToIndex(origin-0.5*spacing)==(0,0,0)"); mitk::Vector3D halfSpacingStep = itkintimage->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; itkintimage->TransformPhysicalPointToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; itkintimage->TransformPhysicalPointToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); MITK_TEST_OUTPUT(<< " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); itkintimage->TransformPhysicalPointToIndex(originOffCenter, itkindex); MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); MITK_TEST_OUTPUT(<< "=> Yes, itk::Image coordinates are center-based."); return EXIT_SUCCESS; } int testGeometry3D(bool imageGeometry) { // Build up a new image Geometry mitk::Geometry3D::Pointer geometry3d = mitk::Geometry3D::New(); float bounds[] = {-10.0, 17.0, -12.0, 188.0, 13.0, 211.0}; MITK_TEST_OUTPUT(<< "Initializing"); geometry3d->Initialize(); MITK_TEST_OUTPUT(<< "Setting ImageGeometry to " << imageGeometry); geometry3d->SetImageGeometry(imageGeometry); MITK_TEST_OUTPUT(<< "Setting bounds by SetFloatBounds(): " << bounds); geometry3d->SetFloatBounds(bounds); MITK_TEST_OUTPUT(<< "Testing AxisVectors"); if (testGetAxisVectorVariants(geometry3d) == false) return EXIT_FAILURE; if (testGetAxisVectorExtent(geometry3d) == false) return EXIT_FAILURE; MITK_TEST_OUTPUT(<< "Creating an AffineTransform3D transform"); mitk::AffineTransform3D::MatrixType matrix; matrix.SetIdentity(); matrix(1, 1) = 2; mitk::AffineTransform3D::Pointer transform; transform = mitk::AffineTransform3D::New(); transform->SetMatrix(matrix); MITK_TEST_OUTPUT(<< "Testing a SetIndexToWorldTransform"); geometry3d->SetIndexToWorldTransform(transform); MITK_TEST_OUTPUT(<< "Testing correctness of value returned by GetSpacing"); const mitk::Vector3D &spacing1 = geometry3d->GetSpacing(); mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 2; if (mitk::Equal(spacing1, expectedSpacing) == false) { MITK_TEST_OUTPUT(<< " [FAILED]"); return EXIT_FAILURE; } MITK_TEST_OUTPUT(<< "Testing a Compose(transform)"); geometry3d->Compose(transform); MITK_TEST_OUTPUT(<< "Testing correctness of value returned by GetSpacing"); const mitk::Vector3D &spacing2 = geometry3d->GetSpacing(); expectedSpacing[1] = 4; if (mitk::Equal(spacing2, expectedSpacing) == false) { MITK_TEST_OUTPUT(<< " [FAILED]"); return EXIT_FAILURE; } MITK_TEST_OUTPUT(<< "Testing correctness of SetSpacing"); mitk::Vector3D newspacing; mitk::FillVector3D(newspacing, 1.5, 2.5, 3.5); geometry3d->SetSpacing(newspacing); const mitk::Vector3D &spacing3 = geometry3d->GetSpacing(); if (mitk::Equal(spacing3, newspacing) == false) { MITK_TEST_OUTPUT(<< " [FAILED]"); return EXIT_FAILURE; } - // Seperate Test function for Index and World consistency + // Separate Test function for Index and World consistency testIndexAndWorldConsistency(geometry3d); testIndexAndWorldConsistencyForVectors(geometry3d); testIndexAndWorldConsistencyForIndex(geometry3d); MITK_TEST_OUTPUT(<< "Testing a rotation of the geometry"); double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D(rotationVector, 1, 0, 0); mitk::Point3D center = geometry3d->GetCenter(); auto op = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle); geometry3d->ExecuteOperation(op); MITK_TEST_OUTPUT(<< "Testing mitk::GetRotation() and success of rotation"); mitk::Matrix3D rotation; mitk::GetRotation(geometry3d, rotation); mitk::Vector3D voxelStep = rotation * newspacing; mitk::Vector3D voxelStepIndex; geometry3d->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex), ""); delete op; std::cout << "[PASSED]" << std::endl; MITK_TEST_OUTPUT(<< "Testing that ImageGeometry is still " << imageGeometry); MITK_TEST_CONDITION_REQUIRED(geometry3d->GetImageGeometry() == imageGeometry, ""); // Test if the translate function moves the origin correctly. mitk::Point3D oldOrigin = geometry3d->GetOrigin(); // use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); // compute ground truth mitk::Point3D tmpResult = geometry3d->GetOrigin() + translationVector; geometry3d->Translate(translationVector); MITK_TEST_CONDITION(mitk::Equal(geometry3d->GetOrigin(), tmpResult), "Testing if origin was translated."); translationVector *= -1; // vice versa geometry3d->Translate(translationVector); MITK_TEST_CONDITION(mitk::Equal(geometry3d->GetOrigin(), oldOrigin), "Testing if the translation could be done vice versa."); return EXIT_SUCCESS; } int testGeometryAfterCasting() { // Epsilon. Allowed difference for rotationvalue float eps = 0.0001; // Cast ITK and MITK images and see if geometry stays typedef itk::Image Image2DType; typedef itk::Image Image3DType; // Create 3D ITK Image from Scratch, cast to 3D MITK image, compare Geometries Image3DType::Pointer image3DItk = Image3DType::New(); Image3DType::RegionType myRegion; Image3DType::SizeType mySize; Image3DType::IndexType myIndex; Image3DType::SpacingType mySpacing; Image3DType::DirectionType myDirection, rotMatrixX, rotMatrixY, rotMatrixZ; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 2.9; myIndex[0] = -15; myIndex[1] = 15; myIndex[2] = 12; mySize[0] = 10; mySize[1] = 2; mySize[2] = 5; myRegion.SetSize(mySize); myRegion.SetIndex(myIndex); image3DItk->SetSpacing(mySpacing); image3DItk->SetRegions(myRegion); image3DItk->Allocate(); image3DItk->FillBuffer(0); myDirection.SetIdentity(); rotMatrixX.SetIdentity(); rotMatrixY.SetIdentity(); rotMatrixZ.SetIdentity(); mitk::Image::Pointer mitkImage; - // direction [row] [coloum] + // direction [row] [column] MITK_TEST_OUTPUT(<< "Casting a rotated 3D ITK Image to a MITK Image and check if Geometry is still same"); for (double rotX = 0; rotX < itk::Math::pi * 2; rotX += itk::Math::pi * 0.4) { // Set Rotation X rotMatrixX[1][1] = cos(rotX); rotMatrixX[1][2] = -sin(rotX); rotMatrixX[2][1] = sin(rotX); rotMatrixX[2][2] = cos(rotX); for (double rotY = 0; rotY < itk::Math::pi * 2; rotY += itk::Math::pi * 0.3) { // Set Rotation Y rotMatrixY[0][0] = cos(rotY); rotMatrixY[0][2] = sin(rotY); rotMatrixY[2][0] = -sin(rotY); rotMatrixY[2][2] = cos(rotY); for (double rotZ = 0; rotZ < itk::Math::pi * 2; rotZ += itk::Math::pi * 0.2) { // Set Rotation Z rotMatrixZ[0][0] = cos(rotZ); rotMatrixZ[0][1] = -sin(rotZ); rotMatrixZ[1][0] = sin(rotZ); rotMatrixZ[1][1] = cos(rotZ); // Multiply matrizes myDirection = myDirection * rotMatrixX * rotMatrixY * rotMatrixZ; image3DItk->SetDirection(myDirection); mitk::CastToMitkImage(image3DItk, mitkImage); const mitk::AffineTransform3D::MatrixType &matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; double itkValue = myDirection[row][col]; double diff = mitkValue - itkValue; // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! { std::cout << "Had a difference of : " << diff; std::cout << "Error: Casting altered Geometry!"; std::cout << "ITK Matrix:\n" << myDirection; std::cout << "Mitk Matrix (With Spacing):\n" << matrix; std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); MITK_TEST_CONDITION_REQUIRED(false == true, ""); return false; } } } } } } // Create 2D ITK Image from Scratch, cast to 2D MITK image, compare Geometries Image2DType::Pointer image2DItk = Image2DType::New(); Image2DType::RegionType myRegion2D; Image2DType::SizeType mySize2D; Image2DType::IndexType myIndex2D; Image2DType::SpacingType mySpacing2D; Image2DType::DirectionType myDirection2D, rotMatrix; mySpacing2D[0] = 31; mySpacing2D[1] = 0.1; myIndex2D[0] = -15; myIndex2D[1] = 15; mySize2D[0] = 10; mySize2D[1] = 2; myRegion2D.SetSize(mySize2D); myRegion2D.SetIndex(myIndex2D); image2DItk->SetSpacing(mySpacing2D); image2DItk->SetRegions(myRegion2D); image2DItk->Allocate(); image2DItk->FillBuffer(0); myDirection2D.SetIdentity(); rotMatrix.SetIdentity(); - // direction [row] [coloum] + // direction [row] [column] MITK_TEST_OUTPUT(<< "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same"); for (double rotTheta = 0; rotTheta < itk::Math::pi * 2; rotTheta += itk::Math::pi * 0.2) { // Set Rotation rotMatrix[0][0] = cos(rotTheta); rotMatrix[0][1] = -sin(rotTheta); rotMatrix[1][0] = sin(rotTheta); rotMatrix[1][1] = cos(rotTheta); // Multiply matrizes myDirection2D = myDirection2D * rotMatrix; image2DItk->SetDirection(myDirection2D); mitk::CastToMitkImage(image2DItk, mitkImage); const mitk::AffineTransform3D::MatrixType &matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); // Compare MITK and ITK matrix for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; if ((row == 2) && (col == row)) { if (mitkValue != 1) { MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 " "MUST be 0 and value for 2|2 must be 1"); return false; } } else if ((row == 2) || (col == 2)) { if (mitkValue != 0) { MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 " "MUST be 0 and value for 2|2 must be 1"); return false; } } else { double itkValue = myDirection2D[row][col]; double diff = mitkValue - itkValue; // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! { std::cout << "Had a difference of : " << diff; std::cout << "Error: Casting altered Geometry!"; std::cout << "ITK Matrix:\n" << myDirection2D; std::cout << "Mitk Matrix (With Spacing):\n" << matrix; std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); MITK_TEST_CONDITION_REQUIRED(false == true, ""); return false; } } } } } // THIS WAS TESTED: // 2D ITK -> 2D MITK, // 3D ITK -> 3D MITK, // Still need to test: 2D MITK Image with ADDITIONAL INFORMATION IN MATRIX -> 2D ITK // 1. Possibility: 3x3 MITK matrix can be converted without loss into 2x2 ITK matrix // 2. Possibility: 3x3 MITK matrix can only be converted with loss into 2x2 ITK matrix // .. before implementing this, we wait for further development in geometry classes (e.g. Geoemtry3D::SetRotation(..)) return EXIT_SUCCESS; } int mitkGeometry3DTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN(mitkGeometry3DTest); int result; MITK_TEST_CONDITION_REQUIRED((result = testItkImageIsCenterBased()) == EXIT_SUCCESS, ""); MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = false"); MITK_TEST_CONDITION_REQUIRED((result = testGeometry3D(false)) == EXIT_SUCCESS, ""); MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = true"); MITK_TEST_CONDITION_REQUIRED((result = testGeometry3D(true)) == EXIT_SUCCESS, ""); MITK_TEST_OUTPUT(<< "Running test to see if Casting MITK to ITK and the other way around destroys geometry"); MITK_TEST_CONDITION_REQUIRED((result = testGeometryAfterCasting()) == EXIT_SUCCESS, ""); MITK_TEST_END(); return EXIT_SUCCESS; } diff --git a/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp b/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp index af4e83d959..a5388d0fd1 100644 --- a/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp +++ b/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp @@ -1,120 +1,120 @@ /*============================================================================ 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 "mitkITKImageImport.h" #include "mitkTestingMacros.h" #include "mitkImagePixelReadAccessor.h" #include #include "mitkImageAccessByItk.h" /** * An ITK-based filter for thresholding. * * The filter represents the typical usage of ITK-like filters inside MITK. It is to be called for an mitk::Image * by using the AccessByItk macro. The filter executes the binary threshold filter and imports the result into the * output by using the ImportItkImage method. * * @param output mitk::Image to hold the result of the filter * @param th[] two double values to set the lower/upper threshold */ //! [ItkThresholdFilter] template static void ItkThresholdFilter(const itk::Image *image, mitk::Image::Pointer &output, const double th[]) { typedef itk::Image InputImageType; typedef itk::ThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer thresholder = ThresholdFilterType::New(); thresholder->SetInput(image); thresholder->ThresholdOutside(th[0], th[1]); thresholder->Update(); try { output = mitk::GrabItkImageMemory(thresholder->GetOutput()); } catch ( const itk::ExceptionObject & ) { MITK_TEST_FAILED_MSG(<< "Thresholding computation failed"); } } //! [ItkThresholdFilter] /** * Creates an mitk::Image, executes the binary threshold filter through AccessByItk and * checks whether the image data was correctly imported back to an mitk::Image. */ template bool Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue() { // data for 3x3x3 image const unsigned int dimensions[3] = {3, 3, 3}; auto image_data = new TPixel[27]; // ground truth for result check auto ground_truth = new TPixel[27]; double threshold[2] = {90.0, 180.0}; // fill image for (unsigned int i = 0; i < 27; i++) { image_data[i] = static_cast(i * 10); ground_truth[i] = 0; if (image_data[i] >= threshold[0] && image_data[i] <= threshold[1]) ground_truth[i] = static_cast(i * 10); } mitk::Image::Pointer input = mitk::Image::New(); input->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); input->SetImportVolume(image_data); //! [OutOfScopeCall] mitk::Image::Pointer output = mitk::Image::New(); AccessByItk_2(input, ItkThresholdFilter, output, threshold); //! [OutOfScopeCall] mitk::ImagePixelReadAccessor readAccessor(output); const TPixel *output_data = readAccessor.GetData(); bool equal = true; for (unsigned int i = 0; i < 27; i++) { equal &= (ground_truth[i] == output_data[i]); if (!equal) { MITK_INFO << " :: At position " << i << " : " << ground_truth[i] << " ? " << output_data[i] << "\n"; break; } } MITK_TEST_CONDITION(equal, " Imported output data equals the ground truth"); return equal; } int mitkGrabItkImageMemoryTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkGrabItkImageMemoryTest") Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import successful on 3D short"); - Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import succesfull on float"); - Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import succesfull on uchar"); - Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import succesfull on int"); + Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import successful on float"); + Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import successful on uchar"); + Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import successful on int"); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkImageEqualTest.cpp b/Modules/Core/test/mitkImageEqualTest.cpp index 5c0c412ec6..97f351627b 100644 --- a/Modules/Core/test/mitkImageEqualTest.cpp +++ b/Modules/Core/test/mitkImageEqualTest.cpp @@ -1,121 +1,121 @@ /*============================================================================ 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 "mitkImage.h" #include "mitkImageGenerator.h" #include "mitkImageSliceSelector.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" class mitkImageEqualTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageEqualTestSuite); MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentImageGeometry_ReturnsFalse); MITK_TEST(Equal_DifferentPixelTypes_ReturnsFalse); MITK_TEST(Equal_DifferentDimensions_ReturnsFalse); MITK_TEST(Equal_DifferentDimensionalities_ReturnsFalse); MITK_TEST(Equal_DifferentPixelValues_ReturnsFalse); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ mitk::Image::Pointer m_Image; mitk::Image::Pointer m_AnotherImage; public: /** -* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { // generate a gradient test image m_Image = mitk::ImageGenerator::GenerateGradientImage(3u, 3u, 1u); m_AnotherImage = m_Image->Clone(); } void tearDown() override { m_Image = nullptr; m_AnotherImage = nullptr; } void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(m_Image, m_Image->Clone(), "A clone should be equal to its original."); } void Equal_DifferentImageGeometry_ReturnsFalse() { mitk::Point3D origin; origin[0] = 0.0; origin[1] = 0.0; origin[2] = mitk::eps * 1.01; m_AnotherImage->GetGeometry()->SetOrigin(origin); MITK_ASSERT_NOT_EQUAL(m_Image, m_AnotherImage, "One origin was modified. Result should be false."); } void Equal_DifferentPixelTypes_ReturnsFalse() { m_AnotherImage = mitk::ImageGenerator::GenerateGradientImage(3u, 3u, 1u); MITK_ASSERT_NOT_EQUAL( m_Image, m_AnotherImage, "One pixel type is float, the other unsigned char. Result should be false."); } void Equal_DifferentDimensions_ReturnsFalse() { m_AnotherImage = mitk::ImageGenerator::GenerateGradientImage(5u, 7u, 3u); MITK_ASSERT_NOT_EQUAL( m_Image, m_AnotherImage, "Dimensions of first image are: (3, 3, 1). Dimensions of second image are: (5, 7, 3). Result should be false."); } void Equal_DifferentDimensionalities_ReturnsFalse() { // Select the first slice of a 2D image and compare it to the 3D original mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(m_Image); sliceSelector->SetSliceNr(0); sliceSelector->Update(); m_AnotherImage = sliceSelector->GetOutput(); MITK_ASSERT_NOT_EQUAL(m_Image, m_AnotherImage, "First image is 3D. Second image is 2D. Result should be false."); } void Equal_DifferentPixelValues_ReturnsFalse() { // todo: Replace the random images via simpler images with fixed values. m_Image = mitk::ImageGenerator::GenerateRandomImage(3u, 3u); m_AnotherImage = mitk::ImageGenerator::GenerateRandomImage(3u, 3u); MITK_ASSERT_NOT_EQUAL(m_Image, m_AnotherImage, "We compare two random images. Result should be false."); } void Equal_EpsilonDifference_ReturnsTrue() { m_Image = mitk::ImageGenerator::GenerateRandomImage(10, 10); m_AnotherImage = m_Image->Clone(); CPPUNIT_ASSERT_MESSAGE("Epsilon = 0.0 --> double images should not be regarded as equal", !mitk::Equal(*m_Image, *m_AnotherImage, 0, true)); CPPUNIT_ASSERT_MESSAGE("Epsilon = 0.001 --> double images should be regarded as equal", mitk::Equal(*m_Image, *m_AnotherImage, 0.001, true)); } }; MITK_TEST_SUITE_REGISTRATION(mitkImageEqual) diff --git a/Modules/Core/test/mitkImageTest.cpp b/Modules/Core/test/mitkImageTest.cpp index 18abf3c780..8dd349ecbf 100644 --- a/Modules/Core/test/mitkImageTest.cpp +++ b/Modules/Core/test/mitkImageTest.cpp @@ -1,538 +1,538 @@ /*============================================================================ 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 includes #include "mitkException.h" #include "mitkIOUtil.h" #include "mitkImageGenerator.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include #include #include #include #include #include "mitkImageSliceSelector.h" // itk includes #include #include // stl includes #include // vtk includes #include // Checks if reference count is correct after using GetVtkImageData() bool ImageVtkDataReferenceCheck(const char *fname) { const std::string filename = std::string(fname); try { mitk::Image::Pointer image = mitk::IOUtil::Load(filename); MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Non-nullptr image") vtkImageData *vtk = image->GetVtkImageData(); if (vtk == nullptr) return false; } catch (...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return false; } return true; } template void TestRandomPixelAccess(const mitk::PixelType /*ptype*/, mitk::Image::Pointer image, mitk::Point3D &point, mitk::ScalarType &value) { // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " " << image->GetGeometry()->GetOrigin()[1] << " " << image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " " << yMax[1] << " " << zMax[2] << ""; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize(std::rand()); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate(image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate(image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate(image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " " << point[1] << " " << point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); // test accessing PixelValue with coordinate leading to a negative index const mitk::Point3D geom_origin = image->GetGeometry()->GetOrigin(); const mitk::Point3D geom_center = image->GetGeometry()->GetCenter(); // shift position from origin outside of the image ( in the opposite direction to [center-origin] vector which points // in the inside) mitk::Point3D position = geom_origin + (geom_origin - geom_center); MITK_INFO << "Testing access outside of the image"; unsigned int dim = image->GetDimension(); if (dim == 3 || dim == 4) { mitk::ImagePixelReadAccessor imAccess3(image, image->GetVolumeData(0)); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if (point[0] < 0 || point[1] < 0 || point[2] < 0) { MITK_WARN << "Given position (" << point << ") is out of image range, returning 0."; } else { value = static_cast(imAccess3.GetPixelByWorldCoordinates(point)); MITK_TEST_CONDITION((value >= imageMin && value <= imageMax), "Value returned is between max/min"); } itk::Index<3> itkIndex; image->GetGeometry()->WorldToIndex(position, itkIndex); MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception); imAccess3.GetPixelByIndexSafe(itkIndex); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception); } MITK_INFO << imageMin << " " << imageMax << " " << value << ""; } class mitkImageTestClass { public: void SetClonedGeometry_None_ClonedEqualInput() { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(100, 100, 100, 1, 0.2, 0.3, 0.4); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); // InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->ChangeImageGeometryConsideringOriginOffset(true); image->SetClonedGeometry(planegeometry); mitk::BaseGeometry::Pointer imageGeometry = image->GetGeometry(); MITK_ASSERT_EQUAL(imageGeometry, planegeometry, "Matrix elements of cloned matrix equal original matrix"); } }; int mitkImageTest(int argc, char *argv[]) { MITK_TEST_BEGIN(mitkImageTest); mitkImageTestClass tester; tester.SetClonedGeometry_None_ClonedEqualInput(); // Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[] = {100, 100, 20}; MITK_TEST_CONDITION_REQUIRED(imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize(pt, 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p = nullptr; int *p2 = nullptr; try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int *)imgMemAcc.GetData(); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION(p != nullptr, "GetData() returned not-nullptr pointer."); // filling image const unsigned int size = dim[0] * dim[1] * dim[2]; for (unsigned int i = 0; i < size; ++i, ++p) *p = (signed int)i; // Getting it again and compare with filled values: try { mitk::ImageReadAccessor imgMemAcc(imgMem); p2 = (int *)imgMemAcc.GetData(); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION(p2 != nullptr, "GetData() returned not-nullptr pointer."); bool isEqual = true; for (unsigned int i = 0; i < size; ++i, ++p2) { if (*p2 != (signed int)i) { isEqual = false; } } MITK_TEST_CONDITION(isEqual, "The values previously set as data are correct [pixelwise comparison]."); // Testing GetSliceData() and compare with filled values: try { mitk::ImageReadAccessor imgMemAcc(imgMem, imgMem->GetSliceData(dim[2] / 2)); p2 = (int *)imgMemAcc.GetData(); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED(p2 != nullptr, "Valid slice data returned"); unsigned int xy_size = dim[0] * dim[1]; unsigned int start_mid_slice = (dim[2] / 2) * xy_size; isEqual = true; for (unsigned int i = 0; i < xy_size; ++i, ++p2) { if (*p2 != (signed int)(i + start_mid_slice)) { isEqual = false; } } MITK_TEST_CONDITION(isEqual, "The SliceData are correct [pixelwise comparison]. "); imgMem = mitk::Image::New(); // testing re-initialization of test image mitk::PixelType pType = mitk::MakePixelType(); imgMem->Initialize(pType, 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension() == 3, "Testing initialization parameter dimension!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pType, "Testing initialization parameter pixeltype!"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetDimension(0) == dim[0] && imgMem->GetDimension(1) == dim[1] && imgMem->GetDimension(2) == dim[2], "Testing initialization of dimensions!"); MITK_TEST_CONDITION(imgMem->IsInitialized(), "Image is initialized."); // Setting volume again: try { mitk::ImageReadAccessor imgMemAcc(imgMem); imgMem->SetVolume(imgMemAcc.GetData()); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); // InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->SetImageGeometry(true); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with // PlaneGeometry and GetData(): "; imgMem->Initialize(mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int *)imgMemAcc.GetData(); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED(p != nullptr, "GetData() returned valid pointer."); // Testing Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry) and GetData(): "; imgMem->Initialize(mitk::MakePixelType(), 40, *planegeometry); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int *)imgMemAcc.GetData(); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED(p != nullptr, "GetData() returned valid pointer."); //----------------- // testing origin information and methods MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetGeometry()->GetOrigin(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetPlaneGeometry(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetPlaneGeometry(0)->GetOrigin(): "); //----------------- // testing spacing information and methodsunsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); try { mitk::ImageReadAccessor imgMemAcc(imgMem); vecImg->Initialize(imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 0, mitk::Image::CopyMemory); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 1, mitk::Image::CopyMemory); mitk::ImageReadAccessor vecImgAcc(vecImg); mitk::ImageReadAccessor vecImgAcc0(vecImg, vecImg->GetChannelData(0)); mitk::ImageReadAccessor vecImgAcc1(vecImg, vecImg->GetChannelData(1)); MITK_TEST_CONDITION_REQUIRED(vecImgAcc0.GetData() != nullptr && vecImgAcc1.GetData() != nullptr, "Testing set and return of channel data!"); MITK_TEST_CONDITION_REQUIRED(vecImg->IsValidSlice(0, 0, 1), ""); MITK_TEST_OUTPUT(<< " Testing whether CopyMemory worked"); MITK_TEST_CONDITION_REQUIRED(imgMemAcc.GetData() != vecImgAcc.GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = nullptr; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull(), "testing destruction!"); } catch ( const mitk::Exception &e ) { MITK_ERROR << e.what(); } //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData *vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions(2, 3, 4); double vtkorigin[] = {-350, -358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->AllocateScalars(VTK_SHORT, 1); std::cout << "[PASSED]" << std::endl; MITK_TEST_OUTPUT(<< " Testing mitk::Image::Initialize(vtkImageData*, ...)"); mitk::Image::Pointer mitkByVtkImage = mitk::Image::New(); mitkByVtkImage->Initialize(vtkimage); MITK_TEST_CONDITION_REQUIRED(mitkByVtkImage->IsInitialized(), ""); vtkimage->Delete(); MITK_TEST_OUTPUT(<< " Testing whether spacing has been correctly initialized from vtkImageData"); mitk::Vector3D spacing2 = mitkByVtkImage->GetGeometry()->GetSpacing(); mitk::Vector3D vtkspacingAsMitkVector; mitk::vtk2itk(vtkspacing, vtkspacingAsMitkVector); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(spacing2, vtkspacingAsMitkVector), ""); MITK_TEST_OUTPUT( << " Testing whether GetSlicedGeometry(0)->GetOrigin() has been correctly initialized from vtkImageData"); mitk::Point3D origin2 = mitkByVtkImage->GetSlicedGeometry(0)->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2, vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2, vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing if vtkOrigin is (0, 0, 0). This behaviour is due to historical development of MITK. " - "Aslo see bug 5050!"); + "Also see bug 5050!"); vtkImageData *vtkImage = imgMem->GetVtkImageData(); auto vtkOrigin = vtkImage->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(vtkOrigin[0], 0), "testing vtkOrigin[0] to be 0"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(vtkOrigin[1], 0), "testing vtkOrigin[1] to be 0"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(vtkOrigin[2], 0), "testing vtkOrigin[2] to be 0"); // testing access by index coordinates and by world coordinates MITK_TEST_CONDITION_REQUIRED(argc == 2, "Check if test image is accessible!"); const std::string filename = std::string(argv[1]); mitk::Image::Pointer image; try { image = mitk::IOUtil::Load(filename); MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Non-nullptr image") } catch (...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return 0; } mitk::Point3D point; mitk::ScalarType value = -1.; mitkPixelTypeMultiplex3( TestRandomPixelAccess, image->GetImageDescriptor()->GetChannelTypeById(0), image, point, value) { // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); // After cloning an image the geometry of both images should be equal too MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); MITK_TEST_CONDITION_REQUIRED( mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), "Clone (testing transformation matrix)"); MITK_TEST_CONDITION_REQUIRED( mitk::MatrixEqualElementWise(cloneImage->GetTimeGeometry() ->GetGeometryForTimeStep(cloneImage->GetDimension(3) - 1) ->GetIndexToWorldTransform() ->GetMatrix(), cloneImage->GetTimeGeometry() ->GetGeometryForTimeStep(image->GetDimension(3) - 1) ->GetIndexToWorldTransform() ->GetMatrix()), "Clone(testing time sliced geometry)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } } // access via itk if (image->GetDimension() > 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if (image->GetDimension() == 3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; try { mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); } catch ( const std::exception &e ) { MITK_INFO << e.what(); } mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " " << itkPhysicalPoint[1] << " " << itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(point, backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " " << idx[1] << " " << idx[2] << ""; if (status && value != -1.) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN << "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) auto *threeDdim = new unsigned int[3]; threeDdim[0] = 100; threeDdim[1] = 200; threeDdim[2] = 1; mitk::Image::Pointer threeDImage = mitk::Image::New(); threeDImage->Initialize(mitk::MakeScalarPixelType(), 3, threeDdim); mitk::Image::Pointer cloneThreeDImage = threeDImage->Clone(); // check that the clone image has the same dimensionality as the source image MITK_TEST_CONDITION_REQUIRED(cloneThreeDImage->GetDimension() == 3, "Testing if the clone image initializes with 3D!"); MITK_TEST_CONDITION_REQUIRED(ImageVtkDataReferenceCheck(argv[1]), "Checking reference count of Image after using GetVtkImageData()"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImageToSurfaceFilterTest.cpp b/Modules/Core/test/mitkImageToSurfaceFilterTest.cpp index 7288503d2b..3e86268336 100644 --- a/Modules/Core/test/mitkImageToSurfaceFilterTest.cpp +++ b/Modules/Core/test/mitkImageToSurfaceFilterTest.cpp @@ -1,150 +1,150 @@ /*============================================================================ 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 "mitkException.h" #include "mitkImageToSurfaceFilter.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include bool CompareSurfacePointPositions(mitk::Surface::Pointer s1, mitk::Surface::Pointer s2) { vtkPoints *p1 = s1->GetVtkPolyData()->GetPoints(); vtkPoints *p2 = s2->GetVtkPolyData()->GetPoints(); if (p1->GetNumberOfPoints() != p2->GetNumberOfPoints()) return false; for (int i = 0; i < p1->GetNumberOfPoints(); ++i) { if (p1->GetPoint(i)[0] != p2->GetPoint(i)[0] || p1->GetPoint(i)[1] != p2->GetPoint(i)[1] || p1->GetPoint(i)[2] != p2->GetPoint(i)[2]) { return true; } } return false; } class mitkImageToSurfaceFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageToSurfaceFilterTestSuite); MITK_TEST(testImageToSurfaceFilterInitialization); MITK_TEST(testInput); MITK_TEST(testSurfaceGeneration); MITK_TEST(testDecimatePromeshDecimation); MITK_TEST(testQuadricDecimation); MITK_TEST(testSmoothingOfSurface); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Image::Pointer m_BallImage; public: /** - * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_BallImage = mitk::IOUtil::Load(GetTestDataFilePath("BallBinary30x30x30.nrrd")); } void tearDown() override {} void testImageToSurfaceFilterInitialization() { mitk::ImageToSurfaceFilter::Pointer testObject = mitk::ImageToSurfaceFilter::New(); CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testObject.IsNotNull()); // testing initialization of member variables! CPPUNIT_ASSERT_MESSAGE("Testing initialization of threshold member variable", testObject->GetThreshold() == 1.0f); CPPUNIT_ASSERT_MESSAGE("Testing initialization of smooth member variable", testObject->GetSmooth() == false); CPPUNIT_ASSERT_MESSAGE("Testing initialization of decimate member variable", testObject->GetDecimate() == mitk::ImageToSurfaceFilter::NoDecimation); CPPUNIT_ASSERT_MESSAGE("Testing initialization of target reduction member variable", testObject->GetTargetReduction() == 0.95f); } void testInput() { mitk::ImageToSurfaceFilter::Pointer testObject = mitk::ImageToSurfaceFilter::New(); testObject->SetInput(m_BallImage); CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testObject->GetInput() == m_BallImage); } void testSurfaceGeneration() { mitk::ImageToSurfaceFilter::Pointer testObject = mitk::ImageToSurfaceFilter::New(); testObject->SetInput(m_BallImage); testObject->Update(); mitk::Surface::Pointer resultSurface = nullptr; resultSurface = testObject->GetOutput(); CPPUNIT_ASSERT_MESSAGE("Testing surface generation!", testObject->GetOutput() != nullptr); } void testDecimatePromeshDecimation() { mitk::ImageToSurfaceFilter::Pointer testObject = mitk::ImageToSurfaceFilter::New(); testObject->SetInput(m_BallImage); testObject->Update(); mitk::Surface::Pointer resultSurface = nullptr; resultSurface = testObject->GetOutput(); mitk::Surface::Pointer testSurface1 = testObject->GetOutput()->Clone(); testObject->SetDecimate(mitk::ImageToSurfaceFilter::DecimatePro); testObject->SetTargetReduction(0.5f); testObject->Update(); mitk::Surface::Pointer testSurface2 = testObject->GetOutput()->Clone(); CPPUNIT_ASSERT_MESSAGE("Testing DecimatePro mesh decimation!", testSurface1->GetVtkPolyData()->GetPoints()->GetNumberOfPoints() > testSurface2->GetVtkPolyData()->GetPoints()->GetNumberOfPoints()); } void testQuadricDecimation() { mitk::ImageToSurfaceFilter::Pointer testObject = mitk::ImageToSurfaceFilter::New(); testObject->SetInput(m_BallImage); testObject->Update(); mitk::Surface::Pointer resultSurface = nullptr; resultSurface = testObject->GetOutput(); mitk::Surface::Pointer testSurface1 = testObject->GetOutput()->Clone(); testObject->SetDecimate(mitk::ImageToSurfaceFilter::QuadricDecimation); testObject->SetTargetReduction(0.5f); testObject->Update(); mitk::Surface::Pointer testSurface3 = testObject->GetOutput()->Clone(); CPPUNIT_ASSERT_MESSAGE("Testing QuadricDecimation mesh decimation!", testSurface1->GetVtkPolyData()->GetPoints()->GetNumberOfPoints() > testSurface3->GetVtkPolyData()->GetPoints()->GetNumberOfPoints()); } void testSmoothingOfSurface() { mitk::ImageToSurfaceFilter::Pointer testObject = mitk::ImageToSurfaceFilter::New(); testObject->SetInput(m_BallImage); testObject->Update(); mitk::Surface::Pointer resultSurface = nullptr; resultSurface = testObject->GetOutput(); mitk::Surface::Pointer testSurface1 = testObject->GetOutput()->Clone(); testObject->SetSmooth(true); testObject->SetDecimate(mitk::ImageToSurfaceFilter::NoDecimation); testObject->Update(); mitk::Surface::Pointer testSurface4 = testObject->GetOutput()->Clone(); CPPUNIT_ASSERT_MESSAGE("Testing smoothing of surface changes point data!", CompareSurfacePointPositions(testSurface1, testSurface4)); } }; MITK_TEST_SUITE_REGISTRATION(mitkImageToSurfaceFilter) diff --git a/Modules/Core/test/mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp b/Modules/Core/test/mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp index fe18d6cbde..2ac597cf87 100644 --- a/Modules/Core/test/mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp +++ b/Modules/Core/test/mitkImageVtkMapper2DResliceInterpolationPropertyTest.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. ============================================================================*/ // MITK #include "mitkDataNode.h" #include "mitkIOUtil.h" #include "mitkRenderingTestHelper.h" #include "mitkTestingMacros.h" #include // VTK #include #include int mitkImageVtkMapper2DResliceInterpolationPropertyTest(int argc, char *argv[]) { try { mitk::RenderingTestHelper openGlTest(640, 480); } catch (const mitk::TestNotRunException &e) { MITK_WARN << "Test not run: " << e.GetDescription(); return 77; } // load all arguments into a datastorage, take last argument as reference // setup a renderwindow of fixed size X*Y // render the datastorage // compare rendering to reference image // note: this test is supposed to test the reslice interpolation modes not the swiveling itself MITK_TEST_BEGIN("mitkImageVtkMapper2DResliceInterpolationPropertyTest") mitk::RenderingTestHelper renderingHelper(640, 480, argc, argv); - // get resliceInterpolation from comandline arg + // get resliceInterpolation from commandline arg int resliceInterpolation = atoi(argv[argc - 4]); // Set interpolation mode for rendering renderingHelper.SetImageProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(resliceInterpolation)); /*+++rotate plane+++*/ // center point for rotation mitk::Point3D centerPoint; centerPoint.Fill(0.0f); // vector for rotating the slice mitk::Vector3D rotationVector; rotationVector.SetElement(0, 0.2); rotationVector.SetElement(1, 0.3); rotationVector.SetElement(2, 0.5); // sets a swivel direction for the image // new version of setting the center point: mitk::Image::Pointer image = static_cast( renderingHelper.GetDataStorage()->GetNode(mitk::NodePredicateDataType::New("Image"))->GetData()); // get the center point of the image centerPoint = image->GetGeometry()->GetCenter(); - // rotate the image arround its own center + // rotate the image around its own center renderingHelper.ReorientSlices(centerPoint, rotationVector); // threshold for CompareRenderWindowAgainstReference double threshold = 0.35; // difference between interpolation modes is very small //### Usage of CompareRenderWindowAgainstReference: See docu of mitkRrenderingTestHelper MITK_TEST_CONDITION(renderingHelper.CompareRenderWindowAgainstReference(argc, argv, threshold) == true, "CompareRenderWindowAgainstReference test result positive?"); // use this to generate a reference screenshot or save the file: if (false) { renderingHelper.Render(); renderingHelper.SaveReferenceScreenShot("C:\\Users\\schroedt\\Pictures\\RenderingTestData\\Pic3dRefLinear.png"); } MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImageVtkMapper2DSwivelTest.cpp b/Modules/Core/test/mitkImageVtkMapper2DSwivelTest.cpp index 69ab654ca0..8f62328eab 100644 --- a/Modules/Core/test/mitkImageVtkMapper2DSwivelTest.cpp +++ b/Modules/Core/test/mitkImageVtkMapper2DSwivelTest.cpp @@ -1,72 +1,72 @@ /*============================================================================ 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 "mitkImage.h" #include "mitkRenderingTestHelper.h" #include "mitkTestingMacros.h" #include // VTK #include int mitkImageVtkMapper2DSwivelTest(int argc, char *argv[]) { try { mitk::RenderingTestHelper openGlTest(640, 480); } catch (const mitk::TestNotRunException &e) { MITK_WARN << "Test not run: " << e.GetDescription(); return 77; } // load all arguments into a datastorage, take last argument as reference // setup a renderwindow of fixed size X*Y // render the datastorage // compare rendering to reference image MITK_TEST_BEGIN("mitkImageVtkMapper2DSwivelTest") mitk::RenderingTestHelper renderingHelper(640, 480, argc, argv); // center point for rotation mitk::Point3D centerPoint; centerPoint.Fill(0.0f); // vector for rotating the slice mitk::Vector3D rotationVector; rotationVector.SetElement(0, 0.2); rotationVector.SetElement(1, 0.3); rotationVector.SetElement(2, 0.5); // sets a swivel direction for the image // new version of setting the center point: mitk::Image::Pointer image = static_cast( renderingHelper.GetDataStorage()->GetNode(mitk::NodePredicateDataType::New("Image"))->GetData()); // get the center point of the image centerPoint = image->GetGeometry()->GetCenter(); - // rotate the image arround its own center + // rotate the image around its own center renderingHelper.ReorientSlices(centerPoint, rotationVector); //### Usage of CompareRenderWindowAgainstReference: See docu of mitkRrenderingTestHelper MITK_TEST_CONDITION(renderingHelper.CompareRenderWindowAgainstReference(argc, argv) == true, "CompareRenderWindowAgainstReference test result positive?"); // use this to generate a reference screenshot or save the file: if (false) { renderingHelper.SaveReferenceScreenShot( "/media/hdd/thomasHdd/Pictures/RenderingTestData/pic3dSwivel640x480REF.png"); } MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImportItkImageTest.cpp b/Modules/Core/test/mitkImportItkImageTest.cpp index 1ce29b55b1..123f01f9f8 100644 --- a/Modules/Core/test/mitkImportItkImageTest.cpp +++ b/Modules/Core/test/mitkImportItkImageTest.cpp @@ -1,306 +1,306 @@ /*============================================================================ 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 "mitkITKImageImport.h" #include "mitkImageCast.h" #include "mitkTestingMacros.h" #include "mitkImagePixelReadAccessor.h" #include #include /** * Create a test image with random pixel values. The image size is determined by the input parameter. * * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageRandom(short int size) { typedef typename itk::Image ImageType; itk::Size regionSize; regionSize.Fill(size); typename itk::RandomImageSource::Pointer randomImageSource = itk::RandomImageSource::New(); randomImageSource->SetNumberOfWorkUnits(1); // to produce non-random results randomImageSource->SetSize(regionSize); randomImageSource->Update(); return randomImageSource->GetOutput(); } /** * Create a test vector image (with two components) with a single pixel value. The image size is determined by the input * parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::VectorImage::Pointer CreateTestVectorImageFixedValue( size_t size, const itk::VariableLengthVector &value) { typedef typename itk::VectorImage ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetVectorLength(value.GetNumberOfElements()); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Create a test image with a single pixel value. The image size is determined by the input parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageFixedValue(size_t size, TPixel value) { typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Compares the meta information of both given images for equality. */ template bool Assert_ImageMetaData_AreEqual(typename ImageType::Pointer itkImage, mitk::Image::Pointer mitkImage) { bool return_value = true; typename ImageType::RegionType itkRegion = itkImage->GetLargestPossibleRegion(); typename ImageType::SizeType itkImageSize = itkRegion.GetSize(); // check dimension for (unsigned int idx = 0; idx < mitkImage->GetDimension(); idx++) { return_value &= (itkImageSize[idx] == mitkImage->GetDimension(idx)); } MITK_TEST_CONDITION(return_value, " - Dimensions equal!") // check pixel type bool ptype_compare = (mitkImage->GetPixelType() == mitk::MakePixelType()); return_value &= ptype_compare; MITK_TEST_CONDITION(ptype_compare, " - Pixel types equal!") mitk::BaseGeometry *imageGeometry = mitkImage->GetGeometry(); const mitk::Point3D origin = imageGeometry->GetOrigin(); bool origin_compare = true; for (unsigned int idx = 0; idx < 3; idx++) { origin_compare &= (itkImage->GetOrigin()[idx] == origin[idx]); } return_value &= origin_compare; MITK_TEST_CONDITION(origin_compare, " - Origin equals!") return return_value; } /** * Generates a random itk image and imports it to mitk image through ImportItkImage and compares the values * voxel-wise afterwards */ template void Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: (Random Image, " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageRandom(5); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } /** * Generates an itk image with fixed pixel value and imports it to mitk image through ImportItkImage * and compares the values voxel-wise afterwards */ template void Assert_ItkImageImportSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageFixedValue(5, itk::NumericTraits::min()); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } void Assert_ItkVectorImageImportAndCast_ReturnsTrue() { typedef itk::VectorImage ImageType; ImageType::PixelType value; value.SetSize(2); value.SetElement(0, 1); value.SetElement(0, 2); ImageType::Pointer itkImage = CreateTestVectorImageFixedValue(5, value); mitk::Image::Pointer mitkImage = mitk::ImportItkImage(itkImage); mitk::PixelType pixelType = mitkImage->GetPixelType(); MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR, "Vector image pixel type") MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::IOComponentEnum::SHORT, "Vector image component type") mitk::Image::Pointer mitkImage2; mitk::CastToMitkImage(itkImage, mitkImage2); mitk::PixelType pixelType2 = mitkImage2->GetPixelType(); MITK_TEST_CONDITION(pixelType == pixelType2, "ImportItkImage and CastToMitkImage produce same pixel types") ImageType::Pointer itkImageOut; mitk::CastToItkImage(mitkImage, itkImageOut); MITK_TEST_CONDITION(pixelType == mitk::MakePixelType(2), "MITK pixel type equals ITK pixel type") typedef itk::VectorImage IntImageType; IntImageType::Pointer itkIntImageOut; mitk::CastToItkImage(mitkImage, itkIntImageOut); MITK_TEST_CONDITION(!(pixelType == mitk::MakePixelType(2)), "MITK pixel type != ITK pixel type") mitk::Image::Pointer mitkImage3; mitk::CastToMitkImage(itkImageOut, mitkImage3); MITK_ASSERT_EQUAL(mitkImage, mitkImage3, "Equality for vector images"); } int mitkImportItkImageTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkImportItkImageTest") - Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); - Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on float"); - Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on uchar"); - Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on int"); - - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); - - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); - Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); + Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import successful on 3D short"); + Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import successful on float"); + Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import successful on uchar"); + Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import successful on int"); + + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on 3D short"); + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on float"); + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on uchar"); + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on int"); + + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on 3D short"); + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on float"); + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on uchar"); + Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import successful on int"); Assert_ItkVectorImageImportAndCast_ReturnsTrue(); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkItkImageIOTest.cpp b/Modules/Core/test/mitkItkImageIOTest.cpp index a9d8d6d9eb..e35cd8d340 100644 --- a/Modules/Core/test/mitkItkImageIOTest.cpp +++ b/Modules/Core/test/mitkItkImageIOTest.cpp @@ -1,428 +1,428 @@ /*============================================================================ 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 "mitkException.h" #include #include #include #include "mitkIOUtil.h" #include #include "mitkITKImageImport.h" #include #include "itksys/SystemTools.hxx" #include #include #include #ifdef WIN32 #include "process.h" #else #include #endif class mitkItkImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkItkImageIOTestSuite); MITK_TEST(TestImageWriterJpg); MITK_TEST(TestImageWriterPng1); MITK_TEST(TestImageWriterPng2); MITK_TEST(TestImageWriterPng3); MITK_TEST(TestImageWriterSimple); MITK_TEST(TestWrite3DImageWithOnePlane); MITK_TEST(TestWrite3DImageWithTwoPlanes); MITK_TEST(TestWrite3DplusT_ArbitraryTG); MITK_TEST(TestWrite3DplusT_ProportionalTG); CPPUNIT_TEST_SUITE_END(); public: void setUp() override {} void tearDown() override {} void TestImageWriterJpg() { TestImageWriter("NrrdWritingTestImage.jpg"); } void TestImageWriterPng1() { TestImageWriter("Png2D-bw.png"); } void TestImageWriterPng2() { TestImageWriter("RenderingTestData/rgbImage.png"); } void TestImageWriterPng3() { TestImageWriter("RenderingTestData/rgbaImage.png"); } void TestWrite3DplusT_ArbitraryTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd"); } void TestWrite3DplusT_ProportionalTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_prop_time_geometry.nrrd"); } void TestImageWriterSimple() { // TODO } std::string AppendExtension(const std::string &filename, const char *extension) { std::string new_filename = filename; new_filename += extension; return new_filename; } bool CompareImageMetaData(mitk::Image::Pointer image, mitk::Image::Pointer reference, bool checkPixelType = true) { // switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed if (image->GetDimension() != reference->GetDimension()) { MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF(" << reference->GetDimension() << ")"; return false; } // pixel type if (checkPixelType && (image->GetPixelType() != reference->GetPixelType() && image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent())) { MITK_ERROR << "Pixeltype differs ( image=" << image->GetPixelType().GetPixelTypeAsString() << "[" << image->GetPixelType().GetBitsPerComponent() << "]" << " reference=" << reference->GetPixelType().GetPixelTypeAsString() << "[" << reference->GetPixelType().GetBitsPerComponent() << "]" << " )"; return false; } return true; } /* Test writing picture formats like *.bmp, *.png, *.tiff or *.jpg NOTE: Saving as picture format must ignore PixelType comparison - not all bits per components are supported (see specification of the format) */ void TestPictureWriting(mitk::Image *image, const std::string &filename, const std::string &extension) { const std::string fullFileName = AppendExtension(filename, extension.c_str()); mitk::Image::Pointer singleSliceImage = nullptr; if (image->GetDimension() == 3) { mitk::ExtractSliceFilter::Pointer extractFilter = mitk::ExtractSliceFilter::New(); extractFilter->SetInput(image); extractFilter->SetWorldGeometry(image->GetSlicedGeometry()->GetPlaneGeometry(0)); extractFilter->Update(); singleSliceImage = extractFilter->GetOutput(); // test 3D writing in format supporting only 2D mitk::IOUtil::Save(image, fullFileName); // test images unsigned int foundImagesCount = 0; - // if the image only contains one sinlge slice the itkImageSeriesWriter won't add a number like + // if the image only contains one single slice the itkImageSeriesWriter won't add a number like // filename.XX.extension if (image->GetDimension(2) == 1) { std::stringstream series_filenames; series_filenames << filename << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } else // test the whole slice stack { for (unsigned int i = 0; i < image->GetDimension(2); i++) { std::stringstream series_filenames; series_filenames << filename << "." << i + 1 << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } } MITK_TEST_CONDITION(foundImagesCount == image->GetDimension(2), "All 2D-Slices of a 3D image were stored correctly."); } else if (image->GetDimension() == 2) { singleSliceImage = image; } // test 2D writing if (singleSliceImage.IsNotNull()) { try { mitk::IOUtil::Save(singleSliceImage, fullFileName); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(fullFileName.c_str()); - MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was succesfully loaded again"); + MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was successfully loaded again"); MITK_TEST_CONDITION_REQUIRED( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component remove(fullFileName.c_str()); } catch (itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "Exception during file writing for ." << extension << ": " << e.what()); } } } /** * test for writing NRRDs */ void TestNRRDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd"); tmpStream.close(); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); - CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was succesfully loaded again", compareImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was successfully loaded again", compareImage.IsNotNull()); - /*It would make sence to check the images as well (see commented cppunit assert), + /*It would make sense to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE( "TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), mitk::eps, true)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); remove(tmpFilePath.c_str()); } catch (...) { std::remove(tmpFilePath.c_str()); CPPUNIT_FAIL("Exception during NRRD file writing"); } } /** * test for writing MHDs */ void TestMHDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mhd"); tmpStream.close(); std::string tmpFilePathWithoutExt = tmpFilePath.substr(0, tmpFilePath.size() - 4); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); - CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was succesfully loaded again! ", compareImage.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was successfully loaded again! ", compareImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE(".mhd file exists", itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".mhd").c_str())); CPPUNIT_ASSERT_MESSAGE(".raw or .zraw exists", itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".raw").c_str()) || itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".zraw").c_str())); - /*It would make sence to check the images as well (see commented cppunit assert), + /*It would make sense to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), 5e-4, true)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); // delete remove(tmpFilePath.c_str()); remove((tmpFilePathWithoutExt + ".raw").c_str()); remove((tmpFilePathWithoutExt + ".zraw").c_str()); } catch (...) { CPPUNIT_FAIL("Exception during.mhd file writing"); } } /** * test for "ImageWriter". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ void TestImageWriter(std::string sourcefile) { sourcefile = GetTestDataFilePath(sourcefile); // load image CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(sourcefile).c_str())); mitk::Image::Pointer image = nullptr; try { image = mitk::IOUtil::Load(sourcefile); } catch (...) { CPPUNIT_FAIL("Exception during file loading:"); } CPPUNIT_ASSERT_MESSAGE("loaded image not nullptr", image.IsNotNull()); // write ITK .mhd image (2D and 3D only) if (image->GetDimension() <= 3) { TestMHDWriting(image); } // testing more component image writing as nrrd files TestNRRDWriting(image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX"); tmpStream.close(); TestPictureWriting(image, tmpFilePath, ".png"); TestPictureWriting(image, tmpFilePath, ".jpg"); TestPictureWriting(image, tmpFilePath, ".tiff"); TestPictureWriting(image, tmpFilePath, ".bmp"); // always end with this! } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithOnePlane() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 1; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")); } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithTwoPlanes() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 2; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkItkImageIO) diff --git a/Modules/Core/test/mitkLevelWindowManagerTest.cpp b/Modules/Core/test/mitkLevelWindowManagerTest.cpp index 43cddaee66..588b2056cc 100644 --- a/Modules/Core/test/mitkLevelWindowManagerTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerTest.cpp @@ -1,439 +1,439 @@ /*============================================================================ 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 class mitkLevelWindowManagerTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLevelWindowManagerTestSuite); MITK_TEST(TestModes); MITK_TEST(TestSetLevelWindowProperty); MITK_TEST(TestVisibilityPropertyChanged); MITK_TEST(TestLayerPropertyChanged); MITK_TEST(TestImageRenderingModePropertyChanged); MITK_TEST(TestImageForLevelWindowPropertyChanged); MITK_TEST(TestSelectedPropertyChanged); MITK_TEST(TestRemoveDataNodes); MITK_TEST(TestCombinedPropertiesChanged); CPPUNIT_TEST_SUITE_END(); private: mitk::LevelWindowManager::Pointer m_LevelWindowManager; mitk::StandaloneDataStorage::Pointer m_DataManager; std::string m_ImagePath1; std::string m_ImagePath2; std::string m_ImagePath3; mitk::DataNode::Pointer m_DataNode1; mitk::DataNode::Pointer m_DataNode2; mitk::DataNode::Pointer m_DataNode3; mitk::Image::Pointer m_mitkMultiComponentImage; mitk::Image::Pointer m_mitkImageComponent1; mitk::Image::Pointer m_mitkImageComponent2; bool AssertImageForLevelWindowProperty(bool assert1, bool assert2, bool assert3) { bool imageForLevelWindowProperty1, imageForLevelWindowProperty2, imageForLevelWindowProperty3; m_DataNode1->GetBoolProperty("imageForLevelWindow", imageForLevelWindowProperty1); m_DataNode2->GetBoolProperty("imageForLevelWindow", imageForLevelWindowProperty2); m_DataNode3->GetBoolProperty("imageForLevelWindow", imageForLevelWindowProperty3); return (assert1 == imageForLevelWindowProperty1) && (assert2 == imageForLevelWindowProperty2) && (assert3 == imageForLevelWindowProperty3); } bool AssertLevelWindowProperty(bool assert1, bool assert2, bool assert3) { auto levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); auto levelWindowProperty2 = dynamic_cast(m_DataNode2->GetProperty("levelwindow")); auto levelWindowProperty3 = dynamic_cast(m_DataNode3->GetProperty("levelwindow")); // Check if the active level window property of the manager is equal to any of the level window properties of the nodes auto managerLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); return (assert1 == (managerLevelWindowProperty == levelWindowProperty1)) && (assert2 == (managerLevelWindowProperty == levelWindowProperty2)) && (assert3 == (managerLevelWindowProperty == levelWindowProperty3)); } public: void setUp() override { m_LevelWindowManager = mitk::LevelWindowManager::New(); m_DataManager = mitk::StandaloneDataStorage::New(); CPPUNIT_ASSERT_NO_THROW_MESSAGE("DataStorage could not be set for the new level window manager", m_LevelWindowManager->SetDataStorage(m_DataManager)); CPPUNIT_ASSERT_MESSAGE("DataStorage could not be retrieved from the new level window manager", m_DataManager == m_LevelWindowManager->GetDataStorage()); m_ImagePath1 = GetTestDataFilePath("Pic3D.nrrd"); m_ImagePath2 = GetTestDataFilePath("UltrasoundImages/4D_TEE_Data_MV.dcm"); m_ImagePath3 = GetTestDataFilePath("RenderingTestData/defaultWatermark.png"); // add multiple objects to the data storage => property observers will be created m_DataNode1 = mitk::IOUtil::Load(m_ImagePath1, *m_DataManager)->GetElement(0); m_DataNode2 = mitk::IOUtil::Load(m_ImagePath2, *m_DataManager)->GetElement(0); CPPUNIT_ASSERT_MESSAGE("Not two relevant nodes found in the data storage", m_LevelWindowManager->GetRelevantNodes()->size() == 2); CPPUNIT_ASSERT_MESSAGE("Not two observers created for the relevant nodes", m_LevelWindowManager->GetNumberOfObservers() == 2); m_DataNode1->SetIntProperty("layer", 1); m_DataNode2->SetIntProperty("layer", 2); bool isImageForLevelWindow1, isImageForLevelWindow2; m_DataNode1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); m_DataNode2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); CPPUNIT_ASSERT_MESSAGE("Initial \"imageForLevelWindow\" property not exclusively set for node 2", !isImageForLevelWindow1 && isImageForLevelWindow2); m_DataNode3 = mitk::IOUtil::Load(m_ImagePath3, *m_DataManager)->GetElement(0); CPPUNIT_ASSERT_MESSAGE("Not three relevant nodes found in the data storage", m_LevelWindowManager->GetRelevantNodes()->size() == 3); CPPUNIT_ASSERT_MESSAGE("Not three observers created for the relevant nodes", m_LevelWindowManager->GetNumberOfObservers() == 3); m_DataNode3->SetIntProperty("layer", 3); AssertImageForLevelWindowProperty(false, false, true); } void tearDown() override {} void TestModes() { CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode is not enabled per default", m_LevelWindowManager->IsAutoTopMost()); CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode is not disabled per default", !m_LevelWindowManager->IsSelectedImages()); m_LevelWindowManager->SetSelectedImages(true); CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode was not disabled on mode switch", !m_LevelWindowManager->IsAutoTopMost()); CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode was not enabled on mode switch", m_LevelWindowManager->IsSelectedImages()); m_LevelWindowManager->SetSelectedImages(false); CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode was not disabled on mode switch", !m_LevelWindowManager->IsAutoTopMost()); CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode was not disabled on mode switch", !m_LevelWindowManager->IsSelectedImages()); m_LevelWindowManager->SetSelectedImages(true); // to enable "SelectedImagesMode" m_LevelWindowManager->SetAutoTopMostImage(true); CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode was not enabled on mode switch", m_LevelWindowManager->IsAutoTopMost()); CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode was not disabled on mode switch", !m_LevelWindowManager->IsSelectedImages()); } void TestSetLevelWindowProperty() { m_LevelWindowManager->SetAutoTopMostImage(false); // Setting the level window property of the manager // will make the corresponding node be the "imageForLevelWindow" node. auto levelWindowProperty = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); levelWindowProperty = dynamic_cast(m_DataNode2->GetProperty("levelwindow")); m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); levelWindowProperty = dynamic_cast(m_DataNode3->GetProperty("levelwindow")); m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); levelWindowProperty = mitk::LevelWindowProperty::New(); //CPPUNIT_ASSERT_THROW_MESSAGE("Expected exception for an invalid level window property was not thrown", //m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty), mitk::Exception); } void TestVisibilityPropertyChanged() { // Hiding a node will make the "next" node be the "imageForLevelWindow" node, if // the hidden node was the "imageForLevelWindow" node before. "Next" is dependent on the node layer. m_DataNode3->SetVisibility(false); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode2->SetVisibility(false); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode1->SetVisibility(false); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); m_DataNode3->SetVisibility(true); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); } void TestLayerPropertyChanged() { m_DataNode3->SetIntProperty("layer", itk::NumericTraits::min()); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode2->SetIntProperty("layer", itk::NumericTraits::min()); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode1->SetIntProperty("layer", itk::NumericTraits::min()); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); m_DataNode3->SetIntProperty("layer", 1); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); } void TestImageRenderingModePropertyChanged() { // checking the default rendering mode auto renderingMode = dynamic_cast(m_DataNode3->GetProperty("Image Rendering.Mode")); CPPUNIT_ASSERT_MESSAGE("Initial \"Image Rendering.Mode\" property not set to \"RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR\"", mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == renderingMode->GetRenderingMode()); // Changing the "Image Rendering.Mode" of a node to either "LOOKUPTABLE_COLOR" or // "COLORTRANSFERFUNCTION_COLOR" will ignore this node for the level window m_DataNode3->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::LOOKUPTABLE_COLOR)); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode2->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR)); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode1->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::LOOKUPTABLE_COLOR)); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); m_DataNode3->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR)); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); } void TestImageForLevelWindowPropertyChanged() { m_LevelWindowManager->SetAutoTopMostImage(false); m_DataNode1->SetBoolProperty("imageForLevelWindow", true); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode2->SetBoolProperty("imageForLevelWindow", true); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode3->SetBoolProperty("imageForLevelWindow", true); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); // The top level node will alsways be used as a fall back node // if no specific mode is selected. m_DataNode3->SetBoolProperty("imageForLevelWindow", false); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); } void TestSelectedPropertyChanged() { // Selecting a node will make this node be the "imageForLevelWindow" node, if // the "SelectedImagesMode" is enabled (disabled per default). m_DataNode1->SetSelected(true); // selection mode not enabled - node3 stays the "imageForLevelWindow" node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); // This will immediately check for the selected node (node 1). m_LevelWindowManager->SetSelectedImages(true); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); // Second node will be selected (node 1 and node 2 selected). // It is not specified which data node will be used for the "imageForLevelWindow" / "levelwindow" property, // since the data storage access to the nodes is non-deterministic. // At least check for any valid level window property. m_DataNode2->SetSelected(true); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is null", m_LevelWindowManager->GetLevelWindowProperty()); // Third node will be selected (node 1, node 2 and node 3 selected) // It is not specified which data node will be used for the "imageForLevelWindow" / "levelwindow" property, // since the data storage access to the nodes is non-deterministic. // At least check for any valid level window property. m_DataNode3->SetSelected(true); auto usedLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is null", usedLevelWindowProperty); // All three nodes are selected: Check if only one node has the "imageForLevelWindow" property set and this node's // "levelwindow" property is used by the level window manager. auto levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("imageForLevelWindow")); if (usedLevelWindowProperty == levelWindowProperty1) { CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); } auto levelWindowProperty2 = dynamic_cast(m_DataNode2->GetProperty("imageForLevelWindow")); if (usedLevelWindowProperty == levelWindowProperty2) { CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); } auto levelWindowProperty3 = dynamic_cast(m_DataNode3->GetProperty("imageForLevelWindow")); if (usedLevelWindowProperty == levelWindowProperty3) { CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); } m_DataNode1->SetSelected(false); m_DataNode2->SetSelected(false); m_DataNode3->SetSelected(false); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); } void TestCombinedPropertiesChanged() { m_LevelWindowManager->SetSelectedImages(true); m_DataNode1->SetSelected(true); // selection mode enabled - node1 becomes the "imageForLevelWindow" node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode2->SetIntProperty("layer", itk::NumericTraits::max()); // selection mode enabled - node1 stays the "imageForLevelWindow" node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode1->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR)); // selection mode enabled - but node1 is ignored - no active level window property CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); m_DataNode3->SetBoolProperty("imageForLevelWindow", true); // selection mode enabled - no valid node selected, no active level window property CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); m_LevelWindowManager->SetSelectedImages(false); // no mode enabled - however, level window is not modified / updated CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); m_LevelWindowManager->Update(itk::ModifiedEvent()); // no mode enabled - level window is modified / updated with topmost visible node2 as fallback CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode3->SetBoolProperty("imageForLevelWindow", true); // no mode enabled - use node3 with "imageForLevelWindow" property set CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); m_DataNode3->SetVisibility(false); // node3 with "imageForLevelWindow" property not visible - use topmost visible node2 as fallback CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode2->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::LOOKUPTABLE_COLOR)); // fallback node2 is ignored - but "imageForLevelWindow" and "levelWindow" stay "true" for node2 CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_LevelWindowManager->SetAutoTopMostImage(true); // no visible node exists that is not ignored CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); auto levelWindowProperty = dynamic_cast(m_DataNode3->GetProperty("levelwindow")); - m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); // explicitely set the level window to node3 + m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); // explicitly set the level window to node3 CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); m_DataNode1->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR)); // auto topmost mode enabled - node1 is the only visible non-ignored node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode2->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR)); // auto topmost mode enabled - node2 is topmost visible node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); m_DataNode2->SetIntProperty("layer", itk::NumericTraits::min()); // auto topmost mode enabled - node1 is topmost visible node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); m_DataNode3->SetVisibility(true); // auto topmost mode enabled - node3 is topmost visible node CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); } void TestRemoveDataNodes() { m_DataManager->Remove(m_DataNode3); CPPUNIT_ASSERT_MESSAGE("Node not correctly removed", m_LevelWindowManager->GetRelevantNodes()->size() == 2); CPPUNIT_ASSERT_MESSAGE("Observer not correctly removed", m_LevelWindowManager->GetNumberOfObservers() == 2); bool isImageForLevelWindow1, isImageForLevelWindow2; m_DataNode1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); m_DataNode2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", !isImageForLevelWindow1 && isImageForLevelWindow2); auto levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); auto levelWindowProperty2 = dynamic_cast(m_DataNode2->GetProperty("levelwindow")); auto managerLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", (false == (managerLevelWindowProperty == levelWindowProperty1)) && (true == (managerLevelWindowProperty == levelWindowProperty2))); m_DataManager->Remove(m_DataNode2); CPPUNIT_ASSERT_MESSAGE("Node not correctly removed", m_LevelWindowManager->GetRelevantNodes()->size() == 1); CPPUNIT_ASSERT_MESSAGE("Observer not correctly removed", m_LevelWindowManager->GetNumberOfObservers() == 1); m_DataNode1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", isImageForLevelWindow1); levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); managerLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", true == (managerLevelWindowProperty == levelWindowProperty1)); m_DataManager->Remove(m_DataNode1); CPPUNIT_ASSERT_MESSAGE("Node not correctly removed", m_LevelWindowManager->GetRelevantNodes()->size() == 0); CPPUNIT_ASSERT_MESSAGE("Observer not correctly removed", m_LevelWindowManager->GetNumberOfObservers() == 0); CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); } }; MITK_TEST_SUITE_REGISTRATION(mitkLevelWindowManager) diff --git a/Modules/Core/test/mitkLevelWindowTest.cpp b/Modules/Core/test/mitkLevelWindowTest.cpp index 0953bb9c49..9e391cdae7 100644 --- a/Modules/Core/test/mitkLevelWindowTest.cpp +++ b/Modules/Core/test/mitkLevelWindowTest.cpp @@ -1,966 +1,966 @@ /*============================================================================ 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 "mitkImageWriteAccessor.h" #include "mitkLevelWindow.h" #include /* -* Reseting the Levelwindow to default values: +* Resetting the Levelwindow to default values: * i.e. Range = -1000..1000, Level = 0 and Window = 500 */ void resetLevelWindow(mitk::LevelWindow &lw) { // Default window bounds lw.SetRangeMinMax(-10000, 10000); lw.SetLevelWindow(0, 500, false); if (lw.GetRangeMin() != -10000 || lw.GetRangeMax() != 10000 || lw.GetLevel() != 0 || lw.GetWindow() != 500) { std::cout << "[Failed] To reset Levelwindow" << std::endl; } } int mitkLevelWindowTest(int, char *[]) { std::cout << "Testing mitk::LevelWindow " << std::endl; std::cout << "Testing mitk::LevelWindow constructor with Level and Window "; auto levWin = new mitk::LevelWindow(256, 500); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultWindow "; mitk::ScalarType defaultWindow = levWin->GetDefaultWindow(); if (!(defaultWindow == 500)) { std::cout << defaultWindow << " [FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultLevel "; mitk::ScalarType defaultLevel = levWin->GetDefaultLevel(); if (!(defaultLevel == 256)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetWindow "; mitk::ScalarType window = levWin->GetWindow(); if (!(window == 500)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetMin "; if (!(levWin->GetLowerWindowBound() == 6)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetMax "; if (!(levWin->GetUpperWindowBound() == 506)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetLevel "; mitk::ScalarType level = levWin->GetLevel(); if (!(level == 256)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetWindow : GetDefaultWindow "; if (!(defaultWindow == window)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetLevel : GetDefaultLevel "; if (!(defaultLevel == level)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetLevelWindow "; levWin->SetLevelWindow(20, 100); if (!(levWin->GetLevel() == 20)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } if (!(levWin->GetWindow() == 100)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetLevelWindow "; levWin->SetLevelWindow(levWin->GetDefaultLevel(), levWin->GetDefaultWindow()); if (!(levWin->GetLevel() == 256) && !(levWin->GetWindow() == 500)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultLevelWindow "; levWin->SetDefaultLevelWindow(20, 200); if (!(levWin->GetDefaultLevel() == 20) && !(levWin->GetDefaultWindow() == 200)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow ResetDefaultLevelWindow "; levWin->SetLevelWindow(100, 50); levWin->ResetDefaultLevelWindow(); // double a = levWin->GetLevel(); // double d = levWin->GetWindow(); if (!((levWin->GetLevel() == 20) && (levWin->GetWindow() == 200))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetWindowBounds "; levWin->SetWindowBounds(0, 2); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2) && (levWin->GetLevel() == 1) && (levWin->GetWindow() == 2))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with rangemin = rangemax"; levWin->SetRangeMinMax(2000, 2000); if (!(levWin->GetRangeMin() == 1999 && levWin->GetRangeMax() == 2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with rangemin > rangemax"; levWin->SetRangeMinMax(2100, 2000); if (!(levWin->GetRangeMin() == 2000 && levWin->GetRangeMax() == 2100)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax "; levWin->SetRangeMinMax(-1000, 2000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetRangeMin "; if (!(levWin->GetRangeMin() == -1000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetRangeMax "; if (!(levWin->GetRangeMax() == 2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetRange "; if (!((levWin->GetRangeMax() - levWin->GetRangeMin()) == levWin->GetRange())) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultBoundaries with rangemin = rangemax"; levWin->SetDefaultBoundaries(2000, 2000); if (!(levWin->GetDefaultLowerBound() == 1999 && levWin->GetDefaultUpperBound() == 2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultBoundaries with rangemin > rangemax"; levWin->SetDefaultBoundaries(2100, 2000); if (!(levWin->GetDefaultLowerBound() == 2000 && levWin->GetDefaultUpperBound() == 2100)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultBoundaries "; levWin->SetDefaultBoundaries(-2000, 8000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultLowerBound "; if (!(levWin->GetDefaultLowerBound() == -2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultUpperBound "; if (!(levWin->GetDefaultUpperBound() == 8000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow ResetDefaultRangeMinMax "; levWin->ResetDefaultRangeMinMax(); if (!((levWin->GetRangeMin() == levWin->GetDefaultLowerBound()) && (levWin->GetRangeMax() == levWin->GetDefaultUpperBound()))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow minRange > maxRange "; levWin->SetRangeMinMax(2000, 1000); if (!((levWin->GetRangeMin() == 1000) && (levWin->GetRangeMax() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(2000, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(-2000, -3000); if (!((levWin->GetRangeMin() == -3000) && (levWin->GetRangeMax() == -2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(0, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 0))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(2000, 0); if (!((levWin->GetRangeMin() == 0) && (levWin->GetRangeMax() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(-10000, 10000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow defaultMinRange > defaultMaxRange "; levWin->SetDefaultBoundaries(2000, 1000); if (!((levWin->GetDefaultLowerBound() == 1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(2000, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(-2000, -3000); if (!((levWin->GetDefaultLowerBound() == -3000) && (levWin->GetDefaultUpperBound() == -2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(0, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 0))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(2000, 0); if (!((levWin->GetDefaultLowerBound() == 0) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(-10000, 10000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min > max "; levWin->SetWindowBounds(2000, 1000); if (!((levWin->GetLowerWindowBound() == 1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(2000, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(-2000, -3000); if (!((levWin->GetLowerWindowBound() == -3000) && (levWin->GetUpperWindowBound() == -2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(0, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 0))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(2000, 0); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; // minmax > maxrange, minmax < minrange, minmaxrange, min < minrange & max > minrange // max < minrange & min > minrange, min > maxrange & max < maxrange, min < minrange & max > maxrange // min > maxrange & max < minrange std::cout << "Testing mitk::LevelWindow max > min > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << levWin->GetLowerWindowBound() << "," << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow max > min > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 12000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout << "[FAILED]" << levWin->GetLowerWindowBound() << "," << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; // Reset default window resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min > max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(12000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min > max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(12000, 11000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < max < minrange autoexpand = FALSE"; levWin->SetWindowBounds(-12000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < max < minrange autoexpand = TRUE"; levWin->SetWindowBounds(-12000, -11000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow max < min < minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -12000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]: Expected (-10000, -9999): " << levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow max < min < minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -12000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < maxrang & max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(9999, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < maxrang & max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(9999, 12000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 12000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < minrange & max > minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -9999, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < minrange & max > minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -9999); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < minrange & max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, 11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < minrange & max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, 11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow maxrange > min = max > minrange autoexpand = FALSE"; levWin->SetWindowBounds(5000, 5000, false); if (!((levWin->GetLowerWindowBound() == 4999.5) && (levWin->GetUpperWindowBound() == 5000.5))) { std::cout << "[FAILED]: Expected (4999.5, 5000.5): " << levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow maxrange > min = max > minrange autoexpand = TRUE"; levWin->SetWindowBounds(5000, 5000); if (!((levWin->GetLowerWindowBound() == 4999.5) && (levWin->GetUpperWindowBound() == 5000.5))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max = minrange autoexpand = FALSE"; levWin->SetWindowBounds(-10000, -10000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]: Expected (-10000, -9999): " << levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << " . " << levWin->GetRangeMin() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max = minrange autoexpand = TRUE"; levWin->SetWindowBounds(-10000, -10000); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max = maxrange autoexpand = FALSE"; levWin->SetWindowBounds(10000, 10000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max = maxrange autoexpand = TRUE"; levWin->SetWindowBounds(10000, 10000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 11000); if (!((levWin->GetLowerWindowBound() == 10999) && (levWin->GetUpperWindowBound() == 11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max < minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max < minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -10999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow maxrange > min > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(-9000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow maxrange > min > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(-9000, -11000, true); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min > maxrange > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min > maxrange > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow SetRangeMinMax with maxrange < min < max autoexpand = FALSE"; levWin->SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange > maxrange & maxrange < min < max "; levWin->ResetDefaultLevelWindow(); levWin->SetRangeMinMax(-15000, -20000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange < min < maxrange < max autoexpand = FALSE"; levWin->SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000, 110, false); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange < min < maxrange < max autoexpand = TRUE"; levWin->SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000, 110); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow SetRangeMinMax with maxrange < minrange & minrange < min < maxrange < max " "autoexpand = FALSE"; levWin->SetRangeMinMax(1000, -80); levWin->SetWindowBounds(-1000, 110, false); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with maxrange < minrange & minrange < min < maxrange < max " "autoexpand = TRUE"; levWin->SetRangeMinMax(1000, -80); levWin->SetWindowBounds(-1000, 110); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow SetRangeMinMax with min < minrange < maxrange SetRangeMinMax(20, 110); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minRange > maxRange & min < maxrange < max "; levWin->SetWindowBounds(-90, 1000); levWin->SetRangeMinMax(100, -80); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 100))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minRange > maxRange & min < minrange < maxrange SetRangeMinMax(20, 100); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 100))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with min < max < minrange "; levWin->SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange > maxrange & min < max < minrange "; levWin->SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with min < minrange SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; // auch für default levelwindow und default range // Create Image out of nowhere mitk::Image::Pointer image; // mitk::PixelType pt(typeid(int)); unsigned int dim[] = {100, 100, 20}; std::cout << "Creating image: "; image = mitk::Image::New(); // image->DebugOn(); image->Initialize(mitk::MakePixelType(), 3, dim); mitk::ImageWriteAccessor imAccess(image); auto *p = (int *)imAccess.GetData(); int size = dim[0] * dim[1] * dim[2]; int i; for (i = 0; i < size; ++i, ++p) *p = i; std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetAuto "; mitk::LevelWindow levelwindow; levelwindow.SetAuto(image); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow constructor with mitkLevelWindow "; const mitk::LevelWindow *lw = new mitk::LevelWindow(levelwindow); if (!(lw->GetRange() == levelwindow.GetRange())) { std::cout << "[FAILED]" << std::endl; delete lw; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow destructor "; delete levWin; delete lw; std::cout << "[PASSED]" << std::endl; mitk::LevelWindow levelWindow(50, 100); levelWindow.SetRangeMinMax(0, 100); // test range restriction/adaption for SetLevelWindow and SetWindowBounds std::cout << "Testing range restriction of mitk::LevelWindow::SetWindowBounds() autoexpand = FALSE"; mitk::ScalarType initialUpperBound = levelWindow.GetUpperWindowBound(); mitk::ScalarType initialLowerBound = levelWindow.GetLowerWindowBound(); levelWindow.SetWindowBounds(-10, 110, false); if (levelWindow.GetUpperWindowBound() != initialUpperBound || levelWindow.GetLowerWindowBound() != initialLowerBound) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range restriction of mitk::LevelWindow::SetWindowBounds() autoexpand = TRUE"; levelWindow.SetWindowBounds(-10, 110); if (levelWindow.GetUpperWindowBound() != 110 || levelWindow.GetLowerWindowBound() != -10) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; levelWindow.SetRangeMinMax(0, 100); levelWindow.SetLevelWindow(50, 100); std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = FALSE"; levelWindow.SetLevelWindow(60, 100, false); if (levelWindow.GetUpperWindowBound() != initialUpperBound) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = TRUE"; levelWindow.SetLevelWindow(60, 100); if (levelWindow.GetUpperWindowBound() != initialUpperBound + 10) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; levelWindow.SetRangeMinMax(0, 100); levelWindow.SetLevelWindow(50, 100); std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = FALSE"; levelWindow.SetLevelWindow(40, 100, false); if (levelWindow.GetLowerWindowBound() != initialLowerBound) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = TRUE"; levelWindow.SetLevelWindow(40, 100); if (levelWindow.GetLowerWindowBound() != initialLowerBound - 10) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range adaption of mitk::LevelWindow::SetWindowBounds()"; - levelWindow.SetWindowBounds(-10, 90, true); // ture == force + levelWindow.SetWindowBounds(-10, 90, true); // true == force if (levelWindow.GetUpperWindowBound() != 90.0 || levelWindow.GetLowerWindowBound() != -10.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range adaption of mitk::LevelWindow::SetWindowBounds()"; - levelWindow.SetWindowBounds(-20, 110, true); // ture == force + levelWindow.SetWindowBounds(-20, 110, true); // true == force if (levelWindow.GetUpperWindowBound() != 110.0 || levelWindow.GetLowerWindowBound() != -20.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range adaption of mitk::LevelWindow::SetLevelWindow()"; - levelWindow.SetLevelWindow(50, 140, true); // ture == force + levelWindow.SetLevelWindow(50, 140, true); // true == force if (levelWindow.GetUpperWindowBound() != 120.0 || levelWindow.GetLowerWindowBound() != -20.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing c'tor with x-ray values (formerly did not expand range enough)"; mitk::LevelWindow crLevelWindow(16352, 16444); if (crLevelWindow.GetLevel() != 16352.0 || crLevelWindow.GetWindow() != 16444.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "[PASSED]" << std::endl; std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } diff --git a/Modules/Core/test/mitkLogTest.cpp b/Modules/Core/test/mitkLogTest.cpp index 716d93c6cf..03ec18cf4e 100644 --- a/Modules/Core/test/mitkLogTest.cpp +++ b/Modules/Core/test/mitkLogTest.cpp @@ -1,300 +1,300 @@ /*============================================================================ 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 "mitkCommon.h" #include "mitkTestingMacros.h" #include #include #include #include #include #include /** Documentation * * @brief this class provides an accessible BackendCout to determine whether this backend was * used to process a message or not. * It is needed for the disable / enable backend test. */ class TestBackendCout : public mbilog::BackendCout { public: TestBackendCout() { m_Called = false; mbilog::BackendCout(); } void ProcessMessage(const mbilog::LogMessage &l) override { m_Called = true; mbilog::BackendCout::ProcessMessage(l); } bool WasCalled() { return m_Called; } private: bool m_Called; }; /** Documentation * * @brief Objects of this class can start an internal thread by calling the Start() method. * The thread is then logging messages until the method Stop() is called. The class * can be used to test if logging is thread-save by using multiple objects and let * them log simuntanously. */ class mitkTestLoggingThread : public itk::Object { public: mitkClassMacroItkParent(mitkTestLoggingThread, itk::Object); itkFactorylessNewMacro(Self); int NumberOfMessages; protected: mitkTestLoggingThread() : NumberOfMessages(0), LoggingRunning(true) { } ~mitkTestLoggingThread() { this->Stop(); } bool LoggingRunning; std::thread Thread; void LogMessages() { auto ThreadID = Thread.get_id(); while (LoggingRunning) { MITK_INFO << "Test info stream in thread" << ThreadID << "\n even with newlines"; MITK_WARN << "Test warning stream in thread " << ThreadID << ". " << "Even with a very long text, even without meaning or implied meaning or content, just a long " "sentence to see whether something has problems with long sentences or output in files or into " "windows or commandlines or whatever."; MITK_DEBUG << "Test debugging stream in thread " << ThreadID; MITK_ERROR << "Test error stream in thread " << ThreadID; MITK_FATAL << "Test fatal stream in thread " << ThreadID; NumberOfMessages += 5; } } static void ThreadStartTracking(void *instance) { auto* thisthread = reinterpret_cast(instance); if (thisthread != nullptr) thisthread->LogMessages(); } public: std::thread::id Start() { LoggingRunning = true; Thread = std::thread(this->ThreadStartTracking, this); return Thread.get_id(); } void Stop() { LoggingRunning = false; if(Thread.joinable()) Thread.join(); } }; /** Documentation * - * @brief This class holds static test methods to sturcture the test of the mitk logging mechanism. + * @brief This class holds static test methods to structure the test of the mitk logging mechanism. */ class mitkLogTestClass { public: static void TestSimpleLog() { bool testSucceded = true; try { MITK_INFO << "Test info stream."; MITK_WARN << "Test warning stream."; MITK_DEBUG << "Test debugging stream."; // only activated if cmake variable is on! // so no worries if you see no output for this line MITK_ERROR << "Test error stream."; MITK_FATAL << "Test fatal stream."; } catch (const mitk::Exception &) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging streams."); } static void TestObjectInfoLogging() { bool testSucceded = true; try { int i = 123; float f = .32234; double d = 123123; std::string testString = "testString"; std::stringstream testStringStream; testStringStream << "test" << "String" << "Stream"; mitk::Point3D testMitkPoint; testMitkPoint.Fill(2); MITK_INFO << i; MITK_INFO << f; MITK_INFO << d; MITK_INFO << testString; MITK_INFO << testStringStream.str(); MITK_INFO << testMitkPoint; } catch (const mitk::Exception &) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging of object information."); } static void TestThreadSaveLog(bool toFile) { bool testSucceded = true; try { if (toFile) { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testthreadlog.log"; itksys::SystemTools::RemoveFile(mitk::Utf8Util::Local8BitToUtf8(filename).c_str()); // remove old file, we do not want to append to large files mitk::LoggingBackend::SetLogFile(filename.c_str()); } unsigned int numberOfThreads = 20; unsigned int threadRuntimeInMilliseconds = 2000; std::vector threadIDs; std::vector threads; for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // initialize threads... threads.push_back(mitkTestLoggingThread::New()); std::cout << "Created " << threadIdx << ". thread." << std::endl; } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // start them std::cout << "Start " << threadIdx << ". thread." << std::endl; threadIDs.push_back(threads[threadIdx]->Start()); std::cout << threadIdx << ". thread has ID " << threadIDs[threadIdx] << std::endl; } // wait for some time (milliseconds) itksys::SystemTools::Delay(threadRuntimeInMilliseconds); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // stop them and wait for them to end std::cout << "Stop " << threadIdx << ". thread." << std::endl; threads[threadIdx]->Stop(); std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." << std::endl; } } catch (std::exception &e) { MITK_ERROR << "exception during 'TestThreadSaveLog': " << e.what(); testSucceded = false; } catch (...) { MITK_ERROR << "unknown exception during 'TestThreadSaveLog'"; testSucceded = false; } - // if no error occured until now, everything is ok + // if no error occurred until now, everything is ok MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging in different threads."); } static void TestLoggingToFile() { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testlog.log"; mitk::LoggingBackend::SetLogFile(filename.c_str()); MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile(); MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(filename).c_str()), "Testing if log file exists."); // TODO delete log file? } static void TestAddAndRemoveBackends() { mbilog::BackendCout myBackend = mbilog::BackendCout(); mbilog::RegisterBackend(&myBackend); MITK_INFO << "Test logging"; mbilog::UnregisterBackend(&myBackend); - // if no error occured until now, everything is ok + // if no error occurred until now, everything is ok MITK_TEST_CONDITION_REQUIRED(true, "Test add/remove logging backend."); } static void TestDefaultBackend() { // not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added // to mbilog utility one may add this test. } static void TestEnableDisableBackends() { TestBackendCout myCoutBackend = TestBackendCout(); mbilog::RegisterBackend(&myCoutBackend); mbilog::DisableBackends(mbilog::Console); MITK_INFO << "There should be no output!"; bool success = !myCoutBackend.WasCalled(); mbilog::EnableBackends(mbilog::Console); MITK_INFO << "Now there should be an output."; success &= myCoutBackend.WasCalled(); mbilog::UnregisterBackend(&myCoutBackend); MITK_TEST_CONDITION_REQUIRED(success, "Test disable / enable logging backends.") } }; int mitkLogTest(int /* argc */, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("Log") - MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!") + MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURRED!") mitkLogTestClass::TestSimpleLog(); mitkLogTestClass::TestObjectInfoLogging(); mitkLogTestClass::TestLoggingToFile(); mitkLogTestClass::TestAddAndRemoveBackends(); mitkLogTestClass::TestThreadSaveLog(false); // false = to console mitkLogTestClass::TestThreadSaveLog(true); // true = to file mitkLogTestClass::TestEnableDisableBackends(); // TODO actually test file somehow? // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkMaterialTest.cpp b/Modules/Core/test/mitkMaterialTest.cpp index 0669196246..9c21d77dc0 100644 --- a/Modules/Core/test/mitkMaterialTest.cpp +++ b/Modules/Core/test/mitkMaterialTest.cpp @@ -1,358 +1,358 @@ /*============================================================================ 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 "mitkBaseProperty.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkMaterial.h" #include "mitkTestingMacros.h" #include #include #include /** * Simple example for a test for the () class "ClassName". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (). */ class MaterialTest { public: mitk::Material::Pointer myMP; MaterialTest() { myMP = nullptr; } void testConstructor() { myMP = mitk::Material::New(); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") } void testConstructorWithColorOpacity() { mitk::Color color; color.Set(0, 0, 0); double opacity = 1.0f; myMP = mitk::Material::New(color, opacity); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") MITK_TEST_CONDITION(color == myMP->GetColor(), "Testing if a Color object was set correctly") MITK_TEST_CONDITION(opacity == myMP->GetOpacity(), "Testing if a Opacity object was set correctly") } void testConstructorWithRedGreenBlueOpacity() { mitk::Material::Color color; color.Set(0, 0, 0); double opacity = 1.0f; double rgb = 0; myMP = mitk::Material::New(rgb, rgb, rgb, opacity); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") MITK_TEST_CONDITION(color == myMP->GetColor(), "Testing if a Color object was set correctly") MITK_TEST_CONDITION(opacity == myMP->GetOpacity(), "Testing if a Opacity object was set correctly") } void testConstructorRedGreenBlueColorCoefficientSpecularCoefficientSpecularPowerOpacity() { mitk::Material::Color color; color.Set(0, 0, 0); double opacity = 1.0f; double rgb = 0; double colorCoefficient = 0; double specularCoefficient = 0; double specularPower = 0; myMP = mitk::Material::New(rgb, rgb, rgb, colorCoefficient, specularCoefficient, specularPower, opacity); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") MITK_TEST_CONDITION(color == myMP->GetColor(), "Testing if a Color object was set correctly") MITK_TEST_CONDITION(opacity == myMP->GetOpacity(), "Testing if a Opacity object was set correctly") MITK_TEST_CONDITION(specularCoefficient == myMP->GetSpecularCoefficient(), "Testing if a Coefficient object was set correctly") MITK_TEST_CONDITION(specularPower == myMP->GetSpecularPower(), "Testing if a SpecularPower object was set correctly") MITK_TEST_CONDITION(colorCoefficient == myMP->GetColorCoefficient(), "Testing if a colorCoefficient object was set correctly") } void testConstructorColorColorCoefficientSpecularCoefficientSpecularPowerOpacity() { mitk::Material::Color color; color.Set(0, 0, 0); double opacity = 1.0f; double rgb = 0; double colorCoefficient = 0; double specularCoefficient = 0; double specularPower = 0; myMP = mitk::Material::New(rgb, rgb, rgb, colorCoefficient, specularCoefficient, specularPower, opacity); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") MITK_TEST_CONDITION(color == myMP->GetColor(), "Testing if a Color object was set correctly") MITK_TEST_CONDITION(opacity == myMP->GetOpacity(), "Testing if a Opacity object was set correctly") MITK_TEST_CONDITION(specularCoefficient == myMP->GetSpecularCoefficient(), "Testing if a Coefficient object was set correctly") MITK_TEST_CONDITION(specularPower == myMP->GetSpecularPower(), "Testing if a SpecularPower object was set correctly") } void testConstructorPropertyRedGreenBlueOpacityAndName() { mitk::Material::Pointer reference = myMP; double opacity = 1.0f; double rgb = 0; std::string name = "Hans Wurst"; myMP = mitk::Material::New(*reference, rgb, rgb, rgb, opacity, name); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") MITK_TEST_CONDITION(opacity == myMP->GetOpacity(), "Testing if a Opacity object was set correctly") // MITK_TEST_CONDITION( name.compare(myMP->GetName(),0,9), "Testing if a Name object was set correctly" ) } void testSetColor() { mitk::Material::Color color; color.Set(0, 0, 0); myMP = mitk::Material::New(); myMP->SetColor(color); MITK_TEST_CONDITION_REQUIRED(myMP.IsNotNull(), "Testing instantiation") MITK_TEST_CONDITION(color == myMP->GetColor(), "Testing if a color was set correctly") color.Set(0, 0, 0); myMP->SetColor(color); MITK_TEST_CONDITION(color == myMP->GetColor(), "Testing if a color was set correctly") } void testSetColorCoefficient() { double colorCoefficient = 0; myMP = mitk::Material::New(); myMP->SetColorCoefficient(colorCoefficient); MITK_TEST_CONDITION(colorCoefficient == myMP->GetColorCoefficient(), "Testing if a colorcoefficent was set correctly") } void testSetSpecularColor() { mitk::Material::Color color; color.Set(0, 0, 0); myMP = mitk::Material::New(); myMP->SetSpecularColor(color); MITK_TEST_CONDITION(color == myMP->GetSpecularColor(), "Testing if a SpecularColor was set correctly") } void testSetSpecularCoefficient() { myMP = mitk::Material::New(); double specularCoefficient = 1; myMP->SetSpecularCoefficient(specularCoefficient); MITK_TEST_CONDITION(specularCoefficient == myMP->GetSpecularCoefficient(), "Testing if a SpecularCoefficient was set correctly") } void testSetSpecularPower() { myMP = mitk::Material::New(); double specularPower = 1; myMP->SetSpecularPower(specularPower); MITK_TEST_CONDITION(specularPower == myMP->GetSpecularPower(), "Testing if a SpecularPower was set correctly") } void testSetOpacity() { myMP = mitk::Material::New(); double opacity = 1; myMP->SetOpacity(opacity); MITK_TEST_CONDITION(opacity == myMP->GetOpacity(), "Testing if a Opacity was set correctly") } void testSetInterpolation() { myMP = mitk::Material::New(); mitk::Material::InterpolationType interpolation = mitk::Material::Flat; myMP->SetInterpolation(interpolation); MITK_TEST_CONDITION(interpolation == myMP->GetInterpolation(), "Testing if a Interpolation was set correctly") } void testSetRepresentation() { myMP = mitk::Material::New(); mitk::Material::RepresentationType representation = mitk::Material::Wireframe; myMP->SetRepresentation(representation); MITK_TEST_CONDITION(representation == myMP->GetRepresentation(), "Testing if a Representation was set correctly") } void testSetLineWidth() { myMP = mitk::Material::New(); double lineWidth = 1; myMP->SetLineWidth(lineWidth); MITK_TEST_CONDITION(lineWidth == myMP->GetLineWidth(), "Testing if a LineWidth was set correctly") } void testInitialize() { mitk::Material::Color color; color.Set(0, 0, 0); double opacity = 1.0f; double rgb = 0; double colorCoefficient = 0; double specularCoefficient = 0; double specularPower = 0; myMP = mitk::Material::New(rgb, rgb, rgb, colorCoefficient, specularCoefficient, specularPower, opacity); double lineWidth = 1; myMP->SetLineWidth(lineWidth); mitk::Material::RepresentationType representation = mitk::Material::Wireframe; myMP->SetRepresentation(representation); mitk::Material::InterpolationType interpolation = mitk::Material::Flat; myMP->SetInterpolation(interpolation); myMP->SetSpecularColor(color); std::string name = "Hans Wurst"; myMP->SetName(name); mitk::Material::Pointer myMP2 = mitk::Material::New(); myMP2->Initialize(*myMP); - MITK_TEST_CONDITION(*myMP == *myMP2, "testing equality after .Intitialize") + MITK_TEST_CONDITION(*myMP == *myMP2, "testing equality after .Initialize") } void testOperatorequality() { { mitk::Material::Color color; color.Set(0, 0, 0); double opacity = 1.0f; double rgb = 0; double colorCoefficient = 0; double specularCoefficient = 0; double specularPower = 0; myMP = mitk::Material::New(rgb, rgb, rgb, colorCoefficient, specularCoefficient, specularPower, opacity); double lineWidth = 1; myMP->SetLineWidth(lineWidth); mitk::Material::RepresentationType representation = mitk::Material::Wireframe; myMP->SetRepresentation(representation); mitk::Material::InterpolationType interpolation = mitk::Material::Flat; myMP->SetInterpolation(interpolation); myMP->SetSpecularColor(color); std::string name = "Hans Wurst"; myMP->SetName(name); mitk::Material::Color color2; color2.Set(0, 0, 0); double opacity2 = 1.0f; double rgb2 = 0; double colorCoefficient2 = 0; double specularCoefficient2 = 0; double specularPower2 = 0; mitk::Material::Pointer myMP2 = mitk::Material::New(rgb2, rgb2, rgb2, colorCoefficient2, specularCoefficient2, specularPower2, opacity2); double lineWidth2 = 1; myMP2->SetLineWidth(lineWidth2); mitk::Material::RepresentationType representation2 = mitk::Material::Wireframe; myMP2->SetRepresentation(representation2); mitk::Material::InterpolationType interpolation2 = mitk::Material::Flat; myMP2->SetInterpolation(interpolation2); myMP2->SetSpecularColor(color2); std::string name2 = "Hans Wurst"; myMP2->SetName(name2); MITK_TEST_CONDITION(*myMP == *myMP2, "testing equality Operator") } { mitk::Material::Color color; color.Set(0, 0, 0); double opacity = 1.0f; double rgb = 0; double colorCoefficient = 0; double specularCoefficient = 0; double specularPower = 0; myMP = mitk::Material::New(rgb, rgb, rgb, colorCoefficient, specularCoefficient, specularPower, opacity); double lineWidth = 1; myMP->SetLineWidth(lineWidth); mitk::Material::RepresentationType representation = mitk::Material::Wireframe; myMP->SetRepresentation(representation); mitk::Material::InterpolationType interpolation = mitk::Material::Flat; myMP->SetInterpolation(interpolation); myMP->SetSpecularColor(color); std::string name = "Hans Wurst"; myMP->SetName(name); mitk::Material::Color color2; color2.Set(0, 0, 0); double opacity2 = 1.0f; double rgb2 = 1; double colorCoefficient2 = 0; double specularCoefficient2 = 0; double specularPower2 = 0; mitk::Material::Pointer myMP2 = mitk::Material::New(rgb2, rgb2, rgb2, colorCoefficient2, specularCoefficient2, specularPower2, opacity2); double lineWidth2 = 1; myMP2->SetLineWidth(lineWidth2); mitk::Material::RepresentationType representation2 = mitk::Material::Wireframe; myMP2->SetRepresentation(representation2); mitk::Material::InterpolationType interpolation2 = mitk::Material::Flat; myMP2->SetInterpolation(interpolation2); myMP2->SetSpecularColor(color2); std::string name2 = "Hans Wurst"; myMP2->SetName(name2); MITK_TEST_CONDITION(!(*myMP == *myMP2), "testing equality Operator") } } void testAssignable() { mitk::Material::Pointer materialProp = mitk::Material::New(); MITK_TEST_CONDITION(myMP->Assignable(*materialProp), "testing Assignable with Material") } void testOperatorAssign() { mitk::Material::Pointer myMP2 = mitk::Material::New(); *myMP2 = *myMP; MITK_TEST_CONDITION(*myMP == *myMP2, "Testing Assignment Operator") } }; int mitkMaterialTest(int /* argc */, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("Material") MaterialTest materialTest; materialTest.testConstructor(); materialTest.testConstructorWithColorOpacity(); materialTest.testConstructorWithRedGreenBlueOpacity(); materialTest.testConstructorRedGreenBlueColorCoefficientSpecularCoefficientSpecularPowerOpacity(); materialTest.testConstructorColorColorCoefficientSpecularCoefficientSpecularPowerOpacity(); materialTest.testConstructorPropertyRedGreenBlueOpacityAndName(); materialTest.testAssignable(); materialTest.testOperatorAssign(); materialTest.testSetColor(); materialTest.testSetColorCoefficient(); materialTest.testSetSpecularColor(); materialTest.testSetSpecularCoefficient(); materialTest.testSetSpecularPower(); materialTest.testSetOpacity(); materialTest.testSetInterpolation(); materialTest.testSetRepresentation(); materialTest.testSetLineWidth(); materialTest.testInitialize(); materialTest.testOperatorequality(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkMessageTest.cpp b/Modules/Core/test/mitkMessageTest.cpp index 891aba839d..1929f4e583 100644 --- a/Modules/Core/test/mitkMessageTest.cpp +++ b/Modules/Core/test/mitkMessageTest.cpp @@ -1,300 +1,300 @@ /*============================================================================ 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 "mitkMessage.h" #include "mitkTestingMacros.h" #include namespace mitk { class mitkMessageTestTestClass { public: // dummy class to send around class Package { public: Package(int content = 43) : m_Content(content) {} void Clear() { m_Content = 0; } bool operator==(const Package &other) { return m_Content == other.m_Content; } private: int m_Content; }; class MessageSenderClass { public: // message without any parameters, pure notification Message<> WaveHand; // message without any parameters, pure notification Message<> ShowFinger; // message with one parameter of type std::string Message1 Say; // message with one parameter of type double Message1 WalkMeters; // message with one parameter of class type Package Message1 GivePackage; // message with two parameters of type int and float Message2 ShoutAgeAndFootSize; void DoShowFinger() { ShowFinger.Send(); // ShowFinger() does the same } void DoWaveHand() { WaveHand(); // WaveHand.Send() does the same } void DoSay(const std::string &words) { Say(words); } void DoWalk(double meters) { WalkMeters(meters); } void DoGivePackage(const Package &package) { GivePackage.Send(package); } void DoShoutAgeAndFootSize(int age, float size) { ShoutAgeAndFootSize(age, size); } }; // Receiver class remembers events received. // Will tell about received events when asked. class MessageReceiverClass { public: MessageReceiverClass() { Amnesia(); } void OnWaveHand() { m_HandWaved = true; } bool HandWaved() { return m_HandWaved; } void OnSay(const std::string &words) { m_WordsSaid = words; } const std::string WordsSaid() { return m_WordsSaid; } void OnWalk(double meters) { m_MetersWalked = meters; } double MetersWalked() { return m_MetersWalked; } void OnGivePackage(const Package &p) { m_PackageReceived = p; } Package PackageReceived() { return m_PackageReceived; } void OnShoutAgeAndFootSize(int age, float footSize) { m_Age = age; m_FootSize = footSize; } int Age() { return m_Age; } float FootSize() { return m_FootSize; } void Amnesia() { m_HandWaved = false; m_WordsSaid.clear(); m_MetersWalked = 0.0; m_PackageReceived.Clear(); m_Age = 0; m_FootSize = 0.0; } void RegisterObservers(MessageSenderClass &sender) { sender.WaveHand += MessageDelegate(this, &MessageReceiverClass::OnWaveHand); sender.ShowFinger += MessageDelegate( this, &MessageReceiverClass::OnWaveHand); // we cannot see clearly, misinterpret this sender.Say += MessageDelegate1(this, &MessageReceiverClass::OnSay); sender.WalkMeters += MessageDelegate1(this, &MessageReceiverClass::OnWalk); sender.GivePackage += MessageDelegate1(this, &MessageReceiverClass::OnGivePackage); sender.ShoutAgeAndFootSize += MessageDelegate2(this, &MessageReceiverClass::OnShoutAgeAndFootSize); } private: bool m_HandWaved; std::string m_WordsSaid; double m_MetersWalked; Package m_PackageReceived; int m_Age; float m_FootSize; }; /* MessageMacro Test classes */ class Law { private: std::string m_Description; public: Law(const std::string &law) : m_Description(law) {} std::string GetDescription() const { return m_Description; } }; // The NewtonMachine will issue specific events class NewtonMachine { mitkNewMessageMacro(AnalysisStarted); mitkNewMessage1Macro(AnalysisStopped, bool); mitkNewMessage1Macro(LawDiscovered, const Law &); // mitkNewMessageWithReturnMacro(PatentFiled, bool); public: void StartAnalysis() { // send the "started" signal m_AnalysisStartedMessage(); // we found a new law of nature by creating one :-) Law massLaw("Unit tests are mandatory!"); m_LawDiscoveredMessage(massLaw); } void StopAnalysis() { // send the "stop" message with true, indicating // that an error occurred m_AnalysisStoppedMessage(true); } bool PatentLaw() { // bool patentAccepted = m_PatentFiledMessage(); // return patentAccepted; // m_PatentFiledMessage(); return false; } }; class Observer { private: NewtonMachine *m_Machine; public: Observer(NewtonMachine *machine) : m_Machine(machine), m_MachineStarted(false), m_MachineStopped(false), m_Error(false), m_Law("NONE") { // Add "observers", i.e. function pointers to the machine m_Machine->AddAnalysisStartedListener(::mitk::MessageDelegate(this, &Observer::MachineStarted)); m_Machine->AddAnalysisStoppedListener( ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); m_Machine->AddLawDiscoveredListener( ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); // m_Machine->AddPatentFiledListener( // ::mitk::MessageDelegate(this, &Observer::ReviewPatent)); } ~Observer() { // Always remove your observers when finished m_Machine->RemoveAnalysisStartedListener(::mitk::MessageDelegate(this, &Observer::MachineStarted)); m_Machine->RemoveAnalysisStoppedListener( ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); m_Machine->RemoveLawDiscoveredListener( ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); // m_Machine->RemoveLawDiscoveredListener( // ::mitk::MessageDelegate(this, &Observer::ReviewPatent)); } void MachineStarted() { m_MachineStarted = true; } void MachineStopped(bool error) { m_MachineStopped = true; m_Error = error; } void LawDiscovered(const Law &law) { m_Law = law; } bool ReviewPatent() { m_PatentReviewed = true; return false; // laws of nature are not patentable. } bool m_MachineStarted; bool m_MachineStopped; bool m_Error; Law m_Law; bool m_PatentReviewed; }; }; // end test class } // end namespace int mitkMessageTest(int /* argc */, char * /*argv*/ []) { MITK_TEST_BEGIN("Message") mitk::mitkMessageTestTestClass::MessageSenderClass sender; mitk::mitkMessageTestTestClass::MessageReceiverClass receiver; MITK_TEST_CONDITION_REQUIRED(true, "Testing instantiation"); receiver.RegisterObservers(sender); MITK_TEST_CONDITION_REQUIRED(true, "Testing registration to messages"); MITK_TEST_CONDITION_REQUIRED((sender.DoWaveHand(), // This is called "comma operator". Don't ask, read! receiver.HandWaved()), "Message without parameters"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED((sender.DoShowFinger(), receiver.HandWaved()), "Message without parameters"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED((sender.DoSay("Blooodworsch"), receiver.WordsSaid() == "Blooodworsch"), "Message with std::string parameter"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED((sender.DoWalk(2.67), (receiver.MetersWalked() - 2.67) < 0.0001), "Message with double parameter"); receiver.Amnesia(); mitk::mitkMessageTestTestClass::Package package(8); MITK_TEST_CONDITION_REQUIRED((sender.DoGivePackage(package), receiver.PackageReceived() == package), "Message with class parameter"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED( (sender.DoShoutAgeAndFootSize(46, 30.5), (receiver.Age() == 46 && (receiver.FootSize() - 30.5 < 0.0001))), "Message with int AND float parameter"); receiver.Amnesia(); mitk::mitkMessageTestTestClass::NewtonMachine newtonMachine; mitk::mitkMessageTestTestClass::Observer observer1(&newtonMachine); mitk::mitkMessageTestTestClass::Observer observer2(&newtonMachine); // This will send two events to registered observers newtonMachine.StartAnalysis(); MITK_TEST_CONDITION(observer1.m_MachineStarted == true, "Message from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer2.m_MachineStarted == true, "Message from Message Macro send to receiver 2"); MITK_TEST_CONDITION(observer1.m_Law.GetDescription() == std::string("Unit tests are mandatory!"), "Message1 from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer2.m_Law.GetDescription() == std::string("Unit tests are mandatory!"), "Message1 from Message Macro send to receiver 2"); // This will send one event to registered observers newtonMachine.StopAnalysis(); MITK_TEST_CONDITION(observer1.m_MachineStopped == true, "Message1 from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer1.m_Error == true, "Message1 parameter from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer2.m_MachineStopped == true, "Message1 from Message Macro send to receiver 2"); MITK_TEST_CONDITION(observer2.m_Error == true, "Message1 parameter from Message Macro send to receiver 2"); - /* Message with return type tests are work in progess... */ + /* Message with return type tests are work in progress... */ // bool patentSuccessful = newtonMachine.PatentLaw(); // what with return types from multiple observers? // MITK_TEST_CONDITION((observer1.m_PatentReviewed == true) && (patentSuccessful == false), // "Message with return type from Message Macro send to receiver 1"); // // MITK_TEST_CONDITION((observer2.m_PatentReviewed == true) && (patentSuccessful == false), // "Message with return type from Message Macro send to receiver 2"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkNodePredicateDataPropertyTest.cpp b/Modules/Core/test/mitkNodePredicateDataPropertyTest.cpp index 6beaf49348..0602c40f92 100644 --- a/Modules/Core/test/mitkNodePredicateDataPropertyTest.cpp +++ b/Modules/Core/test/mitkNodePredicateDataPropertyTest.cpp @@ -1,115 +1,115 @@ /*============================================================================ 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 "mitkGeometry3D.h" #include "mitkBaseDataTestImplementation.h" #include "mitkNodePredicateDataProperty.h" #include "mitkDataNode.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkStringProperty.h" class mitkNodePredicateDataPropertyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkNodePredicateDataPropertyTestSuite); MITK_TEST(Check_InvalidConstructor); MITK_TEST(Check_InvalidCheckNode); MITK_TEST(Check_CheckName); MITK_TEST(Check_CheckNameAndValue); CPPUNIT_TEST_SUITE_END(); private: mitk::BaseDataTestImplementation::Pointer m_Data; mitk::BaseDataTestImplementation::Pointer m_OtherData; mitk::DataNode::Pointer m_Node; mitk::DataNode::Pointer m_NullNode; mitk::DataNode::Pointer m_NullDataNode; mitk::DataNode::Pointer m_OtherNode; mitk::DataNode::Pointer m_NoPropNode; mitk::StringProperty::Pointer m_RefProperty; public: /** - * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_RefProperty = mitk::StringProperty::New("value1"); m_Data = mitk::BaseDataTestImplementation::New(); m_Data->SetProperty("prop1", mitk::StringProperty::New("value1")); m_Data->SetProperty("prop2", mitk::StringProperty::New("value2")); m_OtherData = mitk::BaseDataTestImplementation::New(); m_OtherData->SetProperty("prop1", mitk::StringProperty::New("othervalue")); m_NullNode = nullptr; m_NullDataNode = mitk::DataNode::New(); m_Node = mitk::DataNode::New(); m_Node->SetData(m_Data); m_OtherNode = mitk::DataNode::New(); m_OtherNode->SetData(m_OtherData); m_NoPropNode = mitk::DataNode::New(); m_NoPropNode->SetData(mitk::BaseDataTestImplementation::New()); } void tearDown() override { m_Node = nullptr; m_NullNode = nullptr; m_NullDataNode = nullptr; m_OtherNode = nullptr; m_Data = nullptr; m_OtherData = nullptr; } void Check_InvalidConstructor() { CPPUNIT_ASSERT_THROW(mitk::NodePredicateDataProperty::New(""), mitk::Exception); } void Check_InvalidCheckNode() { mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New("aProp"); CPPUNIT_ASSERT_THROW(predicate->CheckNode(nullptr), mitk::Exception); } void Check_CheckName() { mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New("prop1"); CPPUNIT_ASSERT(!predicate->CheckNode(m_NullDataNode)); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(predicate->CheckNode(m_OtherNode)); CPPUNIT_ASSERT(!predicate->CheckNode(m_NoPropNode)); } void Check_CheckNameAndValue() { mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New("prop1", m_RefProperty); CPPUNIT_ASSERT(!predicate->CheckNode(m_NullDataNode)); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(m_OtherNode)); CPPUNIT_ASSERT(!predicate->CheckNode(m_NoPropNode)); } }; MITK_TEST_SUITE_REGISTRATION(mitkNodePredicateDataProperty) diff --git a/Modules/Core/test/mitkNodePredicateGeometryTest.cpp b/Modules/Core/test/mitkNodePredicateGeometryTest.cpp index 1883a5af2b..e5a2790ec6 100644 --- a/Modules/Core/test/mitkNodePredicateGeometryTest.cpp +++ b/Modules/Core/test/mitkNodePredicateGeometryTest.cpp @@ -1,188 +1,188 @@ /*============================================================================ 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 "mitkGeometry3D.h" #include "mitkBaseDataTestImplementation.h" #include "mitkNodePredicateGeometry.h" #include "mitkDataNode.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkProportionalTimeGeometry.h" class mitkNodePredicateGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkNodePredicateGeometryTestSuite); MITK_TEST(Check_InvalidConstructor); MITK_TEST(Check_CloneAndOriginal); MITK_TEST(Check_DifferentOrigin); MITK_TEST(Check_DifferentIndexToWorldTransform); MITK_TEST(Check_DifferentSpacing); MITK_TEST(Check_DifferentBoundingBox); CPPUNIT_TEST_SUITE_END(); private: mitk::BaseDataTestImplementation::Pointer m_Data; mitk::DataNode::Pointer m_Node; mitk::Geometry3D::Pointer m_RefGeometry; mitk::TimeGeometry::Pointer m_RefTimeGeometry; mitk::Geometry3D::Pointer m_AnotherGeometry3D; public: /** -* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_RefGeometry = mitk::Geometry3D::New(); m_RefGeometry->Initialize(); mitk::ProportionalTimeGeometry::Pointer tgeo = mitk::ProportionalTimeGeometry::New(); tgeo->Initialize(m_RefGeometry, 1); m_RefTimeGeometry = tgeo.GetPointer(); m_Data = mitk::BaseDataTestImplementation::New(); m_Data->SetClonedGeometry(m_RefGeometry); m_Node = mitk::DataNode::New(); m_Node->SetData(m_Data); m_AnotherGeometry3D = m_RefGeometry->Clone(); } void tearDown() override { m_RefGeometry = nullptr; m_RefTimeGeometry = nullptr; m_AnotherGeometry3D = nullptr; m_Data = nullptr; } void Check_InvalidConstructor() { m_RefGeometry = nullptr; m_RefTimeGeometry = nullptr; CPPUNIT_ASSERT_THROW(mitk::NodePredicateGeometry::New(m_RefGeometry, 3), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::NodePredicateGeometry::New(m_RefGeometry), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::NodePredicateGeometry::New(m_RefTimeGeometry), mitk::Exception); } void Check_CloneAndOriginal() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(predicateTime->CheckNode(m_Node)); } void Check_DifferentOrigin() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::Point3D origin; origin[0] = 0.0; origin[1] = 0.0; origin[2] = 1.0; m_AnotherGeometry3D->SetOrigin(origin); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } void Check_DifferentIndexToWorldTransform() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::AffineTransform3D::Pointer differentIndexToWorldTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType differentMatrix; differentMatrix.SetIdentity(); differentMatrix(1, 1) = 2; differentIndexToWorldTransform->SetMatrix(differentMatrix); m_AnotherGeometry3D->SetIndexToWorldTransform(differentIndexToWorldTransform); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } void Check_DifferentSpacing() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::Vector3D differentSpacing; differentSpacing[0] = 1.0; differentSpacing[1] = 2.0; differentSpacing[2] = 3.0+3* mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; m_AnotherGeometry3D->SetSpacing(differentSpacing); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); //check with altered geometry as reference (sanity check). mitk::NodePredicateGeometry::Pointer predicate2 = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate2->CheckNode(m_Node)); - //check less strict precission checkings + //check less strict precision checkings differentSpacing[0] = 1.0; differentSpacing[1] = 1.0; differentSpacing[2] = 1.0 + 3 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; m_AnotherGeometry3D->SetSpacing(differentSpacing); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate->SetCheckPrecision(1e-3); predicateTime->SetCheckPrecision(1e-3); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(predicateTime->CheckNode(m_Node)); } void Check_DifferentBoundingBox() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::ScalarType bounds[] = { 0.0, 0.0, 0.0, 1.0, 2.0, 3.0 }; m_AnotherGeometry3D->SetBounds(bounds); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } }; MITK_TEST_SUITE_REGISTRATION(mitkNodePredicateGeometry) diff --git a/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp b/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp index fd01017681..0864dac46e 100644 --- a/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp +++ b/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp @@ -1,244 +1,244 @@ /*============================================================================ 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 "mitkGeometry3D.h" #include "mitkBaseDataTestImplementation.h" #include "mitkNodePredicateSubGeometry.h" #include "mitkNodePredicateGeometry.h" #include "mitkDataNode.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" class mitkNodePredicateSubGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkNodePredicateSubGeometryTestSuite); MITK_TEST(Check_InvalidConstructor); MITK_TEST(Check_Spacing); MITK_TEST(Check_TransformMatrix); MITK_TEST(Check_Bounds); MITK_TEST(Check_Grid); CPPUNIT_TEST_SUITE_END(); private: mitk::BaseDataTestImplementation::Pointer m_Data; mitk::DataNode::Pointer m_Node; mitk::Geometry3D::Pointer m_RefGeometry; mitk::Geometry3D::Pointer m_AnotherGeometry3D; public: /** -* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_RefGeometry = mitk::Geometry3D::New(); m_RefGeometry->Initialize(); m_Data = mitk::BaseDataTestImplementation::New(); m_Data->SetClonedGeometry(m_RefGeometry); m_Node = mitk::DataNode::New(); m_Node->SetData(m_Data); m_AnotherGeometry3D = m_RefGeometry->Clone(); } void tearDown() override { m_RefGeometry = nullptr; m_AnotherGeometry3D = nullptr; m_Data = nullptr; } void Check_InvalidConstructor() { m_RefGeometry = nullptr; CPPUNIT_ASSERT_THROW(mitk::NodePredicateSubGeometry::New(m_RefGeometry, 3), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::NodePredicateSubGeometry::New(m_RefGeometry), mitk::Exception); } void Check_CloneAndOriginal() { mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } void Check_Spacing() { mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = m_RefGeometry->GetSpacing(); wrongSpacing[i] += mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION * 2; auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = m_RefGeometry->GetSpacing(); wrongSpacing[i] -= mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION * 2; auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } void Check_TransformMatrix() { mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { itk::Matrix wrongMatrix = m_RefGeometry->GetIndexToWorldTransform()->GetMatrix(); wrongMatrix[i][j] += mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION * 2; auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } } void Check_Bounds() { auto newBounds = m_RefGeometry->GetBounds(); newBounds[0] = 10; newBounds[1] = 20; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 10; newBounds[5] = 20; m_RefGeometry->SetBounds(newBounds); mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = m_RefGeometry->Clone(); legalGeometry->SetBounds(legalBounds); m_Node->GetData()->SetGeometry(legalGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = m_RefGeometry->GetBounds(); if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->SetBounds(wrongBounds); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } void Check_Grid() { auto newBounds = m_RefGeometry->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; m_RefGeometry->SetBounds(newBounds); mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); auto smallerGeometry = m_RefGeometry->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); m_Node->GetData()->SetGeometry(legalGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] += smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); m_Node->GetData()->SetGeometry(legalGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } }; MITK_TEST_SUITE_REGISTRATION(mitkNodePredicateSubGeometry) diff --git a/Modules/Core/test/mitkPixelTypeTest.cpp b/Modules/Core/test/mitkPixelTypeTest.cpp index 3bafd0bff5..0f4ad40e51 100644 --- a/Modules/Core/test/mitkPixelTypeTest.cpp +++ b/Modules/Core/test/mitkPixelTypeTest.cpp @@ -1,241 +1,241 @@ /*============================================================================ 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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include "mitkPixelType.h" #include // ITK includes #include "itkImage.h" #include // VTK includes #include struct MyObscurePixelType { typedef float ValueType; static const size_t Length = 2; }; //## Documentation //## main testing method class mitkPixelTypeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPixelTypeTestSuite); MITK_TEST(GetComponentType_Success); MITK_TEST(GetPixelTypeAsString_Success); MITK_TEST(GetBpePtype_Success); MITK_TEST(GetNumberOfComponentsPtype_Success); MITK_TEST(GetBitsPerComponentPtype_Success); MITK_TEST(GetBpeItkPtype_Success); MITK_TEST(GetNumberOfComponentsItkPtype_Success); MITK_TEST(GetBitsPerComponentItkPtype_Success); MITK_TEST(ConstructorWithBrackets_Success); MITK_TEST(InitializeWithEqualSign_Success); MITK_TEST(EqualityOperator_Success); MITK_TEST(NotEqualityOperator_Success); MITK_TEST(GetPixelTypeUnknown_Success); MITK_TEST(SetLengthCorrectly_Success); MITK_TEST(ValueTypeCorresponds_Success); MITK_TEST(ImageTypeTraitInt3D_Success); MITK_TEST(ImageTypeTraitShort2D_Success); MITK_TEST(ImageTypeTraitVectorShort3D_Success); MITK_TEST(ImageTypeTraitItkInt3D_Success); MITK_TEST(ImageTypeTraitItkShort2D_Success); MITK_TEST(ImageTypeTraitItkVectorShort3D_Success); CPPUNIT_TEST_SUITE_END(); private: typedef itk::Image, 3> ItkImageType; typedef itk::Image MyObscureImageType; typedef itk::VectorImage VectorImageType; typedef itk::Image> FixedVectorImageType; public: void setUp() override { } void tearDown() override { } void GetComponentType_Success() { mitk::PixelType ptype = mitk::MakePixelType(); MITK_INFO << "m_Ptype = mitk::MakePixelType();"; MITK_INFO << "m_ItkPtype = mitk::MakePixelType();\n with itk::Image, 3> ItkImageType"; CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::IOComponentEnum::INT); } void GetPixelTypeAsString_Success() { mitk::PixelType ptype = mitk::MakePixelType(); MITK_INFO << ptype.GetPixelTypeAsString(); MITK_INFO << typeid(ItkImageType).name(); } void GetBpePtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[m_Ptype] GetBpe()", ptype.GetBpe() == 8 * sizeof(int) * 5); } void GetNumberOfComponentsPtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[ptype]GetNumberOfComponents()", ptype.GetNumberOfComponents() == 5); } void GetBitsPerComponentPtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[ptype]GetBitsPerComponent()", ptype.GetBitsPerComponent() == 8 * sizeof(int)); } void GetBpeItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBpe()", itkPtype.GetBpe() == 8 * sizeof(int) * 5); } void GetNumberOfComponentsItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetNumberOfComponents()", itkPtype.GetNumberOfComponents() == 5); } void GetBitsPerComponentItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBitsPerComponent()", itkPtype.GetBitsPerComponent() == 8 * sizeof(int)); } void ConstructorWithBrackets_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2(ptype); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int)); } void InitializeWithEqualSign_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2 = ptype; CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType()); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int)); } void EqualityOperator_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2 = ptype; CPPUNIT_ASSERT_MESSAGE("operator ==", ptype == ptype2); } void NotEqualityOperator_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype3 = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("operator !=", ptype != ptype3); } void GetPixelTypeUnknown_Success() { // test instantiation mitk::PixelType obscurePixelType = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::IOPixelEnum::UNKNOWNPIXELTYPE); } void SetLengthCorrectly_Success() { mitk::PixelType obscurePixelType = mitk::MakePixelType(); - CPPUNIT_ASSERT_MESSAGE("Lenght was set correctly", obscurePixelType.GetNumberOfComponents() == MyObscurePixelType::Length); + CPPUNIT_ASSERT_MESSAGE("Length was set correctly", obscurePixelType.GetNumberOfComponents() == MyObscurePixelType::Length); } void ValueTypeCorresponds_Success() { mitk::PixelType obscurePixelType = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("ValueType corresponds.", obscurePixelType.GetComponentType() == mitk::MapPixelComponentType::value); } void ImageTypeTraitInt3D_Success() { // test ImageTypeTrait traits class CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid int 3D equals ITK typeid", typeid(mitk::ImageTypeTrait::ImageType) == typeid(itk::Image)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait::IsVectorImage == false)); } void ImageTypeTraitShort2D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 2D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::Image, 3>)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait, 3>::IsVectorImage == false)); } void ImageTypeTraitVectorShort3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 3D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::VectorImage)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait, 3>::IsVectorImage == true)); } void ImageTypeTraitItkInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK int 3D equals ITK typeid", typeid(mitk::ImageTypeTrait>::ImageType) == typeid(itk::Image)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait>::IsVectorImage == false)); } void ImageTypeTraitItkShort2D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 2D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>>::ImageType) == typeid(itk::Image, 3>)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait, 3>>::IsVectorImage == false)); } void ImageTypeTraitItkVectorShort3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 3D equals ITK typeid", typeid(mitk::ImageTypeTrait>::ImageType) == typeid(itk::VectorImage)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait>::IsVectorImage == true)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPixelType) diff --git a/Modules/Core/test/mitkPlaneGeometryTest.cpp b/Modules/Core/test/mitkPlaneGeometryTest.cpp index bf61cc4606..376771613d 100644 --- a/Modules/Core/test/mitkPlaneGeometryTest.cpp +++ b/Modules/Core/test/mitkPlaneGeometryTest.cpp @@ -1,1093 +1,1093 @@ /*============================================================================ 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 "mitkAffineTransform3D.h" #include "mitkBaseGeometry.h" #include "mitkGeometry3D.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkSlicedGeometry3D.h" #include "mitkThinPlateSplineCurvedGeometry.h" #include #include #include #include #include #include #include #include static const mitk::ScalarType testEps = 1E-9; // the epsilon used in this test == at least float precision. class mitkPlaneGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPlaneGeometryTestSuite); MITK_TEST(TestInitializeStandardPlane); MITK_TEST(TestProjectPointOntoPlane); MITK_TEST(TestPlaneGeometryCloning); MITK_TEST(TestInheritance); MITK_TEST(TestSetExtendInMM); MITK_TEST(TestRotate); MITK_TEST(TestClone); MITK_TEST(TestPlaneComparison); MITK_TEST(TestAxialInitialization); MITK_TEST(TestCoronalInitialization); MITK_TEST(TestSagittalInitialization); MITK_TEST(TestLefthandedCoordinateSystem); MITK_TEST(TestDominantAxesError); MITK_TEST(TestCheckRotationMatrix); // Currently commented out, see See bug 15990 // MITK_TEST(testPlaneGeometryInitializeOrder); MITK_TEST(TestIntersectionPoint); MITK_TEST(TestCase1210); CPPUNIT_TEST_SUITE_END(); private: // private test members that are initialized by setUp() mitk::PlaneGeometry::Pointer planegeometry; mitk::Point3D origin; mitk::Vector3D right, bottom, normal, spacing; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; public: void setUp() override { planegeometry = mitk::PlaneGeometry::New(); width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 1.0; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right, bottom); planegeometry->SetOrigin(origin); planegeometry->SetSpacing(spacing); } void tearDown() override {} // This test verifies inheritance behaviour, this test will fail if the behaviour changes in the future void TestInheritance() { mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::Geometry3D::Pointer g3d = dynamic_cast(plane.GetPointer()); CPPUNIT_ASSERT_MESSAGE("Planegeometry should not be castable to Geometry 3D", g3d.IsNull()); mitk::BaseGeometry::Pointer base = dynamic_cast(plane.GetPointer()); CPPUNIT_ASSERT_MESSAGE("Planegeometry should be castable to BaseGeometry", base.IsNotNull()); g3d = mitk::Geometry3D::New(); base = dynamic_cast(g3d.GetPointer()); CPPUNIT_ASSERT_MESSAGE("Geometry3D should be castable to BaseGeometry", base.IsNotNull()); mitk::SlicedGeometry3D::Pointer sliced = mitk::SlicedGeometry3D::New(); g3d = dynamic_cast(sliced.GetPointer()); CPPUNIT_ASSERT_MESSAGE("SlicedGeometry3D should not be castable to Geometry3D", g3d.IsNull()); mitk::ThinPlateSplineCurvedGeometry::Pointer thin = mitk::ThinPlateSplineCurvedGeometry::New(); plane = dynamic_cast(thin.GetPointer()); CPPUNIT_ASSERT_MESSAGE("AbstractTransformGeometry should be castable to PlaneGeometry", plane.IsNotNull()); plane = mitk::PlaneGeometry::New(); mitk::AbstractTransformGeometry::Pointer atg = dynamic_cast(plane.GetPointer()); CPPUNIT_ASSERT_MESSAGE("PlaneGeometry should not be castable to AbstractTransofrmGeometry", atg.IsNull()); } void TestDominantAxesError() { auto image = mitk::IOUtil::Load(GetTestDataFilePath("NotQuiteARotationMatrix.nrrd")); auto matrix = image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().transpose(); std::vector< int > axes = mitk::PlaneGeometry::CalculateDominantAxes(matrix); CPPUNIT_ASSERT_MESSAGE("Domiant axes cannot be determined in this dataset. Output should be default ordering.", axes.at(0)==0 && axes.at(1)==1 && axes.at(2)==2); } void TestCheckRotationMatrix() { auto image = mitk::IOUtil::Load(GetTestDataFilePath("NotQuiteARotationMatrix.nrrd")); bool is_rotation = mitk::PlaneGeometry::CheckRotationMatrix(image->GetGeometry()->GetIndexToWorldTransform(), 1e-8); CPPUNIT_ASSERT_MESSAGE("Since the test data matrix is not quite a rotation matrix, this should be detected.", !is_rotation); } void TestLefthandedCoordinateSystem() { /** * @brief This method tests InitializeStandardPlane() and IndexToWorld() * with a left-handed coordinate orientation or indexToWorldMatrix. * * Of course this test does not use standard Parameters, which are right-handed. * See also discussion of bug #11477: https://phabricator.mitk.org/T11477 */ planegeometry = mitk::PlaneGeometry::New(); width = 100; widthInMM = 5; height = 200; heightInMM = 3; thicknessInMM = 1.0; mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); // This one negative sign results in lefthanded coordinate orientation and det(matrix) < 0. mitk::FillVector3D(normal, 0, 0, -thicknessInMM); mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix; mitk::AffineTransform3D::MatrixType::InternalMatrixType &vnl_matrix = matrix.GetVnlMatrix(); vnl_matrix.set_column(0, right); vnl_matrix.set_column(1, bottom); vnl_matrix.set_column(2, normal); // making sure that we didn't screw up this special test case or else fail deadly: assert(vnl_determinant(vnl_matrix) < 0.0); transform->SetIdentity(); transform->SetMatrix(matrix); planegeometry->InitializeStandardPlane(width, height, transform); // Crux of the matter. CPPUNIT_ASSERT_MESSAGE( "Testing if IndexToWorldMatrix is correct after InitializeStandardPlane( width, height, transform ) ", mitk::MatrixEqualElementWise(planegeometry->GetIndexToWorldTransform()->GetMatrix(), matrix)); mitk::Point3D p_index; p_index[0] = 10.; p_index[1] = 10.; p_index[2] = 0.; mitk::Point3D p_world; mitk::Point3D p_expectedResult; p_expectedResult[0] = 50.; p_expectedResult[1] = 30.; p_expectedResult[2] = 0.; ((mitk::BaseGeometry::Pointer)planegeometry)->IndexToWorld(p_index, p_world); // Crux of the matter. CPPUNIT_ASSERT_MESSAGE("Testing if IndexToWorld(a,b) function works correctly with lefthanded matrix ", mitk::Equal(p_world, p_expectedResult, testEps)); } // See bug 1210 // Test does not use standard Parameters void TestCase1210() { mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, down, spacing; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, 1.015625, 1.015625, 1.1999969482421875); mitk::FillVector3D(down, 1.4012984643248170709237295832899161312802619418765e-45, 0, 0); mitk::FillVector3D(spacing, 0, 1.4713633875410579244699160624544119378442750389703e-43, 9.2806360452222355258639080851310540729807238879469e-32); std::cout << "Testing InitializeStandardPlane(rightVector, downVector, spacing = nullptr): " << std::endl; CPPUNIT_ASSERT_NO_THROW(planegeometry->InitializeStandardPlane(right, down, &spacing)); /* std::cout << "Testing width, height and thickness (in units): "; if((mitk::Equal(planegeometry->GetExtent(0),width)==false) || (mitk::Equal(planegeometry->GetExtent(1),height)==false) || (mitk::Equal(planegeometry->GetExtent(2),1)==false) ) { std::cout<<"[FAILED]"<GetExtentInMM(0),widthInMM)==false) || (mitk::Equal(planegeometry->GetExtentInMM(1),heightInMM)==false) || (mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM)==false) ) { std::cout<<"[FAILED]"< 0. * */ // Test does not use standard Parameters void TestIntersectionPoint() { // init plane with its parameter mitk::PlaneGeometry::Pointer myPlaneGeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; origin[0] = 0.0; origin[1] = 2.0; origin[2] = 0.0; mitk::Vector3D normal; normal[0] = 0.0; normal[1] = 1.0; normal[2] = 0.0; myPlaneGeometry->InitializePlane(origin, normal); // generate points and line for intersection testing // point distance of given line > 1 mitk::Point3D pointP1; pointP1[0] = 2.0; pointP1[1] = 1.0; pointP1[2] = 0.0; mitk::Point3D pointP2; pointP2[0] = 2.0; pointP2[1] = 4.0; pointP2[2] = 0.0; mitk::Vector3D lineDirection; lineDirection[0] = pointP2[0] - pointP1[0]; lineDirection[1] = pointP2[1] - pointP1[1]; lineDirection[2] = pointP2[2] - pointP1[2]; mitk::Line3D xingline(pointP1, lineDirection); mitk::Point3D calcXingPoint; myPlaneGeometry->IntersectionPoint(xingline, calcXingPoint); // point distance of given line < 1 mitk::Point3D pointP3; pointP3[0] = 2.0; pointP3[1] = 2.2; pointP3[2] = 0.0; mitk::Point3D pointP4; pointP4[0] = 2.0; pointP4[1] = 1.7; pointP4[2] = 0.0; mitk::Vector3D lineDirection2; lineDirection2[0] = pointP4[0] - pointP3[0]; lineDirection2[1] = pointP4[1] - pointP3[1]; lineDirection2[2] = pointP4[2] - pointP3[2]; mitk::Line3D xingline2(pointP3, lineDirection2); mitk::Point3D calcXingPoint2; myPlaneGeometry->IntersectionPoint(xingline2, calcXingPoint2); // intersection points must be the same CPPUNIT_ASSERT_MESSAGE("Failed to calculate Intersection Point", calcXingPoint == calcXingPoint2); } /** * @brief This method tests method ProjectPointOntoPlane. * * See also bug #3409. */ // Test does not use standard Parameters void TestProjectPointOntoPlane() { mitk::PlaneGeometry::Pointer myPlaneGeometry = mitk::PlaneGeometry::New(); // create normal mitk::Vector3D normal; normal[0] = 0.0; normal[1] = 0.0; normal[2] = 1.0; // create origin mitk::Point3D origin; origin[0] = -27.582859; origin[1] = 50; origin[2] = 200.27742; // initialize plane geometry myPlaneGeometry->InitializePlane(origin, normal); - // output to descripe the test + // output to describe the test std::cout << "Testing PlaneGeometry according to bug #3409" << std::endl; std::cout << "Our normal is: " << normal << std::endl; std::cout << "So ALL projected points should have exactly the same z-value!" << std::endl; // create a number of points mitk::Point3D myPoints[5]; myPoints[0][0] = -27.582859; myPoints[0][1] = 50.00; myPoints[0][2] = 200.27742; myPoints[1][0] = -26.58662; myPoints[1][1] = 50.00; myPoints[1][2] = 200.19026; myPoints[2][0] = -26.58662; myPoints[2][1] = 50.00; myPoints[2][2] = 200.33124; myPoints[3][0] = 104.58662; myPoints[3][1] = 452.12313; myPoints[3][2] = 866.41236; myPoints[4][0] = -207.58662; myPoints[4][1] = 312.00; myPoints[4][2] = -300.12346; // project points onto plane mitk::Point3D myProjectedPoints[5]; for (unsigned int i = 0; i < 5; ++i) { myProjectedPoints[i] = myPlaneGeometry->ProjectPointOntoPlane(myPoints[i]); } // compare z-values with z-value of plane (should be equal) bool allPointsOnPlane = true; for (auto &myProjectedPoint : myProjectedPoints) { if (fabs(myProjectedPoint[2] - origin[2]) > mitk::sqrteps) { allPointsOnPlane = false; } } CPPUNIT_ASSERT_MESSAGE("All points lie not on the same plane", allPointsOnPlane); } void TestPlaneGeometryCloning() { mitk::PlaneGeometry::Pointer geometry2D = createPlaneGeometry(); try { mitk::PlaneGeometry::Pointer clone = geometry2D->Clone(); itk::Matrix matrix = clone->GetIndexToWorldTransform()->GetMatrix(); CPPUNIT_ASSERT_MESSAGE("Test if matrix element exists...", matrix[0][0] == 31); double origin = geometry2D->GetOrigin()[0]; CPPUNIT_ASSERT_MESSAGE("First Point of origin as expected...", mitk::Equal(origin, 8)); double spacing = geometry2D->GetSpacing()[0]; CPPUNIT_ASSERT_MESSAGE("First Point of spacing as expected...", mitk::Equal(spacing, 31)); } catch (...) { CPPUNIT_FAIL("Error during access on a member of cloned geometry"); } - // direction [row] [coloum] + // direction [row] [column] MITK_TEST_OUTPUT(<< "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same"); } void TestPlaneGeometryInitializeOrder() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); mitk::PlaneGeometry::Pointer geometry2D1 = mitk::PlaneGeometry::New(); geometry2D1->SetIndexToWorldTransform(myTransform); geometry2D1->SetSpacing(mySpacing); geometry2D1->SetOrigin(myOrigin); mitk::PlaneGeometry::Pointer geometry2D2 = mitk::PlaneGeometry::New(); geometry2D2->SetSpacing(mySpacing); geometry2D2->SetOrigin(myOrigin); geometry2D2->SetIndexToWorldTransform(myTransform); mitk::PlaneGeometry::Pointer geometry2D3 = mitk::PlaneGeometry::New(); geometry2D3->SetIndexToWorldTransform(myTransform); geometry2D3->SetSpacing(mySpacing); geometry2D3->SetOrigin(myOrigin); geometry2D3->SetIndexToWorldTransform(myTransform); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 1 matches that of Geometry 2.", mitk::Equal(geometry2D1->GetOrigin(), geometry2D2->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 1 match those of Geometry 3.", mitk::Equal(geometry2D1->GetOrigin(), geometry2D3->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 2 match those of Geometry 3.", mitk::Equal(geometry2D2->GetOrigin(), geometry2D3->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 1 match those of Geometry 2.", mitk::Equal(geometry2D1->GetSpacing(), geometry2D2->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 1 match those of Geometry 3.", mitk::Equal(geometry2D1->GetSpacing(), geometry2D3->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 2 match those of Geometry 3.", mitk::Equal(geometry2D2->GetSpacing(), geometry2D3->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 1 match those of Geometry 2.", compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D2->GetIndexToWorldTransform()->GetMatrix())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 1 match those of Geometry 3.", compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 2 match those of Geometry 3.", compareMatrix(geometry2D2->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix())); } void TestInitializeStandardPlane() { CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: width", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: height", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: depth", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: width in mm", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: heght in mm", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: depth in mm", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorRight", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorBottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorNormal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mitk::Vector3D spacing; thicknessInMM = 1.5; normal.Normalize(); normal *= thicknessInMM; mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector(), &spacing); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: width", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: height", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: depth", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: width in mm", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: height in mm", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: depth in mm", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorRight", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorBottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorNormal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); ; } void TestSetExtendInMM() { normal.Normalize(); normal *= thicknessInMM; planegeometry->SetExtentInMM(2, thicknessInMM); CPPUNIT_ASSERT_MESSAGE("Testing SetExtentInMM(2, ...), querying by GetExtentInMM(2): ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetExtentInMM(2, ...), querying by GetAxisVector(2) and comparing to normal: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); planegeometry->SetOrigin(origin); CPPUNIT_ASSERT_MESSAGE("Testing SetOrigin", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Right", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestRotate() { // Changing the IndexToWorldTransform to a rotated version by SetIndexToWorldTransform() (keep origin): mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = planegeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::VnlVector axis(3); mitk::FillVector3D(axis, 1.0, 1.0, 1.0); axis.normalize(); vnl_quaternion rotation(axis, 0.223); vnlmatrix = rotation.rotation_matrix_transpose() * vnlmatrix; mitk::Matrix3D matrix; matrix = vnlmatrix; transform->SetMatrix(matrix); transform->SetOffset(planegeometry->GetIndexToWorldTransform()->GetOffset()); right.SetVnlVector(rotation.rotation_matrix_transpose() * right.GetVnlVector()); bottom.SetVnlVector(rotation.rotation_matrix_transpose() * bottom.GetVnlVector()); normal.SetVnlVector(rotation.rotation_matrix_transpose() * normal.GetVnlVector()); planegeometry->SetIndexToWorldTransform(transform); // The origin changed,because m_Origin=m_IndexToWorldTransform->GetOffset()+GetAxisVector(2)*0.5 // and the AxisVector changes due to the rotation. In other words: the rotation was done around // the corner of the box, not around the planes origin. Now change it to a rotation around // the origin, simply by re-setting the origin to the original one: planegeometry->SetOrigin(origin); CPPUNIT_ASSERT_MESSAGE("Testing whether SetIndexToWorldTransform kept origin: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); mitk::Point2D point; point[0] = 4; point[1] = 3; mitk::Point2D dummy; planegeometry->WorldToIndex(point, dummy); planegeometry->IndexToWorld(dummy, dummy); CPPUNIT_ASSERT_MESSAGE("Testing consistency of index and world coordinates.", dummy == point); CPPUNIT_ASSERT_MESSAGE("Testing width of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: right ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(0).GetNorm(), planegeometry->GetExtentInMM(0), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(1).GetNorm(), planegeometry->GetExtentInMM(1), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(2).GetNorm(), planegeometry->GetExtentInMM(2), testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); width *= 2; height *= 3; planegeometry->SetSizeInUnits(width, height); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: right ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(0).GetNorm(), planegeometry->GetExtentInMM(0), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(1).GetNorm(), planegeometry->GetExtentInMM(1), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(2).GetNorm(), planegeometry->GetExtentInMM(2), testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestClone() { mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); // Cave: Statement below is negated! CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", !((clonedplanegeometry.IsNull()) || (clonedplanegeometry->GetReferenceCount() != 1))); CPPUNIT_ASSERT_MESSAGE("Testing origin of cloned version: ", mitk::Equal(clonedplanegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing extent (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: right", mitk::Equal(clonedplanegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: bottom", mitk::Equal(clonedplanegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: normal", mitk::Equal(clonedplanegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(clonedplanegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestSagittalInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); mitk::PlaneGeometry::Pointer clonedplanegeometry = planegeometry->Clone(); // Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Sagittal, zPosition = 0, frontside=true): planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Sagittal); mitk::Vector3D newright, newbottom, newnormal; mitk::ScalarType newthicknessInMM; newright = bottom; newthicknessInMM = widthInMM / width * 1.0; // extent in normal direction is 1; newnormal = right; newnormal.Normalize(); newnormal *= newthicknessInMM; newbottom = normal; newbottom.Normalize(); newbottom *= thicknessInMM; CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of sagittally initialized version:", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0, testEps)); // ok, corner was fine, so we can dare to believe the origin is ok. origin = planegeometry->GetOrigin(); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagittally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagittally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagittally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagittally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagittally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagittally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagittally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagittally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagittally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), newnormal, testEps)); mappingTests2D(planegeometry, height, 1, heightInMM, thicknessInMM, origin, newright, newbottom); // set origin back to the one of the axial slice: origin = clonedplanegeometry->GetOrigin(); // Testing backside initialization: InitializeStandardPlane(clonedplanegeometry, planeorientation = Axial, zPosition // = 0, frontside=false, rotated=true): planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Axial, 0, false, true); mitk::Point3D backsideorigin; backsideorigin = origin + clonedplanegeometry->GetAxisVector(1); //+clonedplanegeometry->GetAxisVector(2); CPPUNIT_ASSERT_MESSAGE("Testing origin of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetOrigin(), backsideorigin, testEps)); mitk::Point3D backsidecornerpoint0; backsidecornerpoint0 = cornerpoint0 + clonedplanegeometry->GetAxisVector(1); //+clonedplanegeometry->GetAxisVector(2); CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of sagittally initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), backsidecornerpoint0, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version " "(should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version " "(should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version " "(should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), -bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, backsideorigin, right, -bottom); } void TestCoronalInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); //-------- mitk::Vector3D newright, newbottom, newnormal; mitk::ScalarType newthicknessInMM; // Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Coronal, zPosition = 0, frontside=true) planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Coronal); newright = right; newbottom = normal; newbottom.Normalize(); newbottom *= thicknessInMM; newthicknessInMM = heightInMM / height * 1.0 /*extent in normal direction is 1*/; newnormal = -bottom; newnormal.Normalize(); newnormal *= newthicknessInMM; CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of coronally initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0, testEps)); // ok, corner was fine, so we can dare to believe the origin is ok. origin = planegeometry->GetOrigin(); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in units) of coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -newnormal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, width, 1, widthInMM, thicknessInMM, origin, newright, newbottom); // Changing plane to in-plane unit spacing using SetSizeInUnits: planegeometry->SetSizeInUnits(planegeometry->GetExtentInMM(0), planegeometry->GetExtentInMM(1)); CPPUNIT_ASSERT_MESSAGE("Testing origin of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -newnormal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, widthInMM, thicknessInMM, widthInMM, thicknessInMM, origin, newright, newbottom); // Changing plane to unit spacing also in normal direction using SetExtentInMM(2, 1.0): planegeometry->SetExtentInMM(2, 1.0); newnormal.Normalize(); CPPUNIT_ASSERT_MESSAGE("Testing origin of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), 1.0, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, coronally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -newnormal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, widthInMM, thicknessInMM, widthInMM, thicknessInMM, origin, newright, newbottom); } void TestAxialInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); // Clone, move, rotate and test for 'IsParallel' and 'IsOnPlane' mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", !((clonedplanegeometry.IsNull()) || (clonedplanegeometry->GetReferenceCount() != 1))); std::cout << "Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Axial, zPosition = 0, " "frontside=true): " << std::endl; planegeometry->InitializeStandardPlane(clonedplanegeometry); CPPUNIT_ASSERT_MESSAGE("Testing origin of axially initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin)); CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of axially initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0)); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of axially initialized version (should be same as in mm due to " "unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of axially initialized version (should be same as in mm due to " "unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in units) of axially initialized version (should be same as in mm due " "to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestPlaneComparison() { // Clone, move, rotate and test for 'IsParallel' and 'IsOnPlane' mitk::PlaneGeometry::Pointer clonedplanegeometry2 = dynamic_cast(planegeometry->Clone().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", !((clonedplanegeometry2.IsNull()) || (clonedplanegeometry2->GetReferenceCount() != 1))); - CPPUNIT_ASSERT_MESSAGE("Testing wheter original and clone are at the same position", + CPPUNIT_ASSERT_MESSAGE("Testing whether original and clone are at the same position", clonedplanegeometry2->IsOnPlane(planegeometry.GetPointer())); CPPUNIT_ASSERT_MESSAGE(" Asserting that origin is on the plane cloned plane:", clonedplanegeometry2->IsOnPlane(origin)); mitk::VnlVector newaxis(3); mitk::FillVector3D(newaxis, 1.0, 1.0, 1.0); newaxis.normalize(); vnl_quaternion rotation2(newaxis, 0.0); mitk::Vector3D clonednormal = clonedplanegeometry2->GetNormal(); mitk::Point3D clonedorigin = clonedplanegeometry2->GetOrigin(); auto planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), 180.0); clonedplanegeometry2->ExecuteOperation(planerot); CPPUNIT_ASSERT_MESSAGE(" Asserting that a flipped plane is still on the original plane: ", clonedplanegeometry2->IsOnPlane(planegeometry.GetPointer())); clonedorigin += clonednormal; clonedplanegeometry2->SetOrigin(clonedorigin); CPPUNIT_ASSERT_MESSAGE("Testing if the translated (cloned, flipped) plane is parallel to its origin plane: ", clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), 0.5); clonedplanegeometry2->ExecuteOperation(planerot); - CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as not paralell [rotation +0.5 degree] : ", + CPPUNIT_ASSERT_MESSAGE("Testing if a non-parallel plane gets recognized as not parallel [rotation +0.5 degree] : ", !clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), -1.0); clonedplanegeometry2->ExecuteOperation(planerot); - CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as not paralell [rotation -0.5 degree] : ", + CPPUNIT_ASSERT_MESSAGE("Testing if a non-parallel plane gets recognized as not parallel [rotation -0.5 degree] : ", !clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), 360.5); clonedplanegeometry2->ExecuteOperation(planerot); - CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as paralell [rotation 360 degree] : ", + CPPUNIT_ASSERT_MESSAGE("Testing if a non-parallel plane gets recognized as parallel [rotation 360 degree] : ", clonedplanegeometry2->IsParallel(planegeometry)); } private: // helper Methods for the Tests mitk::PlaneGeometry::Pointer createPlaneGeometry() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); mitk::PlaneGeometry::Pointer geometry2D = mitk::PlaneGeometry::New(); geometry2D->SetIndexToWorldTransform(myTransform); geometry2D->SetSpacing(mySpacing); geometry2D->SetOrigin(myOrigin); return geometry2D; } bool compareMatrix(itk::Matrix left, itk::Matrix right) { bool equal = true; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) equal &= mitk::Equal(left[i][j], right[i][j]); return equal; } /** * This function tests for correct mapping and is called several times from other tests **/ void mappingTests2D(const mitk::PlaneGeometry *planegeometry, const mitk::ScalarType &width, const mitk::ScalarType &height, const mitk::ScalarType &widthInMM, const mitk::ScalarType &heightInMM, const mitk::Point3D &origin, const mitk::Vector3D &right, const mitk::Vector3D &bottom) { std::cout << "Testing mapping Map(pt2d_mm(x=widthInMM/2.3,y=heightInMM/2.5), pt3d_mm) and compare with expected: "; mitk::Point2D pt2d_mm; mitk::Point3D pt3d_mm, expected_pt3d_mm; pt2d_mm[0] = widthInMM / 2.3; pt2d_mm[1] = heightInMM / 2.5; expected_pt3d_mm = origin + right * (pt2d_mm[0] / right.GetNorm()) + bottom * (pt2d_mm[1] / bottom.GetNorm()); planegeometry->Map(pt2d_mm, pt3d_mm); CPPUNIT_ASSERT_MESSAGE( "Testing mapping Map(pt2d_mm(x=widthInMM/2.3,y=heightInMM/2.5), pt3d_mm) and compare with expected", mitk::Equal(pt3d_mm, expected_pt3d_mm, testEps)); std::cout << "Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected: "; mitk::Point2D testpt2d_mm; planegeometry->Map(pt3d_mm, testpt2d_mm); std::cout << std::setprecision(12) << "Expected pt2d_mm " << pt2d_mm << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_mm " << testpt2d_mm << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10 * mitk::eps << std::endl; // This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected", mitk::Equal(pt2d_mm, testpt2d_mm, 10 * mitk::eps)); std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; mitk::Point2D pt2d_units; pt2d_units[0] = width / 2.0; pt2d_units[1] = height / 2.0; pt2d_mm[0] = widthInMM / 2.0; pt2d_mm[1] = heightInMM / 2.0; planegeometry->IndexToWorld(pt2d_units, testpt2d_mm); std::cout << std::setprecision(12) << "Expected pt2d_mm " << pt2d_mm << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_mm " << testpt2d_mm << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10 * mitk::eps << std::endl; // This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: ", mitk::Equal(pt2d_mm, testpt2d_mm, 10 * mitk::eps)); std::cout << "Testing WorldToIndex(pt2d_mm, pt2d_units) and compare with expected: "; mitk::Point2D testpt2d_units; planegeometry->WorldToIndex(pt2d_mm, testpt2d_units); std::cout << std::setprecision(12) << "Expected pt2d_units " << pt2d_units << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_units " << testpt2d_units << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10 * mitk::eps << std::endl; // This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing WorldToIndex(pt2d_mm, pt2d_units) and compare with expected:", mitk::Equal(pt2d_units, testpt2d_units, 10 * mitk::eps)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPlaneGeometry) diff --git a/Modules/Core/test/mitkPointSetEqualTest.cpp b/Modules/Core/test/mitkPointSetEqualTest.cpp index d00871cfd4..c03b5a4acf 100644 --- a/Modules/Core/test/mitkPointSetEqualTest.cpp +++ b/Modules/Core/test/mitkPointSetEqualTest.cpp @@ -1,102 +1,102 @@ /*============================================================================ 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 "mitkPointSet.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" /** * @brief mitkPointSetEqualTestSuite A test class for Equal methods in mitk::PointSet. */ class mitkPointSetEqualTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPointSetEqualTestSuite); MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentGeometries_ReturnsFalse); MITK_TEST(Equal_DifferentNumberOfPoints_ReturnsFalse); MITK_TEST(Equal_DifferentPoints_ReturnsFalse); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ mitk::PointSet::Pointer m_PointSet; mitk::PointSet::Pointer m_AnotherPointSet; public: /** -* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_PointSet = mitk::PointSet::New(); m_AnotherPointSet = m_PointSet->Clone(); } void tearDown() override { m_PointSet = nullptr; m_AnotherPointSet = nullptr; } void Equal_CloneAndOriginal_ReturnsTrue() { mitk::PointSet::Pointer newPointSet = mitk::PointSet::New(); MITK_ASSERT_EQUAL(newPointSet, newPointSet->Clone(), "A clone should be equal to its original."); } void Equal_DifferentGeometries_ReturnsFalse() { mitk::Point3D origin; origin[0] = 0.0; origin[1] = 0.0; origin[2] = 1.0 + 2 * mitk::eps; m_AnotherPointSet->GetGeometry()->SetOrigin(origin); MITK_ASSERT_NOT_EQUAL(m_PointSet, m_AnotherPointSet, "Origin was modified. Result should be false."); } void Equal_DifferentNumberOfPoints_ReturnsFalse() { mitk::Point3D tmpPoint; tmpPoint[0] = 1.0; tmpPoint[1] = 1.0; tmpPoint[2] = 1.0; m_PointSet->InsertPoint(1, tmpPoint); m_PointSet->InsertPoint(2, tmpPoint); m_AnotherPointSet->InsertPoint(1, tmpPoint); MITK_ASSERT_NOT_EQUAL( m_PointSet, m_AnotherPointSet, "One pointset has two points the other has one. Result should be false."); } void Equal_DifferentPoints_ReturnsFalse() { mitk::Point3D tmpPoint; tmpPoint[0] = 1.0; tmpPoint[1] = 1.0; tmpPoint[2] = 1.0; m_PointSet->InsertPoint(1, tmpPoint); tmpPoint[0] = 1.0 + 2 * mitk::eps; m_AnotherPointSet->InsertPoint(1, tmpPoint); MITK_ASSERT_NOT_EQUAL( m_PointSet, m_AnotherPointSet, "Two pointsets with different points. Result should be false."); } }; MITK_TEST_SUITE_REGISTRATION(mitkPointSetEqual) diff --git a/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp b/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp index 0559a8df0f..0a9a8bc80a 100644 --- a/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp +++ b/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp @@ -1,219 +1,219 @@ /*============================================================================ 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 "mitkPreferenceListReaderOptionsFunctor.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include #include namespace mitk { class TestFileReaderService : public mitk::AbstractFileReader { public: TestFileReaderService(const std::string &description) : AbstractFileReader(CustomMimeType("TestMimeType"), description) { Options options; options["o1"] = 0; this->SetDefaultOptions(options); m_ServiceRegistration = RegisterService(); }; ~TestFileReaderService() override { }; using AbstractFileReader::Read; std::vector> DoRead() override { std::vector> result; return result; }; ConfidenceLevel GetConfidenceLevel() const override { return Supported; }; private: TestFileReaderService * Clone() const override { return new TestFileReaderService(*this); }; us::ServiceRegistration m_ServiceRegistration; }; } // namespace mitk class mitkPreferenceListReaderOptionsFunctorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPreferenceListReaderOptionsFunctorTestSuite); MITK_TEST(UsePreferenceList); MITK_TEST(UseBlackList); MITK_TEST(UseNoList); MITK_TEST(UseBlackAndPreferenceList); MITK_TEST(UseOverlappingBlackAndPreferenceList); MITK_TEST(UsePreferenceListWithInexistantReaders); MITK_TEST(UseAllBlackedList); MITK_TEST(SetOptions); CPPUNIT_TEST_SUITE_END(); private: std::string m_ImagePath; mitk::PreferenceListReaderOptionsFunctor::ListType preference; mitk::PreferenceListReaderOptionsFunctor::ListType black; mitk::PreferenceListReaderOptionsFunctor::ListType emptyList; mitk::TestFileReaderService* m_NormalService; mitk::TestFileReaderService* m_PrefService; mitk::TestFileReaderService* m_BlackService; mitk::CustomMimeType* m_TestMimeType; public: void setUp() override { m_ImagePath = GetTestDataFilePath("BallBinary30x30x30.nrrd"); - preference = { "Prefered Test Service" }; + preference = { "Preferred Test Service" }; black = { "Unwanted Test Service" }; emptyList = {}; m_TestMimeType = new mitk::CustomMimeType("TestMimeType"); m_TestMimeType->AddExtension("nrrd"); m_TestMimeType->SetCategory(mitk::IOMimeTypes::CATEGORY_IMAGES()); m_TestMimeType->SetComment("Test mime type"); us::ModuleContext *context = us::GetModuleContext(); us::ServiceProperties props; props[us::ServiceConstants::SERVICE_RANKING()] = 100; context->RegisterService(m_TestMimeType, props); m_NormalService = new mitk::TestFileReaderService("Normal Test Service"); - m_PrefService = new mitk::TestFileReaderService("Prefered Test Service"); + m_PrefService = new mitk::TestFileReaderService("Preferred Test Service"); m_BlackService = new mitk::TestFileReaderService("Unwanted Test Service"); } void tearDown() override { delete m_PrefService; delete m_BlackService; delete m_NormalService; delete m_TestMimeType; } void UsePreferenceList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, emptyList); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); - CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); + CPPUNIT_ASSERT_EQUAL(std::string("Preferred Test Service"), description); CPPUNIT_ASSERT("0" == info.m_ReaderSelector.GetSelected().GetReader()->GetOptions()["o1"].ToString()); } void UseNoList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(emptyList, emptyList); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("Normal Test Service"), description); } void UseBlackList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(emptyList, black); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT(description != "Unwanted Test Service"); } void UseBlackAndPreferenceList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, black); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); - CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); + CPPUNIT_ASSERT_EQUAL(std::string("Preferred Test Service"), description); } void UseOverlappingBlackAndPreferenceList() { mitk::IOUtil::LoadInfo info(m_ImagePath); - black.push_back("Prefered Test Service"); + black.push_back("Preferred Test Service"); black.push_back("Normal Test Service"); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, black); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("ITK NrrdImageIO"), description); } void UsePreferenceListWithInexistantReaders() { mitk::IOUtil::LoadInfo info(m_ImagePath); preference.push_back("InexistantReader"); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, emptyList); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); - CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); + CPPUNIT_ASSERT_EQUAL(std::string("Preferred Test Service"), description); } void UseAllBlackedList() { mitk::IOUtil::LoadInfo info(m_ImagePath); for (const auto &reader : info.m_ReaderSelector.Get()) { black.push_back(reader.GetDescription()); } mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(emptyList, black); CPPUNIT_ASSERT_THROW(functor(info), mitk::Exception); } void SetOptions() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::IFileReader::Options options; options.insert(std::make_pair("o1", 42)); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, options); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); - CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); + CPPUNIT_ASSERT_EQUAL(std::string("Preferred Test Service"), description); CPPUNIT_ASSERT("42" == info.m_ReaderSelector.GetSelected().GetReader()->GetOption("o1").ToString()); } }; MITK_TEST_SUITE_REGISTRATION(mitkPreferenceListReaderOptionsFunctor) diff --git a/Modules/Core/test/mitkPropertyPersistenceTest.cpp b/Modules/Core/test/mitkPropertyPersistenceTest.cpp index c4b0bc305a..01f2b4413c 100644 --- a/Modules/Core/test/mitkPropertyPersistenceTest.cpp +++ b/Modules/Core/test/mitkPropertyPersistenceTest.cpp @@ -1,309 +1,309 @@ /*============================================================================ 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 "mitkIOMimeTypes.h" #include "mitkPropertyPersistence.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include class mitkPropertyPersistenceTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyPersistenceTestSuite); MITK_TEST(AddInfo); MITK_TEST(GetInfo); MITK_TEST(GetInfo_mime); MITK_TEST(GetInfoByKey); MITK_TEST(HasInfo); MITK_TEST(RemoveAllInfo); MITK_TEST(RemoveInfo); MITK_TEST(RemoveInfo_withMime); CPPUNIT_TEST_SUITE_END(); private: mitk::PropertyPersistenceInfo::Pointer info1; mitk::PropertyPersistenceInfo::Pointer info2; mitk::PropertyPersistenceInfo::Pointer info3; mitk::PropertyPersistenceInfo::Pointer info4; mitk::PropertyPersistenceInfo::Pointer info5; mitk::PropertyPersistenceInfo::Pointer info6; mitk::PropertyPersistenceInfo::Pointer infoX; mitk::PropertyPersistenceInfo::Pointer infoX2; std::string prop1; std::string prop2; std::string prop3; std::string prop4; std::string prop5; std::string prop6; std::string propX; std::string keyX; std::string propXTemplate; std::string keyXTemplate; std::string propX2; mitk::IPropertyPersistence *service; static bool checkExistance(const mitk::PropertyPersistence::InfoResultType &infos, const mitk::PropertyPersistenceInfo *info) { auto predicate = [info](const mitk::PropertyPersistenceInfo::ConstPointer &x) { return infosAreEqual(info, x); }; auto finding = std::find_if(infos.begin(), infos.end(), predicate); bool result = finding != infos.end(); return result; } static bool infosAreEqual(const mitk::PropertyPersistenceInfo *ref, const mitk::PropertyPersistenceInfo *info) { bool result = true; if (!info || !ref) { return false; } result = result && ref->GetName() == info->GetName(); result = result && ref->GetKey() == info->GetKey(); result = result && ref->GetMimeTypeName() == info->GetMimeTypeName(); return result; } public: void setUp() override { service = mitk::CreateTestInstancePropertyPersistence(); prop1 = "prop1"; prop2 = "prop1"; prop3 = "prop1"; prop4 = "prop4"; prop5 = "prop5"; propX = "prop(\\d*)"; keyX = "key(\\d*)"; propXTemplate = "prop$1"; keyXTemplate = "key.$1"; propX2 = "otherprop(\\d*)"; info1 = mitk::PropertyPersistenceInfo::New(); info1->SetNameAndKey(prop1, "key1"); info2 = mitk::PropertyPersistenceInfo::New(prop2, "mime2"); info2->SetNameAndKey(prop2, "key2"); info3 = mitk::PropertyPersistenceInfo::New(prop3, "mime3"); info3->SetNameAndKey(prop3, "key3"); info4 = mitk::PropertyPersistenceInfo::New(prop4, "mime2"); info4->SetNameAndKey(prop4, "key2"); info5 = mitk::PropertyPersistenceInfo::New(prop5, "mime5"); info5->SetNameAndKey(prop5, "key5"); infoX = mitk::PropertyPersistenceInfo::New("", "mimeX"); infoX->UseRegEx(propX, propXTemplate, keyX, keyXTemplate); infoX2 = mitk::PropertyPersistenceInfo::New(); infoX2->UseRegEx(propX2, propXTemplate); service->AddInfo(info1, false); service->AddInfo(info2, false); service->AddInfo(info3, false); service->AddInfo(info4, false); service->AddInfo(info5, false); service->AddInfo(infoX, false); service->AddInfo(infoX2, false); } void tearDown() override { delete service; } void AddInfo() { mitk::PropertyPersistenceInfo::Pointer info2_new = mitk::PropertyPersistenceInfo::New(prop2, "otherMime"); info2_new->SetNameAndKey(prop2, "newKey"); mitk::PropertyPersistenceInfo::Pointer info2_otherKey = mitk::PropertyPersistenceInfo::New("prop2", "mime2"); info2_otherKey->SetNameAndKey(prop2, "otherKey"); mitk::PropertyPersistenceInfo::Pointer info_newPropNKey = mitk::PropertyPersistenceInfo::New("", "otherMime"); info_newPropNKey->SetNameAndKey("newProp", "newKey"); CPPUNIT_ASSERT_MESSAGE("Testing addinfo of already existing info (no overwrite) -> no adding", !service->AddInfo(info2_otherKey, false)); CPPUNIT_ASSERT_MESSAGE( "Testing addinfo of already existing info (no overwrite) -> no adding -> key should not be changed.", service->GetInfo(prop2, "mime2", false).front()->GetKey() == "key2"); CPPUNIT_ASSERT_MESSAGE("Testing addinfo of already existing info (overwrite) -> adding", service->AddInfo(info2_otherKey, true)); CPPUNIT_ASSERT_MESSAGE( "Testing addinfo of already existing info (no overwrite) -> adding -> key should be changed.", service->GetInfo(prop2, "mime2", false).front()->GetKey() == "otherKey"); CPPUNIT_ASSERT_MESSAGE("Testing addinfo of info (other mime type; no overwrite) -> adding", service->AddInfo(info2_new, false)); CPPUNIT_ASSERT_MESSAGE("Testing addinfo of info (other mime type; no overwrite) -> adding -> info exists.", !service->GetInfo(prop2, "otherMime", false).empty()); CPPUNIT_ASSERT_MESSAGE("Testing addinfo of info (new prop name; no overwrite) -> adding", service->AddInfo(info_newPropNKey, false)); CPPUNIT_ASSERT_MESSAGE("Testing addinfo of info (new prop name; no overwrite) -> adding ->info exists.", !service->GetInfo("newProp", "otherMime", false).empty()); } void GetInfo() { mitk::PropertyPersistence::InfoResultType infos = service->GetInfo(prop1, false); CPPUNIT_ASSERT(infos.size() == 3); CPPUNIT_ASSERT_MESSAGE("Check expected element 1.", checkExistance(infos, info1)); CPPUNIT_ASSERT_MESSAGE("Check expected element 1.", checkExistance(infos, info2)); CPPUNIT_ASSERT_MESSAGE("Check expected element 1.", checkExistance(infos, info3)); infos = service->GetInfo(prop4, false); CPPUNIT_ASSERT(infos.size() == 1); CPPUNIT_ASSERT_MESSAGE("Check expected element 1.", checkExistance(infos, info4)); infos = service->GetInfo("unkown", false); - CPPUNIT_ASSERT_MESSAGE("Check size of result for unkown prop.", infos.empty()); + CPPUNIT_ASSERT_MESSAGE("Check size of result for unknown prop.", infos.empty()); infos = service->GetInfo("prop101", false); CPPUNIT_ASSERT(infos.empty()); infos = service->GetInfo("prop101", true); CPPUNIT_ASSERT(infos.size() == 1); CPPUNIT_ASSERT_MESSAGE("Check Name of expected element 1.", infos.front()->GetName() == "prop101"); CPPUNIT_ASSERT_MESSAGE("Check Key of expected element 1.", infos.front()->GetKey() == "key.101"); CPPUNIT_ASSERT_MESSAGE("Check MimeTypeName of expected element 1.", infos.front()->GetMimeTypeName() == "mimeX"); } void GetInfoByKey() { mitk::PropertyPersistence::InfoResultType infos = service->GetInfoByKey("key2", false); CPPUNIT_ASSERT(infos.size() == 2); CPPUNIT_ASSERT_MESSAGE("Check expected element 1.", checkExistance(infos, info2)); CPPUNIT_ASSERT_MESSAGE("Check expected element 2.", checkExistance(infos, info4)); infos = service->GetInfoByKey("key5", false); CPPUNIT_ASSERT(infos.size() == 1); CPPUNIT_ASSERT_MESSAGE("Check expected element 1.", checkExistance(infos, info5)); infos = service->GetInfoByKey("unkownkey", false); - CPPUNIT_ASSERT_MESSAGE("Check size of result for unkown key.", infos.empty()); + CPPUNIT_ASSERT_MESSAGE("Check size of result for unknown key.", infos.empty()); infos = service->GetInfoByKey("key101", false); CPPUNIT_ASSERT_MESSAGE("Check size of result for key101.", infos.empty()); infos = service->GetInfoByKey("key101", true); CPPUNIT_ASSERT(infos.size() == 1); CPPUNIT_ASSERT_MESSAGE("Check Name of expected element 1.", infos.front()->GetName() == "prop101"); CPPUNIT_ASSERT_MESSAGE("Check Key of expected element 1.", infos.front()->GetKey() == "key101"); CPPUNIT_ASSERT_MESSAGE("Check MimeTypeName of expected element 1.", infos.front()->GetMimeTypeName() == "mimeX"); } void GetInfo_mime() { mitk::PropertyPersistence::InfoResultType infos = service->GetInfo(prop1, "mime2", false, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (existing element, no wildcard allowed, wildcard exists).", infosAreEqual(info2, infos.front())); infos = service->GetInfo(prop1, "mime2", true, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (existing element, wildcard allowed, wildcard exists).", infosAreEqual(info2, infos.front())); infos = service->GetInfo(prop1, "unknownmime", false, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting element, no wildcard allowed, wildcard exists).", infos.empty()); infos = service->GetInfo(prop1, "unknownmime", true, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting element, wildcard allowed, wildcard exists).", infosAreEqual(info1, infos.front())); infos = service->GetInfo(prop4, "unknownmime", false, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting element, no wildcard allowed).", infos.empty()); infos = service->GetInfo(prop4, "unknownmime", true, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting element, wildcard allowed).", infos.empty()); infos = service->GetInfo("prop101", "unknownmime", false, true); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting mime, no wildcard allowed, regex allowed).", infos.empty()); infos = service->GetInfo("prop101", "mimeX", false, true); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (existing mime, no wildcard allowed, regex allowed).", infos.size() == 1); infos = service->GetInfo("otherprop", "unknownmime", false, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting mime, no wildcard allowed, no regex allowed).", infos.empty()); infos = service->GetInfo("otherprop", "unknownmime", true, false); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting mime, wildcard allowed, no regex allowed).", infos.empty()); infos = service->GetInfo("otherprop", "unknownmime", false, true); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting mime, no wildcard allowed, regex allowed).", infos.empty()); infos = service->GetInfo("otherprop", "unknownmime", true, true); CPPUNIT_ASSERT_MESSAGE("Check GetInfos (inexisting mime, wildcard allowed, regex allowed).", infos.size() == 1); } void HasInfo() { CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop1)", service->HasInfo(prop1)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop4)", service->HasInfo(prop4)); - CPPUNIT_ASSERT_MESSAGE("Check HasInfos (unkown prop)", !service->HasInfo("unkownProp")); + CPPUNIT_ASSERT_MESSAGE("Check HasInfos (unknown prop)", !service->HasInfo("unkownProp")); } void RemoveAllInfo() { CPPUNIT_ASSERT_NO_THROW(service->RemoveAllInfo()); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop1)", !service->HasInfo(prop1)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop4)", !service->HasInfo(prop4)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop5)", !service->HasInfo(prop5)); } void RemoveInfo() { CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo(prop1)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop1)", !service->HasInfo(prop1, false)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop4)", service->HasInfo(prop4, false)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop5)", service->HasInfo(prop5, false)); CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo(prop4)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop4)", !service->HasInfo(prop4, false)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop5)", service->HasInfo(prop5, false)); CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo(prop5)); CPPUNIT_ASSERT_MESSAGE("Check HasInfos (prop5)", !service->HasInfo(prop5, false)); CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo("unknown_prop")); } void RemoveInfo_withMime() { CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo(prop1, "mime2")); CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos if info was removed", service->GetInfo(prop1, "mime2", false).empty()); CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos, if other info of same property name still exists", !service->GetInfo(prop1, "mime3", false).empty()); CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos, if other info of other property name but same mime still exists", !service->GetInfo(prop4, "mime2", false).empty()); CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo(prop5, "wrongMime")); CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos on prop 5 with wrong mime", service->HasInfo(prop5, false)); CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo(prop5, "mime5")); CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos on prop 5", !service->HasInfo(prop5, false)); CPPUNIT_ASSERT_NO_THROW(service->RemoveInfo("unkown_prop", "mime2")); - CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos, if unkown property name but exting mime was used", + CPPUNIT_ASSERT_MESSAGE("Check RemoveInfos, if unknown property name but existing mime was used", service->HasInfo(prop4, false)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyPersistence) diff --git a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp index a4cd0f314c..f52b5a0166 100644 --- a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp +++ b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp @@ -1,1146 +1,1146 @@ /*============================================================================ 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 "mitkPropertyRelationRuleBase.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include /** This class is used to test PropertyRelationRuleBase and get access to internals where needed to test them as well. */ namespace mitk { class TestRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(TestRule, mitk::PropertyRelationRuleBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; RuleIDType GetRuleID() const override { if (m_AbstractMode) { return "TestRule"; } else { return "TestRule_type1"; } }; std::string GetDisplayName() const override { return "TestDisplayName"; } std::string GetSourceRoleName() const override { return "source role"; } std::string GetDestinationRoleName() const override { return "destination role"; } bool m_AbstractMode; bool IsAbstract() const override { return m_AbstractMode; } using Superclass::GetRootKeyPath; using Superclass::Connect; protected: TestRule() : m_AbstractMode(false) { }; ~TestRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; bool IsSupportedRuleID(const RuleIDType& ruleID) const override { if (m_AbstractMode) { return ruleID.find(this->GetRuleID()) == 0; } else { return Superclass::IsSupportedRuleID(ruleID); } }; DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override { DataRelationUIDVectorType result; mitk::BaseProperty::ConstPointer destProp; if (destination != nullptr) { destProp = destination->GetConstProperty("name"); } if (destProp.IsNotNull()) { auto destRegExStr = PropertyKeyPathToPropertyRegEx(Superclass::GetRootKeyPath().AddAnyElement().AddElement("dataHandle")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto keys = source->GetPropertyKeys(); for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto sourceProp = source->GetConstProperty(key); if (sourceProp->GetValueAsString() == destProp->GetValueAsString()) { if (instance_matches.size()>1) { auto finding = std::find(instances_IDLayer.begin(), instances_IDLayer.end(), instance_matches[1]); if (finding == instances_IDLayer.end()) { result.emplace_back(this->GetRelationUIDByInstanceID(source, instance_matches[1]), this->GetRuleIDByInstanceID(source, instance_matches[1])); } } } } } } if (result.empty() && instances_IDLayer.empty()) { auto refNameProp = source->GetConstProperty("referencedName"); if (refNameProp.IsNotNull() && (destProp.IsNull() || destProp->GetValueAsString() == refNameProp->GetValueAsString())) { result.emplace_back(refNameProp->GetValueAsString(),""); } } return result; }; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override { auto destProp = destination->GetConstProperty("name"); source->SetProperty("referencedName", StringProperty::New(destProp->GetValueAsString())); source->SetProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle")), StringProperty::New(destProp->GetValueAsString())); }; void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const override { source->RemoveProperty("referencedName"); try { auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); source->RemoveProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle"))); } catch(...) { } }; }; } // namespace mitk class mitkPropertyRelationRuleBaseTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyRelationRuleBaseTestSuite); MITK_TEST(GetRootKeyPath); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Disconnect_partial_ID); MITK_TEST(Disconnect_partial_Data); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); MITK_TEST(GetRIIPropertyKeyPath); CPPUNIT_TEST_SUITE_END(); private: mitk::TestRule::Pointer rule; mitk::TestRule::Pointer abstractRule; mitk::DataNode::Pointer unRelated; mitk::PointSet::Pointer unRelated_1_data; mitk::DataNode::Pointer source_implicit_1; mitk::DataNode::Pointer source_data_1; mitk::DataNode::Pointer source_idOnly_1; mitk::DataNode::Pointer source_1; mitk::DataNode::Pointer source_multi; mitk::DataNode::Pointer source_otherRule; mitk::DataNode::Pointer source_otherTypeRule; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1; mitk::PointSet::Pointer dest_1_data; mitk::DataNode::Pointer dest_2; mitk::PointSet::Pointer dest_2_data; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } public: void setUp() override { rule = mitk::TestRule::New(); abstractRule = mitk::TestRule::New(); abstractRule->m_AbstractMode = true; unRelated = mitk::DataNode::New(); unRelated->SetName("unRelated"); unRelated_1_data = mitk::PointSet::New(); unRelated->SetData(unRelated_1_data); dest_1 = mitk::DataNode::New(); dest_1->SetName("dest_1"); dest_1_data = mitk::PointSet::New(); dest_1->SetData(dest_1_data); dest_2 = mitk::DataNode::New(); dest_2->SetName("dest_2"); dest_2_data = mitk::PointSet::New(); dest_2->SetData(dest_2_data); source_implicit_1 = mitk::DataNode::New(); source_implicit_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); source_idOnly_1 = mitk::DataNode::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_data_1 = mitk::DataNode::New(); source_data_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.dataHandle"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid10"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); source_1 = mitk::DataNode::New(); source_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid3"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.dataHandle"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid8"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); source_multi = mitk::DataNode::New(); source_multi->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid4"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.dataHandle"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.4.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid5")); name = "MITK.Relations.4.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.4.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid6")); name = "MITK.Relations.2.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("unknown")); name = "MITK.Relations.2.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_otherRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherTypeRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); } void tearDown() override {} void GetRootKeyPath() { mitk::PropertyKeyPath ref; ref.AddElement("MITK").AddElement("Relations"); CPPUNIT_ASSERT(mitk::PropertyRelationRuleBase::GetRootKeyPath() == ref); } void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(rule->IsSource(source_multi)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherTypeRule)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_multi)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherTypeRule)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated)); CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherTypeRule, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2)); CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated)); CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherTypeRule).empty()); auto uids = rule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); uids = rule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); uids = abstractRule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherTypeRule); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherTypeRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherTypeRule, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, dest_1).front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherTypeRule)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_2); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationDetector(source_multi, "uid5"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_implicit_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); // check upgrade of an data connection CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_data_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1_data->GetUID()); // check actualization of an id only connection CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_idOnly_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", + CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.dataHandle"; prop = source_idOnly_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information not stored.", prop->GetValueAsString() == dest_1->GetName()); prop = source_idOnly_1->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == dest_1->GetName()); // check actualization of an existing connection rule->Connect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", + CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.", rule->GetExistingRelations(source_1).size() == 1); // check new connection auto newConnectUID = rule->Connect(source_multi, unRelated); CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated, mitk::PropertyRelationRuleBase::RelationType::Complete)); name = "MITK.Relations.5.dataHandle"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); prop = source_multi->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); name = "MITK.Relations.5.destinationUID"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated_1_data->GetUID()); auto storedRelationUIDs = rule->GetRelationUIDs(source_multi, unRelated); CPPUNIT_ASSERT_MESSAGE( "Relation uid was not stored for given source and destination.", storedRelationUIDs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Incorrect Relation uid was stored.", storedRelationUIDs[0] == newConnectUID); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.",this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_multi, dest_1); CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_otherTypeRule, "1")); } void Disconnect_partial_ID() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::ID), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::ID), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_1, "2")); } void Disconnect_partial_Data() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::Data), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::Data), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); abstractRule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_multi, dest_1); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherTypeRule, "1")); } void GetRIIPropertyKeyPath() { auto path = mitk::PropertyRelationRuleBase::GetRootKeyPath(); mitk::PropertyKeyPath referencePath({ "MITK", "Relations" }); CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("a","1"); referencePath = { "MITK", "Relations", "1", "a" }; CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("", "1"); referencePath = { "MITK", "Relations", "1"}; CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("a", ""); referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("a"); CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath(); referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("relationUID"); CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath("1"); referencePath = { "MITK", "Relations", "1", "relationUID" }; CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath(); referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("ruleID"); CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath("1"); referencePath = { "MITK", "Relations", "1", "ruleID" }; CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath(); referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("destinationUID"); CPPUNIT_ASSERT(referencePath == path); path = mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath("1"); referencePath = { "MITK", "Relations", "1", "destinationUID" }; CPPUNIT_ASSERT(referencePath == path); } }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyRelationRuleBase) diff --git a/Modules/Core/test/mitkRawImageFileReaderTest.cpp b/Modules/Core/test/mitkRawImageFileReaderTest.cpp index 018481299f..845d05e7d9 100644 --- a/Modules/Core/test/mitkRawImageFileReaderTest.cpp +++ b/Modules/Core/test/mitkRawImageFileReaderTest.cpp @@ -1,62 +1,62 @@ /*============================================================================ 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 "mitkIOConstants.h" #include "mitkIOUtil.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" class mitkRawImageFileReaderTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkRawImageFileReaderTestSuite); MITK_TEST(testReadFile); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ std::string m_ImagePath; - std::string m_ImagePathNrrdRef; // corresponding mhd path for comparision + std::string m_ImagePathNrrdRef; // corresponding mhd path for comparison public: /** - * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_ImagePath = GetTestDataFilePath("brain.raw"); m_ImagePathNrrdRef = GetTestDataFilePath("brainHalfSize.nrrd"); // we need half size because the brain file has // spacing 2 and this reader doesn't support spacing } void tearDown() override {} void testReadFile() { mitk::IFileReader::Options options; options[mitk::IOConstants::DIMENSION()] = 3; options[mitk::IOConstants::PIXEL_TYPE()] = mitk::IOConstants::PIXEL_TYPE_FLOAT(); options[mitk::IOConstants::SIZE_X()] = 91; options[mitk::IOConstants::SIZE_Y()] = 109; options[mitk::IOConstants::SIZE_Z()] = 91; options[mitk::IOConstants::ENDIANNESS()] = mitk::IOConstants::ENDIANNESS_LITTLE(); mitk::Image::Pointer readFile = mitk::IOUtil::Load(m_ImagePath, options); CPPUNIT_ASSERT_MESSAGE("Testing reading a raw file.", readFile.IsNotNull()); // compare with the reference image mitk::Image::Pointer compareImage = mitk::IOUtil::Load(m_ImagePathNrrdRef); MITK_ASSERT_EQUAL( compareImage, readFile, "Testing if image is equal to the same image as reference file loaded with mitk"); } }; MITK_TEST_SUITE_REGISTRATION(mitkRawImageFileReader) diff --git a/Modules/Core/test/mitkSTLFileReaderTest.cpp b/Modules/Core/test/mitkSTLFileReaderTest.cpp index aa2823ea1a..de2a938607 100644 --- a/Modules/Core/test/mitkSTLFileReaderTest.cpp +++ b/Modules/Core/test/mitkSTLFileReaderTest.cpp @@ -1,65 +1,65 @@ /*============================================================================ 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 "mitkIOUtil.h" #include "mitkImage.h" #include "mitkSlicedGeometry3D.h" #include "mitkSurface.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include class mitkSTLFileReaderTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSTLFileReaderTestSuite); MITK_TEST(testReadFile); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ std::string m_SurfacePath; public: /** - * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_SurfacePath = GetTestDataFilePath("ball.stl"); } void tearDown() override {} void testReadFile() { // Read STL-Image from file mitk::Surface::Pointer surface = mitk::IOUtil::Load(m_SurfacePath); // check some basic stuff CPPUNIT_ASSERT_MESSAGE("Reader output not nullptr", surface.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("IsInitialized()", surface->IsInitialized()); CPPUNIT_ASSERT_MESSAGE("mitk::Surface::SetVtkPolyData()", (surface->GetVtkPolyData() != nullptr)); CPPUNIT_ASSERT_MESSAGE("Availability of geometry", (surface->GetGeometry() != nullptr)); // use vtk stl reader for reference vtkSmartPointer myVtkSTLReader = vtkSmartPointer::New(); myVtkSTLReader->SetFileName(m_SurfacePath.c_str()); myVtkSTLReader->Update(); vtkSmartPointer myVtkPolyData = myVtkSTLReader->GetOutput(); // vtkPolyData from vtkSTLReader directly int n = myVtkPolyData->GetNumberOfPoints(); // vtkPolyData from mitkSTLFileReader int m = surface->GetVtkPolyData()->GetNumberOfPoints(); CPPUNIT_ASSERT_MESSAGE("Number of Points in VtkPolyData", (n == m)); } }; MITK_TEST_SUITE_REGISTRATION(mitkSTLFileReader) diff --git a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp index 71ac772ba4..c0189e6068 100644 --- a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp +++ b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp @@ -1,973 +1,973 @@ /*============================================================================ 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 "mitkSourceImageRelationRule.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkPropertyNameHelper.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkPropertyNameHelper.h" #include class mitkSourceImageRelationRuleTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSourceImageRelationRuleTestSuite); MITK_TEST(ConstructionAndGetter); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::SourceImageRelationRule::Pointer rule; mitk::SourceImageRelationRule::Pointer abstractRule; mitk::Image::Pointer unRelated; mitk::DataNode::Pointer unRelated_Node; mitk::Image::Pointer source_implicit_1; mitk::DataNode::Pointer source_implicit_1_Node; mitk::Image::Pointer source_Data_1; mitk::DataNode::Pointer source_Data_1_Node; mitk::Image::Pointer source_idOnly_1; mitk::DataNode::Pointer source_idOnly_1_Node; mitk::Image::Pointer source_1; mitk::DataNode::Pointer source_1_Node; mitk::Image::Pointer source_otherRule; mitk::DataNode::Pointer source_otherRule_Node; mitk::Image::Pointer source_otherPurpose; mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1_Node; mitk::Image::Pointer dest_1; mitk::DataNode::Pointer dest_2_Node; mitk::Image::Pointer dest_2; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } std::vector GetReferenceSequenceIndices(const mitk::IPropertyProvider * source, const mitk::IPropertyProvider * destination) const { std::vector result; auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } mitk::PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = source->GetPropertyKeys(); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (*refUIDProp == *destInstanceUIDProp) { mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key); result.push_back(std::to_string(finding.GetNode(2).selection)); } } } return result; }; void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID)); mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID)); mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose)); } bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath)); if (prop->GetValueAsString() != instanceUID) { return false; } mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath)); if (prop->GetValueAsString() != classUID) { return false; } mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); if (prop->GetValueAsString() != purpose) { return false; } return true; } public: void setUp() override { auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018); auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016); rule = mitk::SourceImageRelationRule::New("Test"); abstractRule = mitk::SourceImageRelationRule::New(); unRelated = mitk::Image::New(); unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated")); unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); unRelated_Node = mitk::DataNode::New(); unRelated_Node->SetData(unRelated); dest_1_Node = mitk::DataNode::New(); dest_1_Node->SetName("dest_1"); dest_1 = mitk::Image::New(); dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1")); dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_1_Node->SetData(dest_1); dest_2_Node = mitk::DataNode::New(); dest_2_Node->SetName("dest_2"); dest_2 = mitk::Image::New(); dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2")); dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_2_Node->SetData(dest_2); source_implicit_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Test", 0); source_implicit_1_Node = mitk::DataNode::New(); source_implicit_1_Node->SetData(source_implicit_1); source_idOnly_1 = mitk::Image::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_idOnly_1_Node = mitk::DataNode::New(); source_idOnly_1_Node->SetData(source_idOnly_1); source_Data_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Test", 0); SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.ruleID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); name = "MITK.Relations.2.ruleID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_Data_1_Node = mitk::DataNode::New(); source_Data_1_Node->SetData(source_Data_1); source_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_1, "dest_1", "image", "Test", 0); SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3")); name = "MITK.Relations.1.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8")); name = "MITK.Relations.2.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); source_1_Node = mitk::DataNode::New(); source_1_Node->SetData(source_1); source_otherRule = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherRule_Node = mitk::DataNode::New(); source_otherRule_Node->SetData(source_otherRule); source_otherPurpose = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_otherPurpose_Node = mitk::DataNode::New(); source_otherPurpose_Node->SetData(source_otherPurpose); } void tearDown() override {} void ConstructionAndGetter() { CPPUNIT_ASSERT(abstractRule->IsAbstract()); CPPUNIT_ASSERT(!rule->IsAbstract()); CPPUNIT_ASSERT_EQUAL(abstractRule->GetRuleID(), std::string("SourceImageRelation")); CPPUNIT_ASSERT_EQUAL(abstractRule->GetDisplayName(), std::string("Abstract image to image relation")); CPPUNIT_ASSERT_EQUAL(abstractRule->GetSourceRoleName(), std::string("derived data")); CPPUNIT_ASSERT_EQUAL(abstractRule->GetDestinationRoleName(), std::string("source image")); CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Test")); CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("Test relation")); CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("derived data")); CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image")); rule = mitk::SourceImageRelationRule::New("Test2", "DisplayName2"); CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Test2")); CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("DisplayName2")); CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("derived data")); CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image")); rule = mitk::SourceImageRelationRule::New("Test3", "DisplayName3", "s", "d"); CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Test3")); CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("DisplayName3")); CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("s")); CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("d")); } void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node)); CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1)); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_Data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose_Node)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty()); auto uids = rule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); uids = abstractRule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherPurpose); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherPurpose, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherPurpose_Node)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_implicit_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Test", 0)); // check upgrade and reuse of an Data connection (no new relation should be generated). CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_Data_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(relUID.size() == 1); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Test", 0)); // check actualization of an id only connection rule->Connect(source_idOnly_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", + CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.", rule->GetExistingRelations(source_1).size() == 1); // check actualization of an existing connection rule->Connect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", + CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); // check creation of an new connection rule->Connect(unRelated, dest_1); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.", + CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating existing one.", rule->GetExistingRelations(unRelated).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); + CPPUNIT_ASSERT_MESSAGE("Dicom reference sequence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); // check creation of a 2nd connection of the same purpose rule->Connect(unRelated, dest_2); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.", rule->GetExistingRelations(unRelated).size() == 2); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); name = "MITK.Relations.2.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID()); name = "MITK.Relations.2.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.2.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Test", 1)); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); //check if index correction is correct, when disconnecting rule->Connect(source_1, dest_2); rule->Connect(source_1, unRelated); rule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4")); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Test", 2)); std::string name = "MITK.Relations.4.destinationUID"; auto prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID()); name = "MITK.Relations.4.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2"); rule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1")); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); abstractRule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherPurpose, "1")); } }; MITK_TEST_SUITE_REGISTRATION(mitkSourceImageRelationRule) diff --git a/Modules/Core/test/mitkSurfaceEqualTest.cpp b/Modules/Core/test/mitkSurfaceEqualTest.cpp index 5cfaebe32b..9bfaa33cdb 100644 --- a/Modules/Core/test/mitkSurfaceEqualTest.cpp +++ b/Modules/Core/test/mitkSurfaceEqualTest.cpp @@ -1,188 +1,188 @@ /*============================================================================ 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 "mitkSurface.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include #include /** * @brief mitkPointSetEqualTestSuite A test class for Equal methods in mitk::PointSet. */ class mitkSurfaceEqualTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSurfaceEqualTestSuite); MITK_TEST(Equal_CloneAndOriginalOneTimestep_ReturnsTrue); MITK_TEST(Equal_CloneAndOriginalTwoTimesteps_ReturnsTrue); MITK_TEST(Equal_OneTimeStepVSTwoTimeStep_ReturnsFalse); MITK_TEST(Equal_TwoTimeStepsDifferentPoints_ReturnsFalse); MITK_TEST(Equal_DifferentPoints_ReturnsFalse); MITK_TEST(Equal_SurfaceWithPolygonSurfaceWithPolyLine_ReturnsFalse); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ mitk::Surface::Pointer m_Surface3D; mitk::Surface::Pointer m_Surface3DTwoTimeSteps; vtkSmartPointer m_PointsOne; vtkSmartPointer m_PointsTwo; vtkSmartPointer m_PolygonArrayTwo; vtkSmartPointer m_PolyDataOne; public: /** -* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { // generate two sets of points m_PointsOne = vtkSmartPointer::New(); m_PointsOne->InsertNextPoint(0.0, 0.0, 0.0); m_PointsOne->InsertNextPoint(1.0, 0.0, 0.0); m_PointsOne->InsertNextPoint(0.0, 1.0, 0.0); m_PointsOne->InsertNextPoint(1.0, 1.0, 0.0); m_PointsTwo = vtkSmartPointer::New(); m_PointsTwo->InsertNextPoint(0.0, 0.0, 0.0); m_PointsTwo->InsertNextPoint(0.0, 0.0, 2.0); m_PointsTwo->InsertNextPoint(0.0, 1.0, 0.0); m_PointsTwo->InsertNextPoint(0.0, 1.0, 2.0); // generate two polygons vtkSmartPointer polygonOne = vtkSmartPointer::New(); polygonOne->GetPointIds()->SetNumberOfIds(4); polygonOne->GetPointIds()->SetId(0, 0); polygonOne->GetPointIds()->SetId(1, 1); polygonOne->GetPointIds()->SetId(2, 2); polygonOne->GetPointIds()->SetId(3, 3); vtkSmartPointer polygonTwo = vtkSmartPointer::New(); polygonTwo->GetPointIds()->SetNumberOfIds(4); polygonTwo->GetPointIds()->SetId(0, 3); polygonTwo->GetPointIds()->SetId(1, 2); polygonTwo->GetPointIds()->SetId(2, 0); polygonTwo->GetPointIds()->SetId(3, 1); // generate polydatas vtkSmartPointer polygonArrayOne = vtkSmartPointer::New(); polygonArrayOne->InsertNextCell(polygonOne); m_PolyDataOne = vtkSmartPointer::New(); m_PolyDataOne->SetPoints(m_PointsOne); m_PolyDataOne->SetPolys(polygonArrayOne); m_PolygonArrayTwo = vtkSmartPointer::New(); m_PolygonArrayTwo->InsertNextCell(polygonTwo); vtkSmartPointer polyDataTwo = vtkSmartPointer::New(); polyDataTwo->SetPoints(m_PointsOne); polyDataTwo->SetPolys(m_PolygonArrayTwo); // generate surfaces m_Surface3D = mitk::Surface::New(); m_Surface3D->SetVtkPolyData(m_PolyDataOne); m_Surface3DTwoTimeSteps = mitk::Surface::New(); m_Surface3DTwoTimeSteps->SetVtkPolyData(m_PolyDataOne, 0); m_Surface3DTwoTimeSteps->SetVtkPolyData(polyDataTwo, 1); } void tearDown() override { m_Surface3D = nullptr; m_Surface3DTwoTimeSteps = nullptr; m_PolyDataOne = nullptr; m_PolygonArrayTwo = nullptr; m_PointsOne = nullptr; m_PointsTwo = nullptr; } void Equal_CloneAndOriginalOneTimestep_ReturnsTrue() { MITK_ASSERT_EQUAL(m_Surface3D, m_Surface3D->Clone(), "A one timestep clone should be equal to its original."); } void Equal_CloneAndOriginalTwoTimesteps_ReturnsTrue() { MITK_ASSERT_EQUAL(m_Surface3DTwoTimeSteps, m_Surface3DTwoTimeSteps->Clone(), "A two timestep clone should be equal to its original."); } void Equal_OneTimeStepVSTwoTimeStep_ReturnsFalse() { MITK_ASSERT_NOT_EQUAL( m_Surface3D, m_Surface3DTwoTimeSteps, "A one timestep and two timestep surface should not be equal."); } void Equal_TwoTimeStepsDifferentPoints_ReturnsFalse() { vtkSmartPointer polyDataDifferentPoints = vtkSmartPointer::New(); polyDataDifferentPoints->SetPoints(m_PointsTwo); polyDataDifferentPoints->SetPolys(m_PolygonArrayTwo); mitk::Surface::Pointer surface3DTwoTimeStepsDifferentPoints = mitk::Surface::New(); surface3DTwoTimeStepsDifferentPoints->SetVtkPolyData(m_PolyDataOne, 0); surface3DTwoTimeStepsDifferentPoints->SetVtkPolyData(polyDataDifferentPoints, 1); // The geometry also changes, because the second pointset has a different geometry/extent. MITK_ASSERT_NOT_EQUAL(surface3DTwoTimeStepsDifferentPoints, m_Surface3DTwoTimeSteps, "A surface with the same timesteps and different points should not be equal."); } void Equal_SurfaceWithPolygonSurfaceWithPolyLine_ReturnsFalse() { // generate a line vtkSmartPointer polyLineOne = vtkSmartPointer::New(); polyLineOne->GetPointIds()->SetNumberOfIds(2); polyLineOne->GetPointIds()->SetId(0, 0); polyLineOne->GetPointIds()->SetId(1, 1); vtkSmartPointer polyLineArrayOne = vtkSmartPointer::New(); polyLineArrayOne->InsertNextCell(polyLineOne); vtkSmartPointer polyDataLine = vtkSmartPointer::New(); polyDataLine->SetPoints(m_PointsOne); polyDataLine->SetLines(polyLineArrayOne); mitk::Surface::Pointer surface3DLine = mitk::Surface::New(); surface3DLine->SetVtkPolyData(polyDataLine); MITK_ASSERT_NOT_EQUAL(m_Surface3D, surface3DLine, "A surface with the same timesteps and points and the same " "number of cells, but different types of cells should not be " "equal."); } void Equal_DifferentPoints_ReturnsFalse() { mitk::Surface::Pointer surfaceWithADifferentPoint = m_Surface3D->Clone(); // modify points. m_Surface3D contains m_PointsOne surfaceWithADifferentPoint->GetVtkPolyData()->SetPoints(m_PointsTwo); MITK_ASSERT_NOT_EQUAL(m_Surface3D, surfaceWithADifferentPoint, "A surface with a single timestep and different points should not be equal."); } }; MITK_TEST_SUITE_REGISTRATION(mitkSurfaceEqual) diff --git a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp index 6229d1b1bf..3b93328eef 100644 --- a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp +++ b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp @@ -1,253 +1,253 @@ /*============================================================================ 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 "mitkSurfaceToImageFilter.h" #include "mitkTestFixture.h" #include class mitkSurfaceToImageFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSurfaceToImageFilterTestSuite); MITK_TEST(test3DSurfaceValidOutput); MITK_TEST(test3DSurfaceCorrect); MITK_TEST(test3DSurfaceIn4DImage); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Surface::Pointer m_Surface; public: /** - * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Surface = mitk::IOUtil::Load(GetTestDataFilePath("ball.stl")); } void tearDown() override {} void test3DSurfaceValidOutput() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); additionalInputImage->Initialize(mitk::MakeScalarPixelType(), *m_Surface->GetTimeGeometry()); // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); surfaceToImageFilter->SetUShortBinaryPixelType(true); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT); } void test3DSurfaceCorrect() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); // todo I don't know if this image is always needed. There is no documentation of the filter. Use git blame and ask // the author. mitk::Image::Pointer additionalInputImage = mitk::Image::New(); auto *dims = new unsigned int[3]; dims[0] = 32; dims[1] = 32; dims[2] = 32; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 3, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); // Arrange the filter // The docu does not really tell if this is always needed. Could we skip SetImage in any case? surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<3> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_OutputCorrect", valuesCorrect == true); } void test3DSurfaceIn4DImage() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); auto *dims = new unsigned int[4]; dims[0] = 32; dims[1] = 32; dims[2] = 32; dims[3] = 2; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 4, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); mitk::Image::Pointer secondStep = additionalInputImage->Clone(); unsigned int size = sizeof(unsigned char); for (unsigned int i = 0; i < secondStep->GetDimension(); ++i) { size *= secondStep->GetDimension(i); } { mitk::ImageWriteAccessor accessor(secondStep); memset(accessor.GetData(), 1, size); } additionalInputImage->GetTimeGeometry()->Expand(2); additionalInputImage->GetGeometry(1)->SetSpacing(secondStep->GetGeometry()->GetSpacing()); additionalInputImage->GetGeometry(1)->SetOrigin(secondStep->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry(1)->SetIndexToWorldTransform( secondStep->GetGeometry()->GetIndexToWorldTransform()); { mitk::ImageReadAccessor readAccess(secondStep); additionalInputImage->SetImportVolume(readAccess.GetData(), 0); additionalInputImage->SetImportVolume(readAccess.GetData(), 1); } // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<4> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); - // Values inside the ball should be 1 hould be 1 + // Values inside the ball should be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); - // Values inside the ball but in the second timestep hould be 0 + // Values inside the ball but in the second timestep should be 0 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_Output4DCorrect", valuesCorrect == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSurfaceToImageFilter) diff --git a/Modules/Core/test/mitkTimeGeometryTest.cpp b/Modules/Core/test/mitkTimeGeometryTest.cpp index 332311a1b7..73abe034fd 100644 --- a/Modules/Core/test/mitkTimeGeometryTest.cpp +++ b/Modules/Core/test/mitkTimeGeometryTest.cpp @@ -1,862 +1,862 @@ /*============================================================================ 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 "mitkGeometry3D.h" #include "mitkTimeGeometry.h" #include "mitkInteractionConst.h" #include "mitkRotationOperation.h" #include #include #include "mitkTestingMacros.h" #include #include #include "mitkImageGenerator.h" #include "mitkPointSet.h" #include #include static const mitk::ScalarType test_eps = 1E-6; /* some reference values in the test seem to have been calculated with float precision. Thus, set this to float precision epsilon.*/ static const mitk::ScalarType test_eps_square = 1E-3; class mitkTimeGeometryTestClass { public: void Translation_Image_MovedOrigin(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { // DimX, DimY, DimZ, mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::BaseGeometry::Pointer geometry = image->GetTimeGeometry()->GetGeometryForTimeStep(0); mitk::Point3D imageOrigin = geometry->GetOrigin(); mitk::Point3D expectedOrigin; expectedOrigin[0] = 0; expectedOrigin[1] = 0; expectedOrigin[2] = 0; MITK_TEST_CONDITION(mitk::Equal(imageOrigin, expectedOrigin), "Original origin match expected origin"); expectedOrigin[0] = 0.325; expectedOrigin[1] = 0.487; expectedOrigin[2] = 0.78; mitk::Vector3D translationVector; translationVector[0] = expectedOrigin[0]; translationVector[1] = expectedOrigin[1]; translationVector[2] = expectedOrigin[2]; for (mitk::TimeStepType timeStep = 0; timeStep < image->GetTimeGeometry()->CountTimeSteps(); ++timeStep) { image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)->Translate(translationVector); } imageOrigin = image->GetGeometry(0)->GetOrigin(); MITK_TEST_CONDITION(mitk::Equal(imageOrigin, expectedOrigin), "Translated origin match expected origin"); expectedOrigin[0] = 2 * translationVector[0]; expectedOrigin[1] = 2 * translationVector[1]; expectedOrigin[2] = 2 * translationVector[2]; for (mitk::TimeStepType timeStep = 0; timeStep < image->GetTimeGeometry()->CountTimeSteps(); ++timeStep) { image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)->Translate(translationVector); } imageOrigin = image->GetGeometry(0)->GetOrigin(); MITK_TEST_CONDITION(mitk::Equal(imageOrigin, expectedOrigin), "Translated origin match expected origin"); } void Rotate_Image_RotatedPoint(mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int /*DimT*/) { mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); // DimX, DimY, DimZ, dataNode->SetData(baseData); ds->Add(dataNode); mitk::BaseGeometry::Pointer geometry = baseData->GetTimeGeometry()->GetGeometryForTimeStep(0); mitk::Point3D expectedPoint; expectedPoint[0] = 3 * 0.5; expectedPoint[1] = 3 * 0.33; expectedPoint[2] = 3 * 0.78; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Index-to-World without rotation as expected "); mitk::Point3D pointOfRotation; pointOfRotation[0] = 0; pointOfRotation[1] = 0; pointOfRotation[2] = 0; mitk::Vector3D vectorOfRotation; vectorOfRotation[0] = 1; vectorOfRotation[1] = 0.5; vectorOfRotation[2] = 0.2; mitk::ScalarType angleOfRotation = 73.0; auto rotation = new mitk::RotationOperation(mitk::OpROTATE, pointOfRotation, vectorOfRotation, angleOfRotation); baseData->GetTimeGeometry()->ExecuteOperation(rotation); delete rotation; expectedPoint[0] = 2.6080379; expectedPoint[1] = -0.75265157; expectedPoint[2] = 1.1564401; baseData->GetGeometry(0)->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Rotation returns expected values "); } void Scale_Image_ScaledPoint(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { // DimX, DimY, DimZ, mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::BaseGeometry::Pointer geometry = image->GetTimeGeometry()->GetGeometryForTimeStep(0); mitk::Point3D expectedPoint; expectedPoint[0] = 3 * 0.5; expectedPoint[1] = 3 * 0.33; expectedPoint[2] = 3 * 0.78; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Index-to-World with old Scaling as expected "); mitk::Vector3D newSpacing; newSpacing[0] = 2; newSpacing[1] = 1.254; newSpacing[2] = 0.224; image->SetSpacing(newSpacing); expectedPoint[0] = 3 * 2; expectedPoint[1] = 3 * 1.254; expectedPoint[2] = 3 * 0.224; image->GetGeometry(0)->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint), "Index-toWorld with new Scaling returns expected values "); } void GetMinimumTimePoint_4DBaseData_Zero(mitk::BaseData *baseData, unsigned int /*DimT*/) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMinimumTimePoint(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, 0), "Returns correct minimum time point "); } void GetMaximumTimePoint_4DBaseData_DimT(mitk::BaseData *baseData, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMaximumTimePoint(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, DimT), "Returns correct maximum time point "); } void CountTimeSteps_Image_ReturnDimT( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimeStepType expectedTimeSteps = geometry->CountTimeSteps(); MITK_TEST_CONDITION(mitk::Equal(expectedTimeSteps, DimT), "Returns correct number of time Steps "); } void GetMinimumTimePoint_3DImage_Min(mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int /*DimT*/) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMinimumTimePoint(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, -std::numeric_limits().max()), "Returns correct minimum time point "); } void GetMaximumTimePoint_3DImage_Max(mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int /*DimT*/) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMaximumTimePoint(); MITK_INFO << expectedTimePoint; MITK_INFO << std::numeric_limits().max(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, std::numeric_limits().max()), "Returns correct maximum time point "); } void GetTimeBounds_4DImage_ZeroAndDimT(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimeBounds expectedTimeBounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[0], 0), "Returns correct minimum time point "); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[1], DimT), "Returns correct maximum time point "); } void GetTimeBounds_3DImage_ZeroAndDimT(mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int /*DimT*/) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimeBounds expectedTimeBounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[0], -std::numeric_limits().max()), "Returns correct minimum time point "); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[1], std::numeric_limits().max()), "Returns correct maximum time point "); } void IsValidTimePoint_ImageValidTimePoint_True( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimePoint(DimT - 1); MITK_TEST_CONDITION(mitk::Equal(isValid, true), "Is valid time Point correct minimum time point "); } void IsValidTimePoint_ImageNegativInvalidTimePoint_False(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); bool isValid = geometry->IsValidTimePoint(-static_cast(DimT)); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void IsValidTimePoint_ImageInvalidTimePoint_False(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); bool isValid = geometry->IsValidTimePoint(DimT + 1); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void IsValidTimeStep_ImageValidTimeStep_True( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimeStep(DimT - 1); MITK_TEST_CONDITION(mitk::Equal(isValid, true), "Is valid time Point correct minimum time point "); } void IsValidTimeStep_ImageNegativInvalidTimeStep_False( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimeStep(-static_cast(DimT)); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void IsValidTimeStep_ImageInvalidTimeStep_False( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimeStep(DimT); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void TimeStepToTimePoint_ImageValidTimeStep_TimePoint( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType timePoint = geometry->TimeStepToTimePoint(DimT - 1); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT - 1), "Calculated right time Point for Time Step "); } void TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType timePoint = geometry->TimeStepToTimePoint(DimT + 1); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT + 1), "Calculated right time Point for invalid Time Step "); } void TimePointToTimeStep_ImageValidTimePoint_TimePoint( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimeStepType timePoint = geometry->TimePointToTimeStep(DimT - 0.5); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT - 1), "Calculated right time step for valid time point"); } void TimePointToTimeStep_4DImageInvalidTimePoint_TimePoint(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimeStepType timePoint = geometry->TimePointToTimeStep(DimT + 1.5); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT + 1), "Calculated right time step for invalid time point"); } void TimePointToTimeStep_4DImageNegativInvalidTimePoint_TimePoint(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimePointType negativTimePoint = (-1.0 * DimT) - 1.5; mitk::TimeStepType timePoint = geometry->TimePointToTimeStep(negativTimePoint); - MITK_TEST_CONDITION(mitk::Equal(timePoint, 0), "Calculated right time step for negativ invalid time point"); + MITK_TEST_CONDITION(mitk::Equal(timePoint, 0), "Calculated right time step for negative invalid time point"); } void GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(mitk::BaseData *baseData, mitk::ScalarType inputX, mitk::ScalarType inputY, mitk::ScalarType inputZ, mitk::ScalarType outputX, mitk::ScalarType outputY, mitk::ScalarType outputZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT - 1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Point3D expectedPoint; expectedPoint[0] = outputX; expectedPoint[1] = outputY; expectedPoint[2] = outputZ; mitk::Point3D originalPoint; originalPoint[0] = inputX; originalPoint[1] = inputY; originalPoint[2] = inputZ; mitk::Point3D worldPoint; geometry3D->IndexToWorld(originalPoint, worldPoint); - MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation match expection. "); + MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation matches expectation. "); } void GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT + 1); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned"); } void GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(mitk::BaseData *baseData, mitk::ScalarType inputX, mitk::ScalarType inputY, mitk::ScalarType inputZ, mitk::ScalarType outputX, mitk::ScalarType outputY, mitk::ScalarType outputZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimePoint(DimT - 0.5); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Point3D expectedPoint; expectedPoint[0] = outputX; expectedPoint[1] = outputY; expectedPoint[2] = outputZ; mitk::Point3D originalPoint; originalPoint[0] = inputX; originalPoint[1] = inputY; originalPoint[2] = inputZ; mitk::Point3D worldPoint; geometry3D->IndexToWorld(originalPoint, worldPoint); - MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation match expection. "); + MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation matches expectation. "); } void GetGeometryForTimePoint_4DImageInvalidTimePoint_NullPointer(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimePoint(DimT + 1); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned with invalid time point"); } void GetGeometryForTimePoint_4DImageNEgativInvalidTimePoint_NullPointer(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimePointType timePoint = (-1.0 * (DimT)) - 1; mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimePoint(timePoint); - MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned with invalid negativ time point"); + MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned with invalid negative time point"); } void GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(mitk::BaseData *baseData, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT - 1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Point3D expectedPoint; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry3D->IndexToWorld(originalPoint, expectedPoint); mitk::Vector3D translationVector; translationVector[0] = 5; translationVector[1] = 8; translationVector[2] = 7; geometry3D->Translate(translationVector); geometry3D = geometry->GetGeometryForTimeStep(DimT - 1); geometry3D->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint), "Geometry transformation not changed. "); } void GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer( mitk::BaseData *baseData, unsigned int /*DimX*/, unsigned int /*DimY*/, unsigned int /*DimZ*/, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT + 1); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned"); } void SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(mitk::BaseData *baseData, mitk::ScalarType scaleX, mitk::ScalarType scaleY, mitk::ScalarType scaleZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT - 1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Vector3D translationVector; translationVector[0] = 5; translationVector[1] = 8; translationVector[2] = 7; geometry3D->Translate(translationVector); geometry->SetTimeStepGeometry(geometry3D, DimT - 1); mitk::Point3D expectedPoint; expectedPoint[0] = 3 * scaleX + 5; expectedPoint[1] = 3 * scaleY + 8; expectedPoint[2] = 3 * scaleZ + 7; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry->GetGeometryForTimeStep(DimT - 1)->IndexToWorld(originalPoint, worldPoint); - MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation match expection. "); + MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation matches expectation. "); } void Expand_BaseDataDoubleSize_SizeChanged(mitk::BaseData *baseData, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - MITK_TEST_CONDITION(geometry->CountTimeSteps() == DimT, "Number of time Steps match expection. "); + MITK_TEST_CONDITION(geometry->CountTimeSteps() == DimT, "Number of time Steps matches expectation. "); geometry->Expand(DimT * 2); - MITK_TEST_CONDITION(geometry->CountTimeSteps() == DimT * 2, "Number of time Steps match expection. "); + MITK_TEST_CONDITION(geometry->CountTimeSteps() == DimT * 2, "Number of time Steps matches expectation. "); mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT * 2 - 1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry is generated. "); } void CheckBounds_BaseData_PointsAsExpected(mitk::BaseData *baseData, mitk::ScalarType minX, mitk::ScalarType minY, mitk::ScalarType minZ, mitk::ScalarType maxX, mitk::ScalarType maxY, mitk::ScalarType maxZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::Point3D expectedPoint; expectedPoint[0] = minX; expectedPoint[1] = minY; expectedPoint[2] = minZ; mitk::Point3D point = geometry->GetCornerPointInWorld(0); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 0 as expected "); point = geometry->GetCornerPointInWorld(true, true, true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 0 as expected "); point = geometry->GetCornerPointInWorld(1); expectedPoint[0] = minX; expectedPoint[1] = minY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "GBounding Point 1 as expected "); point = geometry->GetCornerPointInWorld(true, true, false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 1 as expected "); point = geometry->GetCornerPointInWorld(2); expectedPoint[0] = minX; expectedPoint[1] = maxY; expectedPoint[2] = minZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 2 as expected "); point = geometry->GetCornerPointInWorld(true, false, true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 2 as expected "); point = geometry->GetCornerPointInWorld(3); expectedPoint[0] = minX; expectedPoint[1] = maxY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 3 as expected "); point = geometry->GetCornerPointInWorld(true, false, false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 3 as expected "); point = geometry->GetCornerPointInWorld(4); expectedPoint[0] = maxX; expectedPoint[1] = minY; expectedPoint[2] = minZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 4 as expected "); point = geometry->GetCornerPointInWorld(false, true, true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 4 as expected "); point = geometry->GetCornerPointInWorld(5); expectedPoint[0] = maxX; expectedPoint[1] = minY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 5 as expected "); point = geometry->GetCornerPointInWorld(false, true, false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 5 as expected "); point = geometry->GetCornerPointInWorld(6); expectedPoint[0] = maxX; expectedPoint[1] = maxY; expectedPoint[2] = minZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 6 as expected "); point = geometry->GetCornerPointInWorld(false, false, true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 6 as expected "); point = geometry->GetCornerPointInWorld(7); expectedPoint[0] = maxX; expectedPoint[1] = maxY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 7 as expected "); point = geometry->GetCornerPointInWorld(false, false, false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 7 as expected "); } void CheckLength_BaseData_AsExpected(mitk::BaseData *baseData, double length, double squareLength) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); double dimension = geometry->GetDiagonalLengthInWorld(); MITK_TEST_CONDITION(mitk::Equal(dimension, length, test_eps), "Length as expected "); dimension = geometry->GetDiagonalLength2InWorld(); MITK_TEST_CONDITION(mitk::Equal(dimension, squareLength, test_eps_square), "Square length as expected "); } void CheckPointInside_BaseDataPointInside_True(mitk::BaseData *baseData, mitk::ScalarType pointX, mitk::ScalarType pointY, mitk::ScalarType pointZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::Point3D expectedPoint; expectedPoint[0] = pointX; expectedPoint[1] = pointY; expectedPoint[2] = pointZ; bool isInside = geometry->IsWorldPointInside(expectedPoint); MITK_TEST_CONDITION(isInside, "Point is inside Image..."); } void CheckPointInside_BaseDataPointOutside_False(mitk::BaseData *baseData, mitk::ScalarType pointX, mitk::ScalarType pointY, mitk::ScalarType pointZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::Point3D expectedPoint; expectedPoint[0] = pointX; expectedPoint[1] = pointY; expectedPoint[2] = pointZ; bool isInside = geometry->IsWorldPointInside(expectedPoint); MITK_TEST_CONDITION(!isInside, "Point is outside Image..."); } void CheckBounds_Image_AsSet(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT, 0.5, 0.33, 0.78, 100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::BoundingBox::BoundsArrayType bound = geometry->GetBoundsInWorld(); bool isEqual = true; isEqual = isEqual && mitk::Equal(bound[0], -0.5 * 0.5, test_eps); isEqual = isEqual && mitk::Equal(bound[1], 29.5 * 0.5, test_eps); isEqual = isEqual && mitk::Equal(bound[2], -0.5 * 0.33, test_eps); isEqual = isEqual && mitk::Equal(bound[3], 24.5 * 0.33, test_eps); isEqual = isEqual && mitk::Equal(bound[4], -0.5 * 0.78, test_eps); isEqual = isEqual && mitk::Equal(bound[5], 19.5 * 0.78, test_eps); MITK_TEST_CONDITION(isEqual, "Bounds as precalculated..."); } void CheckBounds_BaseData_AsSet(mitk::BaseData *baseData, mitk::ScalarType minBoundX, mitk::ScalarType maxBoundX, mitk::ScalarType minBoundY, mitk::ScalarType maxBoundY, mitk::ScalarType minBoundZ, mitk::ScalarType maxBoundZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BoundingBox::BoundsArrayType bound = geometry->GetBoundsInWorld(); bool isEqual = true; isEqual = isEqual && mitk::Equal(bound[0], minBoundX); isEqual = isEqual && mitk::Equal(bound[1], maxBoundX); isEqual = isEqual && mitk::Equal(bound[2], minBoundY); isEqual = isEqual && mitk::Equal(bound[3], maxBoundY); isEqual = isEqual && mitk::Equal(bound[4], minBoundZ); isEqual = isEqual && mitk::Equal(bound[5], maxBoundZ); MITK_TEST_CONDITION(isEqual, "Bounds as precalculated..."); } void CheckExtent_BaseData_AsSet(mitk::BaseData *baseData, double extentX, double extentY, double extentZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isEqual = true; isEqual = isEqual && mitk::Equal(geometry->GetExtentInWorld(0), extentX, test_eps); // 30*0.5); isEqual = isEqual && mitk::Equal(geometry->GetExtentInWorld(1), extentY, test_eps); // 25*0.33); isEqual = isEqual && mitk::Equal(geometry->GetExtentInWorld(2), extentZ, test_eps); // 20*0.78); MITK_TEST_CONDITION(isEqual, "Extent as precalculated..."); } mitk::PointSet::Pointer makePointset() { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); mitk::Point3D pointA, pointB, pointC; pointA.Fill(1); pointB.Fill(2); pointC.Fill(3); pointSet->SetPoint(1, pointA); pointSet->SetPoint(2, pointB); pointSet->SetPoint(3, pointC); pointSet->Update(); MITK_INFO << pointSet->GetPoint(0); MITK_INFO << pointSet->GetPoint(1); MITK_INFO << pointSet->GetPoint(2); MITK_INFO << pointSet->GetPoint(3); mitk::PointSet::Pointer pointSet2 = pointSet->Clone(); MITK_INFO << pointSet2->GetPoint(0); MITK_INFO << pointSet2->GetPoint(1); MITK_INFO << pointSet2->GetPoint(2); MITK_INFO << pointSet2->GetPoint(3); return pointSet; } }; int mitkTimeGeometryTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN(mitkTimeGeometryTest); mitkTimeGeometryTestClass testClass; MITK_TEST_OUTPUT(<< "Test for 3D image"); mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(30, 25, 20, 1, 0.5, 0.33, 0.78, 100); testClass.Translation_Image_MovedOrigin(30, 25, 20, 1); testClass.Rotate_Image_RotatedPoint(image->Clone(), 30, 25, 20, 1); testClass.Scale_Image_ScaledPoint(30, 25, 20, 1); testClass.CountTimeSteps_Image_ReturnDimT(image->Clone(), 30, 25, 20, 1); testClass.GetMinimumTimePoint_3DImage_Min(image->Clone(), 30, 25, 20, 1); testClass.GetMaximumTimePoint_3DImage_Max(image->Clone(), 30, 25, 20, 1); testClass.GetTimeBounds_3DImage_ZeroAndDimT(image->Clone(), 30, 25, 20, 1); testClass.IsValidTimePoint_ImageValidTimePoint_True(image->Clone(), 30, 25, 20, 1); testClass.IsValidTimeStep_ImageValidTimeStep_True(image->Clone(), 30, 25, 20, 1); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(image->Clone(), 30, 25, 20, 1); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(image->Clone(), 30, 25, 20, 1); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(image->Clone(), 30, 25, 20, 1); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(image->Clone(), 30, 25, 20, 1); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(image->Clone(), 30, 25, 20, 1); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry( image->Clone(), 3, 3, 3, 3 * 0.5, 3 * 0.33, 3 * 0.78, 1); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30, 25, 20, 1); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry( image->Clone(), 3, 3, 3, 3 * 0.5, 3 * 0.33, 3 * 0.78, 1); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 1); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30, 25, 20, 1); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 0.5, 0.33, 0.78, 1); testClass.Expand_BaseDataDoubleSize_SizeChanged(image->Clone(), 1); testClass.CheckBounds_BaseData_PointsAsExpected( image->Clone(), -0.5 * 0.5, -0.5 * 0.33, -0.5 * 0.78, 29.5 * 0.5, 24.5 * 0.33, 19.5 * 0.78); testClass.CheckLength_BaseData_AsExpected(image->Clone(), 23.160796233014466, 536.42248214721712); testClass.CheckPointInside_BaseDataPointInside_True(image->Clone(), 10, 5, 5); testClass.CheckPointInside_BaseDataPointOutside_False(image->Clone(), 100, 500, 100); testClass.CheckBounds_Image_AsSet(30, 25, 20, 1); testClass.CheckExtent_BaseData_AsSet(image->Clone(), 30 * 0.5, 25 * 0.33, 20 * 0.78); MITK_TEST_OUTPUT(<< "Test for 2D image"); image = mitk::ImageGenerator::GenerateRandomImage(30, 25, 1, 1, 0.5, 0.33, 0.78, 100); testClass.Translation_Image_MovedOrigin(30, 25, 1, 1); testClass.Rotate_Image_RotatedPoint(image->Clone(), 30, 25, 1, 1); testClass.Scale_Image_ScaledPoint(30, 25, 1, 1); testClass.CountTimeSteps_Image_ReturnDimT(image->Clone(), 30, 25, 1, 1); testClass.GetMinimumTimePoint_3DImage_Min(image->Clone(), 30, 25, 1, 1); testClass.GetMaximumTimePoint_3DImage_Max(image->Clone(), 30, 25, 1, 1); testClass.GetTimeBounds_3DImage_ZeroAndDimT(image->Clone(), 30, 25, 1, 1); testClass.IsValidTimePoint_ImageValidTimePoint_True(image->Clone(), 30, 25, 1, 1); testClass.IsValidTimeStep_ImageValidTimeStep_True(image->Clone(), 30, 25, 1, 1); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(image->Clone(), 30, 25, 1, 1); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(image->Clone(), 30, 25, 1, 1); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(image->Clone(), 30, 25, 1, 1); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(image->Clone(), 30, 25, 1, 1); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(image->Clone(), 30, 25, 1, 1); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry( image->Clone(), 3, 3, 3, 3 * 0.5, 3 * 0.33, 3 * 0.78, 1); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30, 25, 1, 1); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry( image->Clone(), 3, 3, 3, 3 * 0.5, 3 * 0.33, 3 * 0.78, 1); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 1); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30, 25, 1, 1); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 0.5, 0.33, 0.78, 1); testClass.Expand_BaseDataDoubleSize_SizeChanged(image->Clone(), 1); testClass.CheckBounds_BaseData_PointsAsExpected( image->Clone(), -0.5 * 0.5, -0.5 * 0.33, -0.5 * 0.78, 29.5 * 0.5, 24.5 * 0.33, 0.5 * 0.78); testClass.CheckLength_BaseData_AsExpected(image->Clone(), 17.1368287615, 293.6709); testClass.CheckPointInside_BaseDataPointInside_True(image->Clone(), 10, 5, 0); testClass.CheckPointInside_BaseDataPointOutside_False(image->Clone(), 100, 500, 0.5); testClass.CheckExtent_BaseData_AsSet(image->Clone(), 30 * 0.5, 25 * 0.33, 1 * 0.78); MITK_TEST_OUTPUT(<< "Test for 3D+time image"); image = mitk::ImageGenerator::GenerateRandomImage(30, 25, 20, 5, 0.5, 0.33, 0.78, 100); testClass.Translation_Image_MovedOrigin(30, 25, 20, 5); // Test with 3D+t-Image testClass.Rotate_Image_RotatedPoint(image->Clone(), 30, 25, 20, 5); // Test with 3D+t-Image testClass.Scale_Image_ScaledPoint(30, 25, 20, 5); // Test with 3D+t-Image testClass.CountTimeSteps_Image_ReturnDimT(image->Clone(), 30, 25, 20, 5); testClass.GetMinimumTimePoint_4DBaseData_Zero(image->Clone(), 5); testClass.GetMaximumTimePoint_4DBaseData_DimT(image->Clone(), 5); testClass.GetTimeBounds_4DImage_ZeroAndDimT(30, 25, 20, 5); testClass.IsValidTimePoint_ImageValidTimePoint_True(image->Clone(), 30, 25, 20, 5); testClass.IsValidTimePoint_ImageNegativInvalidTimePoint_False(30, 25, 20, 5); testClass.IsValidTimePoint_ImageInvalidTimePoint_False(30, 25, 20, 5); testClass.IsValidTimeStep_ImageValidTimeStep_True(image->Clone(), 30, 25, 20, 5); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(image->Clone(), 30, 25, 20, 5); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(image->Clone(), 30, 25, 20, 5); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(image->Clone(), 30, 25, 20, 5); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(image->Clone(), 30, 25, 20, 5); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(image->Clone(), 30, 25, 20, 5); testClass.TimePointToTimeStep_4DImageInvalidTimePoint_TimePoint(30, 25, 20, 5); testClass.TimePointToTimeStep_4DImageNegativInvalidTimePoint_TimePoint(30, 25, 20, 5); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry( image->Clone(), 3, 3, 3, 3 * 0.5, 3 * 0.33, 3 * 0.78, 5); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30, 25, 20, 5); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry( image->Clone(), 3, 3, 3, 3 * 0.5, 3 * 0.33, 3 * 0.78, 5); testClass.GetGeometryForTimePoint_4DImageInvalidTimePoint_NullPointer(30, 25, 20, 5); testClass.GetGeometryForTimePoint_4DImageNEgativInvalidTimePoint_NullPointer(30, 25, 20, 5); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 5); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30, 25, 20, 5); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 0.5, 0.33, 0.78, 5); testClass.Expand_BaseDataDoubleSize_SizeChanged(image->Clone(), 5); testClass.CheckBounds_BaseData_PointsAsExpected( image->Clone(), -0.5 * 0.5, -0.5 * 0.33, -0.5 * 0.78, 29.5 * 0.5, 24.5 * 0.33, 19.5 * 0.78); testClass.CheckLength_BaseData_AsExpected(image->Clone(), 23.160796233014466, 536.42248214721712); testClass.CheckPointInside_BaseDataPointInside_True(image->Clone(), 10, 5, 5); testClass.CheckPointInside_BaseDataPointOutside_False(image->Clone(), 100, 100, 500); testClass.CheckBounds_Image_AsSet(30, 25, 20, 5); testClass.CheckExtent_BaseData_AsSet(image->Clone(), 30 * 0.5, 25 * 0.33, 20 * 0.78); /* MITK_TEST_OUTPUT(<< "Test for 2D+time image"); testClass.Translation_Image_MovedOrigin(30,25,1 ,5); // Test with 2D+t-Image testClass.Rotate_Image_RotatedPoint(30,25,1 ,5); // Test with 2D+t-Image testClass.Scale_Image_ScaledPoint(30,25,1 ,5); // Test with 2D+t-Image */ mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); mitk::Point3D pointA, pointB, pointC; pointA.Fill(1); pointB.Fill(2); pointC.Fill(3); pointSet->SetPoint(0, pointA); pointSet->SetPoint(1, pointB); pointSet->SetPoint(2, pointC); testClass.CountTimeSteps_Image_ReturnDimT(pointSet->Clone(), 30, 25, 20, 1); // testClass.GetMinimumTimePoint_3DImage_Min(pointSet->Clone(),30,25,20,1); // testClass.GetMaximumTimePoint_3DImage_Max(pointSet->Clone(),30,25,20,1); // testClass.GetTimeBounds_3DImage_ZeroAndDimT(pointSet->Clone(),30,25,20,1); testClass.IsValidTimePoint_ImageValidTimePoint_True(pointSet->Clone(), 30, 25, 20, 1); testClass.IsValidTimeStep_ImageValidTimeStep_True(pointSet->Clone(), 30, 25, 20, 1); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(pointSet->Clone(), 30, 25, 20, 1); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(pointSet->Clone(), 30, 25, 20, 1); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(pointSet->Clone(), 30, 25, 20, 1); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(pointSet->Clone(), 30, 25, 20, 1); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(pointSet->Clone(), 30, 25, 20, 1); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(pointSet->Clone(), 3, 3, 3, 3, 3, 3, 1); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(pointSet->Clone(), 30, 25, 20, 1); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(pointSet->Clone(), 3, 3, 3, 3, 3, 3, 1); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(pointSet->Clone(), 1); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(pointSet->Clone(), 30, 25, 20, 1); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(pointSet->Clone(), 1, 1, 1, 1); testClass.Expand_BaseDataDoubleSize_SizeChanged(pointSet->Clone(), 1); testClass.CheckBounds_BaseData_PointsAsExpected(pointSet->Clone(), 1, 1, 1, 3, 3, 3); testClass.CheckLength_BaseData_AsExpected(pointSet->Clone(), 3.46410161, 12); testClass.CheckPointInside_BaseDataPointInside_True(pointSet->Clone(), 2, 2, 3); testClass.CheckPointInside_BaseDataPointOutside_False(pointSet->Clone(), 4, 5, 1); testClass.CheckBounds_BaseData_AsSet(pointSet->Clone(), 1, 3, 1, 3, 1, 3); testClass.CheckExtent_BaseData_AsSet(pointSet->Clone(), 2, 2, 2); MITK_TEST_END(); return EXIT_SUCCESS; } diff --git a/Modules/Core/test/mitkVerboseLimitedLinearUndoTest.cpp b/Modules/Core/test/mitkVerboseLimitedLinearUndoTest.cpp index f5a00644ec..b768bddb07 100644 --- a/Modules/Core/test/mitkVerboseLimitedLinearUndoTest.cpp +++ b/Modules/Core/test/mitkVerboseLimitedLinearUndoTest.cpp @@ -1,132 +1,132 @@ /*============================================================================ 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 "mitkInteractionConst.h" #include "mitkOperation.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include "mitkTestingMacros.h" #include int g_GlobalCounter = 0; namespace mitk { /** * @brief Class to check that the destructor of object Operation is called and memory freed **/ class TestOperation : public Operation { public: TestOperation(OperationType operationType) : Operation(operationType) { g_GlobalCounter++; }; ~TestOperation() override { g_GlobalCounter--; }; }; } // namespace /** * @brief Test of the LimitedLinearUndo object * * This test was motivated by bug 3248 which had to check memory leakage * while using mitkOperations within the UndoMechanism. * OperationObjects are added to the UndoController and stored within * two lists (m_UndoList and m_RedoList) inside LimitedLinearUndo and * derived from this VerboseLimitedLinearUndo. When using Undo during * runtime operations are moved from UndoList to RedoList. In case of * a new interaction, causing new operations to be stored in the UndoList * the RedoList needs to be cleared. For this, the operations and all * connected objects need to be deleted and memory to be freed. * And this what this test checks! * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ int mitkVerboseLimitedLinearUndoTest(int /* argc */, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("VerboseLimitedLinearUndo") // an UndoController for the management auto myUndoController = new mitk::UndoController(); // set model, even if it is verboseLimitedLinearUndo by default; this already is tested by UndoControllerTest! myUndoController->SwitchUndoModel(mitk::UndoController::VERBOSE_LIMITEDLINEARUNDO); for (int i = 0; i < 2; i++) { auto doOp = new mitk::TestOperation(mitk::OpTEST); auto undoOp = new mitk::TestOperation(mitk::OpTEST); mitk::OperationEvent *operationEvent = new mitk::OperationEvent(nullptr, doOp, undoOp, "Test"); myUndoController->SetOperationEvent(operationEvent); // increase the ID to separate the operationEvents from each other. Otherwise they would be undone all together at // once. mitk::OperationEvent::IncCurrObjectEventId(); } - // now 2 * 2 operation should have been instanciated + // now 2 * 2 operation should have been instantiated MITK_TEST_CONDITION_REQUIRED(g_GlobalCounter == 4, "checking initialization of mitkOperation"); // undo one operation; 1 operationEvent element in undo list, 1 in Redo list myUndoController->Undo(); // sending two new OperationEvents: RedoList should be deleted and memory of operations freed for (int i = 0; i < 2; i++) { auto doOp = new mitk::TestOperation(mitk::OpTEST); auto undoOp = new mitk::TestOperation(mitk::OpTEST); mitk::OperationEvent *operationEvent = new mitk::OperationEvent(nullptr, doOp, undoOp, "Test"); myUndoController->SetOperationEvent(operationEvent); // increase the ID to separate the operationEvents from each other. Otherwise they would be undone all together at // once. mitk::OperationEvent::IncCurrObjectEventId(); } // 2 operations should have been deleted, 4 should have been added MITK_TEST_CONDITION_REQUIRED(g_GlobalCounter == 6, "checking adding of operations"); // two operations to RedoList myUndoController->Undo(); myUndoController->ClearRedoList(); // one operationEvent containing 2 operations should have been deleted MITK_TEST_CONDITION_REQUIRED(g_GlobalCounter == 4, "checking deleting RedoList"); // clear all myUndoController->Clear(); MITK_TEST_CONDITION_REQUIRED(g_GlobalCounter == 0, "checking deleting all operations in UndoModel"); // sending two new OperationEvents for (int i = 0; i < 2; i++) { auto doOp = new mitk::TestOperation(mitk::OpTEST); auto undoOp = new mitk::TestOperation(mitk::OpTEST); mitk::OperationEvent *operationEvent = new mitk::OperationEvent(nullptr, doOp, undoOp, "Test"); myUndoController->SetOperationEvent(operationEvent); // increase the ID to separate the operationEvents from each other. Otherwise they would be undone all together at // once. mitk::OperationEvent::IncCurrObjectEventId(); } MITK_TEST_CONDITION_REQUIRED(g_GlobalCounter == 4, "checking added operations in UndoModel"); delete myUndoController; // after deleting UndoController g_GlobalCounter will still be 4 because m_CurrentUndoModel inside myUndoModel is a // static singleton MITK_TEST_CONDITION_REQUIRED(g_GlobalCounter == 4, "checking singleton UndoModel"); // always end with this! MITK_TEST_END() // operations will be deleted after terminating the application } diff --git a/Plugins/org.mitk.gui.qt.mitkworkbench.intro/resources/WelcomePresentation.html b/Plugins/org.mitk.gui.qt.mitkworkbench.intro/resources/WelcomePresentation.html index a9e59ac637..0f4c56df50 100644 --- a/Plugins/org.mitk.gui.qt.mitkworkbench.intro/resources/WelcomePresentation.html +++ b/Plugins/org.mitk.gui.qt.mitkworkbench.intro/resources/WelcomePresentation.html @@ -1,286 +1,286 @@ May I introduce you to MITK?




May I introduce you to MITK?





For a short explanation of the navigation through the tutorial, press DOWN.

Navigation through the tutorial slides

In the lower right corner you see four arrows for navigation through the slides.




If an arrow turns bold, you can follow it in this direction either using your mouse or keyboard.


Besides the arrow keys you can also use the space bar,
to navigate you through all slides.

When you press "Esc", you will see an overview about all the available slides.

Welcome Page


The first window you will see, is the
Welcome Page!


You can always reopen it by clicking on
"Help" and then "Welcome".
To return there from this presentation
click the MITK logo in the lower left corner.




-

Here you can find links to further on-line tutorials and related documentation.

+

Here you can find links to further on-line tutorials and related documentation.


Window overview


On the upper border you find the Toolbar
with shortcuts to all important functions.


Your images and results appear
in the Display area in 2D and 3D views.





Tools are called Plugins in MITK. After opening they appear to the sides of the display area by default.

Toolbar

You can access the main functions with the buttons on the left side of the toolbar.

  • Open File
  • Save/Close Project
  • Undo/Redo
  • Image Navigator
  • ...

On the right side you find the available Plugins for working on your data.

    Plugins:

  • Clipping Plane
  • Segmentation Utilities
  • Movie Maker
  • Image Cropper
  • ...

The help button can also be found here.








Data Manager

In the Data Manager you get an overview of all your data.




Besides the image itself, additional objects
such as segmentations, 3D models, ...
are shown




The Image Navigator helps to scroll faster through your scans or to navigate to a certain slice.

Display Area

Three windows of the display area show you slices from the three different anatomical planes.

axial

coronal

sagittal





On the right side is the level-window
for adjusting the displayed
grey level range of the image.




The fourth window shows you the 3D view.

3D

Display Windows

A crosshair is shown in each display window to facilitate the navigation and slice selection.

On the upper right corner you have more options to customize the view,
for example if you want to rotate the sectional planes.

Here you can also select other layouts of the display windows.








Mouse Navigation

Inside the Display area you can easily navigate through the images with your mouse.

Press left and move the cursor:
Moves the crosshair in all 3 planes.

Scrolling:
Scrolls through the slides.

Press the scroll wheel and move:
Shifts the section you are looking at.



Right-Click and moving:
Zooms in and out.





Thank you for downloading MITK!


Press F1, to get fast access
to the documentation of the Plugin you're currently using!
Try it now inside of the Data Manager Plugin.


Maybe one of the following links could also interest you:

The end!