diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox index ef6f9c5bfa..c66a8971dc 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox @@ -1,31 +1,32 @@ /** \page Concepts MITK Concepts The following items describe some issues about MITK on a more abstract level. -# \subpage CodingPage "Coding Concepts" -# \ref CodingPageGeneral -# \ref CodingPageStyle -# \ref CodingPageMITKMacros -# \subpage MicroServices_Overview -# Data Concepts -# \subpage BasicDataTypesPage -# \subpage DataManagementPage -# \subpage ReaderWriterPage -# \subpage MitkImagePage -# \subpage MITKSegmentationTaskListsPage + -# \subpage MITKROIPage -# \subpage PropertiesPage -# \subpage GeometryOverviewPage -# \subpage PipelineingConceptPage -# \subpage AnnotationPage -# \subpage PersistenceConceptPage -# \subpage SelectionConceptPage -# \subpage QVTKRendering -# Interaction -# \subpage DataInteractionPage -# \subpage InteractionPage -# \subpage LoggingPage -# \subpage ExceptionPage -# \subpage ModularizationPage "Modularization Concept" -# \ref ModularizationPageOverview */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md new file mode 100644 index 0000000000..2dac2c9742 --- /dev/null +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md @@ -0,0 +1,154 @@ +# MITK ROI {#MITKROIPage} + +[TOC] + +## Disclaimer + +Until the MITK ROI file format is going to be officially announced in a 2024 release of MITK, the file format must be considered experimental and is prone to change without any prior warning. + +## Overview + +MITK ROI is a JSON-based file format defining a collection of region of interests (ROIs). + +ROIs must have an ID (unsigned integer) and their shape is currently considered to be an axis-aligned bounding box. +Its bounds are defined by minimum and maximum index coordinates, typically relative to an image. +Custom properties of various known types can be optionally attached to a ROI. +A few of these properties are used by MITK to define the appearance of a rendered ROI, for example: + + - "color" (mitk::ColorProperty): Color/RGB triplet of the rendered ROI (default: white \[1.0, 1.0, 1.0\]) + - "opacity" (mitk::FloatProperty): Opacity of the rendered ROI (default: 100% \[1.0\]) + - "lineWidth" (mitk::FloatProperty): Line width of the egdes of the rendered ROI (default: 1px \[1.0\]) + +ROIs can be optionally time-resolved and define both coordinates and properties per time step, allowing for a dynamic appearance, position, and size over time. + +ROIs also display a caption at their bottom-left corner (supporting multiple lines), that can be set once per MITK ROI file for all contained ROIs. +Placeholders enclosed by braces in the caption are substituted by their corresponding ROI property values at runtime. +The default caption is "{name} ({ID})", where ``{ID}`` is a special placeholder for the ID of a ROI (technically not a ROI property), and ``{name}`` refers to the ROI property "name" (typically an mitk::StringProperty). + +Last but not least the reference (image) geometry of the ROIs in an MITK ROI file must be specified to be able to map all index coordinates to actual world coordinates. +A geometry is defined by an origin, the pixel/voxel spacing, a size, and optionally the number of time steps in case of a time-resolved MITK ROI file. + +## File format + +As all features are explained in the overview above, the JSON-based file format is defined here by two examples with minimal additional notes: one example for a static MITK ROI file and one example for a time-resolved MITK ROI file. + +### Static MITK ROI file + +This example contains two ROIs for detected tumors in an image with certain confidence. +Names and confidence values will be displayed in separate lines for each ROI. + +~~~{.json} +{ + "FileFormat": "MITK ROI", + "Version": 1, + "Name": "Static example", + "Caption": "{name}\nConfidence: {confidence}", + "Geometry": { + "Origin": [0, 0, 0], + "Spacing": [1, 1, 3], + "Size": [256, 256, 49] + }, + "ROIs": [ + { + "ID": 0, + "Min": [4, 4, 1], + "Max": [124, 124, 31], + "Properties": { + "StringProperty": { + "name": "tumor", + "comment": "Detected a tumor with 95% confidence.", + "note": "Properties are grouped by their type to reduce verbosity." + }, + "ColorProperty": { + "color": [0, 1, 0] + }, + "FloatProperty": { + "confidence": 0.95 + } + } + }, + { + "ID": 1, + "Min": [132, 4, 1], + "Max": [252, 60, 15], + "Properties": { + "StringProperty": { + "name": "Another tumor", + "comment": "Maybe another tumor (confidence only 25%)." + }, + "ColorProperty": { + "color": [1, 0, 0] + }, + "FloatProperty": { + "confidence": 0.25 + } + } + } + ] +} + +~~~ + +Further hints: + + - "FileFormat" ("MITK ROI"), "Version" (1), and "Geometry" are mandatory. + - "Name" is optional. If not set, the file name is used by MITK instead. + - ROIs are defined by JSON objects in the "ROIs" JSON array. + - See the derived classes of mitk::BaseProperty for an overview of known property types. + +### Time-resolved MITK ROI file + +This example only contains a single ROI but it is defined for several time steps. +Fallbacks of time step properties to default properties are demonstrated as well. + +~~~{.json} +{ + "FileFormat": "MITK ROI", + "Version": 1, + "Name": "Time-resolved example", + "Geometry": { + "Origin": [0, 0, 0], + "Spacing": [1, 1, 3], + "Size": [256, 256, 49], + "TimeSteps": 3 + }, + "ROIs": [ + { + "ID": 0, + "Properties": { + "ColorProperty": { + "color": [1, 0, 0] + }, + "StringProperty": { + "name": "Color-changing ROI" + } + }, + "TimeSteps": [ + { + "t": 0, + "Min": [4, 4, 1], + "Max": [124, 124, 31] + }, + { + "t": 2, + "Min": [14, 14, 11], + "Max": [121, 121, 28], + "Properties": { + "ColorProperty": { + "color": [0, 1, 0] + } + } + } + ] + } + ] +} +~~~ + +Further hints: + + - The geometry defines 3 time steps. + - The "Properties" directly in the ROI function as fallbacks, if they are not defined for a certain time step. + - Time time step indices "t" are mandatory. The ROI is only present at time steps 0 and 2. + - The ROI is red (fallback) at time step 0 and green at time step 2. + - Its extents are larger at time step 0 than at time step 2. diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md index ddbb5b926b..b070e52c16 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md @@ -1,145 +1,144 @@ # MITK Segmentation Task Lists {#MITKSegmentationTaskListsPage} [TOC] ## Overview MITK Segmentation Task Lists are a JSON-based file format defining a list of segmentation tasks. Segmentation tasks consist at least of a path to a reference image and a unique result path. The result path specifies where the final segmentation of the task is expected to be located once it is done. Optional properties of a segmentation task include a task name and description as well as various degrees of start conditions for the segmentation like a label name, a list of suggested names and colors for new labels, a label set preset, or even a pre-segmentation to begin with. The complete set of properties is specified further below in the file format specification. MITK Segmentation Task Lists must be considered experimental at the moment and are prone to change without any prior warning. -They are currently supported only in the MITK FlowBench application where a dedicated widget for task navigation and management appears if an MITK Segmentation Task List has been opened. ## File format MITK Segmentation Task Lists are JSON files containing a JSON object as root, which must contain the two mandatory properties `FileFormat` and `Version`: ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1 } ~~~ We also recommend to specify an optional `Name` that is used in the application if present instead of the plain filename of the JSON file: ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Name": "My First Task List" } ~~~ ### Tasks The root object must also contain a mandatory `Tasks` array containing JSON objects that specify the individual tasks of the task list. A minimum task object must consist of `Image` and `Result` file paths. `Image` is a path to the reference image for the task and `Result` is a path where the final segmentation is expected to be stored once it is done. Paths can be absolute or relative to the MITK Segmentation Task List file. ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Tasks": [ { "Image": "images/input.nrrd", "Result": "results/output.nrrd" } ] } ~~~ In addition, tasks can define various optional properties that mainly specify the start conditions for a segmentation: - `Name` (*string*): A name for the task. - `Description` (*string*): A short description/definition of the task. - `LabelName` (*string*): The name of the first label in a new segmentation that is created for the task on the fly. - `LabelNameSuggestions` (*file path*): A Label Suggestions JSON file specifying names and optional colors that are suggested to the user for new labels in the segmentation. - `Preset` (*file path*): A Label Set Preset XML file in MITK's .lsetp file format. The preset is applied to a new segmentation that is created for the task on the fly. We recommend to use the Segmentation plugin to create such label set preset files as described in its [user guide](@ref org_mitk_views_segmentationlabelpresets). - `Segmentation` (*file path*): A pre-segmentation that a user can start with or should refine. - `Dynamic` (*boolean*): In case `Image` refers to a dynamic (3d+t) image, specifies whether the segmentation should be static (*false*), i.e. equal for all time steps, or dynamic (*true*), i.e. individual for each time step. ### Task defaults / common properties If a task list contains multiple tasks with common properties, they do not have to be specified for each and every task again and again. Instead, the root object may contain an optional `Defaults` object that is identical in format to the tasks specified above. There is one exception, though: A `Defaults` object must not contain a `Result` file path, since result files of tasks must be distinct by definition. As the name indicates, default properties can still be overridden by individual tasks if they are specified explicitly. ### Example The following example is a complete showcase of the properties and features listed above. It defines four tasks, three of which refer to the same reference image so it is specified as default. Remember that the only task property required to be distinct is `Result` so you are pretty free in your task design. For simplicity, we chose to define tasks around organs for this example and named the tasks accordingly: ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Name": "Example Segmentation Task List", "Defaults": { "Image": "images/Pic3D.nrrd" }, "Tasks": [ { "Name": "Liver", "LabelName": "Liver", "LabelNameSuggestions": "suggestions/label_suggestions.json", "Description": "This task provides an image and label name suggestions for new labels. The segmentation will start with an empty label named Liver.", "Result": "results/liver.nrrd" }, { "Name": "Kidneys", "Description": "This task provides an image and a label set preset that is applied to the new segmentation.", "Preset": "presets/kidneys.lsetp", "Result": "results/kidneys.nrrd" }, { "Name": "Spleen", "Description": "This task provides an image and an initial (pre-)segmentation.", "Segmentation": "segmentations/spleen.nrrd", "Result": "results/spleen.nrrd" }, { "Name": "Surprise", "Description": "And now for something completely different. This task overrides the default Image and starts with an empty static segmentation for a dynamic image.", "Image": "images/US4DCyl.nrrd", "Result": "results/US4DCyl.nrrd", "Dynamic": false } ] } ~~~ ## Label name suggestions The `LabelNameSuggestions` property of segmentation tasks is supposed to reference a JSON file that consists of an array of objects with a mandatory `name` property and an optional `color` property. For example: ~~~{.json} [ { "name": "Abdomen", "color": "red" }, { "name": "Lung", "color": "#00ff00" }, { "name": "Heart" }, { "name": "Aortic Valve", "color": "CornflowerBlue" } ] ~~~ diff --git a/Modules/AppUtil/src/mitkBaseApplication.cpp b/Modules/AppUtil/src/mitkBaseApplication.cpp index 68948a6607..91201a6414 100644 --- a/Modules/AppUtil/src/mitkBaseApplication.cpp +++ b/Modules/AppUtil/src/mitkBaseApplication.cpp @@ -1,902 +1,905 @@ /*============================================================================ 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 namespace { void outputQtMessage(QtMsgType type, const QMessageLogContext&, const QString& msg) { auto message = msg.toStdString(); switch (type) { case QtDebugMsg: MITK_DEBUG << message; break; case QtInfoMsg: MITK_INFO << message; break; case QtWarningMsg: MITK_WARN << message; break; case QtCriticalMsg: MITK_ERROR << message; break; case QtFatalMsg: MITK_ERROR << message; abort(); default: MITK_INFO << message; break; } } } namespace mitk { const QString BaseApplication::ARG_APPLICATION = "BlueBerry.application"; const QString BaseApplication::ARG_CLEAN = "BlueBerry.clean"; const QString BaseApplication::ARG_CONSOLELOG = "BlueBerry.consoleLog"; const QString BaseApplication::ARG_DEBUG = "BlueBerry.debug"; const QString BaseApplication::ARG_FORCE_PLUGIN_INSTALL = "BlueBerry.forcePlugins"; const QString BaseApplication::ARG_HOME = "BlueBerry.home"; const QString BaseApplication::ARG_NEWINSTANCE = "BlueBerry.newInstance"; const QString BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING = "BlueBerry.noLazyRegistryCacheLoading"; const QString BaseApplication::ARG_NO_REGISTRY_CACHE = "BlueBerry.noRegistryCache"; const QString BaseApplication::ARG_PLUGIN_CACHE = "BlueBerry.plugin_cache_dir"; const QString BaseApplication::ARG_PLUGIN_DIRS = "BlueBerry.plugin_dirs"; const QString BaseApplication::ARG_PRELOAD_LIBRARY = "BlueBerry.preloadLibrary"; const QString BaseApplication::ARG_PRODUCT = "BlueBerry.product"; const QString BaseApplication::ARG_PROVISIONING = "BlueBerry.provisioning"; const QString BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE = "BlueBerry.registryMultiLanguage"; const QString BaseApplication::ARG_SPLASH_IMAGE = "BlueBerry.splashscreen"; const QString BaseApplication::ARG_STORAGE_DIR = "BlueBerry.storageDir"; const QString BaseApplication::ARG_XARGS = "xargs"; const QString BaseApplication::ARG_LOG_QT_MESSAGES = "Qt.logMessages"; const QString BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET = "Segmentation.labelSetPreset"; const QString BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS = "Segmentation.labelSuggestions"; const QString BaseApplication::PROP_APPLICATION = "blueberry.application"; const QString BaseApplication::PROP_FORCE_PLUGIN_INSTALL = BaseApplication::ARG_FORCE_PLUGIN_INSTALL; const QString BaseApplication::PROP_NEWINSTANCE = BaseApplication::ARG_NEWINSTANCE; const QString BaseApplication::PROP_NO_LAZY_REGISTRY_CACHE_LOADING = BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING; const QString BaseApplication::PROP_NO_REGISTRY_CACHE = BaseApplication::ARG_NO_REGISTRY_CACHE; const QString BaseApplication::PROP_PRODUCT = "blueberry.product"; const QString BaseApplication::PROP_REGISTRY_MULTI_LANGUAGE = BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE; class SplashCloserCallback : public QRunnable { public: SplashCloserCallback(QSplashScreen* splashscreen) : m_Splashscreen(splashscreen) { } void run() override { this->m_Splashscreen->close(); } private: QSplashScreen *m_Splashscreen; // Owned by BaseApplication::Impl }; struct BaseApplication::Impl { ctkProperties m_FWProps; QCoreApplication *m_QApp; int m_Argc; char **m_Argv; #ifdef Q_OS_MAC std::vector m_Argv_macOS; #endif QString m_AppName; QString m_OrgaName; QString m_OrgaDomain; bool m_SingleMode; bool m_SafeMode; QSplashScreen *m_Splashscreen; SplashCloserCallback *m_SplashscreenClosingCallback; bool m_LogQtMessages; QStringList m_PreloadLibs; QString m_ProvFile; Impl(int argc, char **argv) : m_Argc(argc), m_Argv(argv), #ifdef Q_OS_MAC m_Argv_macOS(), #endif m_SingleMode(false), m_SafeMode(true), m_Splashscreen(nullptr), m_SplashscreenClosingCallback(nullptr), m_LogQtMessages(false) { #ifdef Q_OS_MAC /* On macOS the process serial number is passed as an command line argument (-psn_) in certain circumstances. This option causes a Poco exception. We remove it, if present. */ m_Argv_macOS.reserve(argc + 1); const char psn[] = "-psn"; for (int i = 0; i < argc; ++i) { if (0 == strncmp(argv[i], psn, sizeof(psn) - 1)) continue; m_Argv_macOS.push_back(argv[i]); } m_Argv_macOS.push_back(nullptr); m_Argc = static_cast(m_Argv_macOS.size() - 1); m_Argv = m_Argv_macOS.data(); #endif } ~Impl() { delete m_SplashscreenClosingCallback; delete m_Splashscreen; delete m_QApp; } QVariant getProperty(const QString &property) const { auto iter = m_FWProps.find(property); return m_FWProps.end() != iter ? iter.value() : QVariant(); } void handleBooleanOption(const std::string &name, const std::string &) { if (ARG_LOG_QT_MESSAGES.toStdString() == name) { m_LogQtMessages = true; return; } auto fwKey = QString::fromStdString(name); // Translate some keys to proper framework properties if (ARG_CONSOLELOG == fwKey) fwKey = ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG; // For all other options we use the command line option name as the // framework property key. m_FWProps[fwKey] = true; } void handlePreloadLibraryOption(const std::string &, const std::string &value) { m_PreloadLibs.push_back(QString::fromStdString(value)); } void handleClean(const std::string &, const std::string &) { m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN] = ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT; } void initializeCTKPluginFrameworkProperties(Poco::Util::LayeredConfiguration &configuration) { // Add all configuration key/value pairs as framework properties Poco::Util::LayeredConfiguration::Keys keys; Poco::Util::LayeredConfiguration::Keys keyStack; configuration.keys(keyStack); std::vector keyChain; while (!keyStack.empty()) { const auto currSubKey = keyStack.back(); if (!keyChain.empty() && keyChain.back() == currSubKey) { keyChain.pop_back(); keyStack.pop_back(); continue; } Poco::Util::LayeredConfiguration::Keys subKeys; configuration.keys(currSubKey, subKeys); if (subKeys.empty()) { std::string finalKey; keyStack.pop_back(); for (const auto& key : keyChain) finalKey += key + '.'; finalKey += currSubKey; keys.push_back(finalKey); } else { keyChain.push_back(currSubKey); for (const auto& key : subKeys) keyStack.push_back(key); } } for (const auto& key : keys) { if (configuration.hasProperty(key)) { // .ini and command line options overwrite already inserted keys auto qKey = QString::fromStdString(key); m_FWProps[qKey] = QString::fromStdString(configuration.getString(key)); } } } void parseProvisioningFile(const QString &filePath) { // Skip parsing if the file path is empty if (filePath.isEmpty()) return; auto consoleLog = this->getProperty(ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG).toBool(); // Read initial plugins from a provisioning file QFileInfo provFile(filePath); QStringList pluginsToStart; if (provFile.exists()) { MITK_INFO(consoleLog) << "Using provisioning file: " << qPrintable(provFile.absoluteFilePath()); ProvisioningInfo provInfo(provFile.absoluteFilePath()); // It can still happen that the encoding is not compatible with the fromUtf8 function (i.e. when // manipulating the LANG variable). The QStringList in provInfo is empty then. if (provInfo.getPluginDirs().empty()) { MITK_ERROR << "Cannot search for provisioning file, the retrieved directory list is empty.\n" << "This can happen if there are some special non-ASCII characters in the install path."; } else { for(const auto& pluginPath : provInfo.getPluginDirs()) ctkPluginFrameworkLauncher::addSearchPath(pluginPath); auto pluginUrlsToStart = provInfo.getPluginsToStart(); for (const auto& url : qAsConst(pluginUrlsToStart)) pluginsToStart.push_back(url.toString()); } } else { MITK_INFO(consoleLog) << "Provisionig file does not exist."; } if (!pluginsToStart.isEmpty()) { m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS] = pluginsToStart; // Use transient start with declared activation policy (this helps when the provisioning file // changes and some plug-ins should not be installed in the application any more). ctkPlugin::StartOptions startOptions(ctkPlugin::START_TRANSIENT | ctkPlugin::START_ACTIVATION_POLICY); m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS_START_OPTIONS] = static_cast(startOptions); } } }; BaseApplication::BaseApplication(int argc, char **argv) : Application(), d(new Impl(argc, argv)) { } BaseApplication::~BaseApplication() { delete d; } void BaseApplication::printHelp(const std::string &, const std::string &) { Poco::Util::HelpFormatter help(this->options()); help.setAutoIndent(); help.setCommand(this->commandName()); help.format(std::cout); exit(EXIT_OK); } void BaseApplication::setApplicationName(const QString &name) { if (nullptr != qApp) qApp->setApplicationName(name); d->m_AppName = name; } QString BaseApplication::getApplicationName() const { return nullptr != qApp ? qApp->applicationName() : d->m_AppName; } void BaseApplication::setOrganizationName(const QString &name) { if (nullptr != qApp) qApp->setOrganizationName(name); d->m_OrgaName = name; } QString BaseApplication::getOrganizationName() const { return nullptr != qApp ? qApp->organizationName() : d->m_OrgaName; } void BaseApplication::setOrganizationDomain(const QString &domain) { if (nullptr != qApp) qApp->setOrganizationDomain(domain); d->m_OrgaDomain = domain; } QString BaseApplication::getOrganizationDomain() const { return nullptr != qApp ? qApp->organizationDomain() : d->m_OrgaDomain; } void BaseApplication::setSingleMode(bool singleMode) { if (nullptr != qApp) return; d->m_SingleMode = singleMode; } bool BaseApplication::getSingleMode() const { return d->m_SingleMode; } void BaseApplication::setSafeMode(bool safeMode) { if (nullptr != qApp && nullptr == d->m_QApp) return; d->m_SafeMode = safeMode; nullptr == d->m_QApp && getSingleMode() ? static_cast(d->m_QApp)->setSafeMode(safeMode) : static_cast(d->m_QApp)->setSafeMode(safeMode); } bool BaseApplication::getSafeMode() const { return d->m_SafeMode; } void BaseApplication::setPreloadLibraries(const QStringList &libraryBaseNames) { d->m_PreloadLibs = libraryBaseNames; } QStringList BaseApplication::getPreloadLibraries() const { return d->m_PreloadLibs; } void BaseApplication::setProvisioningFilePath(const QString &filePath) { d->m_ProvFile = filePath; } QString BaseApplication::getProvisioningFilePath() const { auto provFilePath = d->m_ProvFile; // A null QString means look up a default provisioning file if (provFilePath.isNull() && nullptr != qApp) { QFileInfo appFilePath(QCoreApplication::applicationFilePath()); QDir basePath(QCoreApplication::applicationDirPath()); auto provFileName = appFilePath.baseName() + ".provisioning"; QFileInfo provFile(basePath.absoluteFilePath(provFileName)); #ifdef Q_OS_MAC /* * On macOS, if started from the build directory, the .provisioning file is located at: * * The executable path is: * * In this case we have to cdUp threetimes. * * During packaging the MitkWorkbench.provisioning file is placed at the same * level like the executable. Nothing has to be done. */ if (!provFile.exists()) { basePath.cdUp(); basePath.cdUp(); basePath.cdUp(); provFile = basePath.absoluteFilePath(provFileName); } #endif if (provFile.exists()) { provFilePath = provFile.absoluteFilePath(); } #ifdef CMAKE_INTDIR else { basePath.cdUp(); provFile.setFile(basePath.absoluteFilePath(provFileName)); if (provFile.exists()) provFilePath = provFile.absoluteFilePath(); } #endif } return provFilePath; } void BaseApplication::initializeQt() { if (nullptr != qApp) return; #ifdef Q_OS_LINUX qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--single-process"); // See T29332 #endif // If parameters have been set before, we have to store them to hand them // through to the application auto appName = this->getApplicationName(); auto orgName = this->getOrganizationName(); auto orgDomain = this->getOrganizationDomain(); // Create a QCoreApplication instance this->getQApplication(); // Provide parameters to QCoreApplication this->setApplicationName(appName); this->setOrganizationName(orgName); this->setOrganizationDomain(orgDomain); if (d->m_LogQtMessages) qInstallMessageHandler(outputQtMessage); QWebEngineUrlScheme qtHelpScheme("qthelp"); qtHelpScheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::LocalAccessAllowed); QWebEngineUrlScheme::registerScheme(qtHelpScheme); } void BaseApplication::initialize(Poco::Util::Application &self) { // 1. Call the super-class method Poco::Util::Application::initialize(self); // 2. Initialize the Qt framework (by creating a QCoreApplication) this->initializeQt(); // 3. Seed the random number generator, once at startup. QTime time = QTime::currentTime(); qsrand((uint)time.msec()); // 4. Load the "default" configuration, which involves parsing // an optional .ini file and parsing any // command line arguments this->loadConfiguration(); // 5. Add configuration data from the command line and the // optional .ini file as CTK plugin // framework properties. d->initializeCTKPluginFrameworkProperties(this->config()); // 6. Initialize splash screen if an image path is provided // in the .ini file this->initializeSplashScreen(qApp); // 7. Set the custom CTK Plugin Framework storage directory QString storageDir = this->getCTKFrameworkStorageDir(); if (!storageDir.isEmpty()) { d->m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE] = storageDir; // Initialize core service preferences at the exact same location as their predecessor BlueBerry preferences mitk::CoreServicePointer preferencesService(mitk::CoreServices::GetPreferencesService()); preferencesService->InitializeStorage(storageDir.toStdString() + "/data/3/prefs.xml"); } // 8. Set the library search paths and the pre-load library property this->initializeLibraryPaths(); auto preloadLibs = this->getPreloadLibraries(); if (!preloadLibs.isEmpty()) d->m_FWProps[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES] = preloadLibs; // 9. Initialize the CppMicroServices library. // The initializeCppMicroServices() method reuses the // FRAMEWORK_STORAGE property, so we call it after the // getCTKFrameworkStorageDir method. this->initializeCppMicroServices(); // 10. Parse the (optional) provisioning file and set the // correct framework properties. d->parseProvisioningFile(this->getProvisioningFilePath()); // 11. Set the CTK Plugin Framework properties ctkPluginFrameworkLauncher::setFrameworkProperties(d->m_FWProps); } void BaseApplication::uninitialize() { auto pfw = this->getFramework(); if (pfw) { pfw->stop(); // Wait for up to 10 seconds for the CTK plugin framework to stop pfw->waitForStop(10000); } Poco::Util::Application::uninitialize(); } int BaseApplication::getArgc() const { return d->m_Argc; } char **BaseApplication::getArgv() const { return d->m_Argv; } QString BaseApplication::getCTKFrameworkStorageDir() const { QString storageDir; if (this->getSingleMode()) { // This function checks if an instance is already running and either sends a message to // it containing the command line arguments or checks if a new instance was forced by // providing the BlueBerry.newInstance command line argument. In the latter case, a path // to a temporary directory for the new application's storage directory is returned. storageDir = handleNewAppInstance(static_cast(d->m_QApp), d->m_Argc, d->m_Argv, ARG_NEWINSTANCE); } if (storageDir.isEmpty()) { // This is a new instance and no other instance is already running. We specify the // storage directory here (this is the same code as in berryInternalPlatform.cpp) // so that we can re-use the location for the persistent data location of the // the CppMicroServices library. // Append a hash value of the absolute path of the executable to the data location. // This allows to start the same application from different build or install trees. storageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" + this->getOrganizationName() + "/" + this->getApplicationName() + '_'; storageDir += QString::number(qHash(QCoreApplication::applicationDirPath())) + "/"; } return storageDir; } void BaseApplication::initializeCppMicroServices() { auto storageDir = this->getProperty(ctkPluginConstants::FRAMEWORK_STORAGE).toString(); if (!storageDir.isEmpty()) us::ModuleSettings::SetStoragePath((storageDir + "us" + QDir::separator()).toStdString()); } QCoreApplication *BaseApplication::getQApplication() const { if (nullptr == qApp) { + vtkLogger::SetStderrVerbosity(vtkLogger::VERBOSITY_WARNING); + vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(0); auto defaultFormat = QVTKOpenGLNativeWidget::defaultFormat(); defaultFormat.setSamples(0); QSurfaceFormat::setDefaultFormat(defaultFormat); #ifdef Q_OS_OSX QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); #endif QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); d->m_QApp = this->getSingleMode() ? static_cast(new QmitkSingleApplication(d->m_Argc, d->m_Argv, this->getSafeMode())) : static_cast(new QmitkSafeApplication(d->m_Argc, d->m_Argv, this->getSafeMode())); } return qApp; } void BaseApplication::initializeLibraryPaths() { QStringList suffixes; suffixes << "plugins"; #ifdef Q_OS_WINDOWS suffixes << "bin/plugins"; #ifdef CMAKE_INTDIR suffixes << "bin/" CMAKE_INTDIR "/plugins"; #endif #else suffixes << "lib/plugins"; #ifdef CMAKE_INTDIR suffixes << "lib/" CMAKE_INTDIR "/plugins"; #endif #endif #ifdef Q_OS_MAC suffixes << "../../plugins"; #endif // We add a couple of standard library search paths for plug-ins QDir appDir(QCoreApplication::applicationDirPath()); // Walk one directory up and add bin and lib sub-dirs; this might be redundant appDir.cdUp(); for (const auto& suffix : qAsConst(suffixes)) ctkPluginFrameworkLauncher::addSearchPath(appDir.absoluteFilePath(suffix)); } int BaseApplication::main(const std::vector &args) { // Start the plugin framework and all installed plug-ins according to their auto-start setting QStringList arguments; for (auto const &arg : args) arguments.push_back(QString::fromStdString(arg)); if (nullptr != d->m_Splashscreen) { // A splash screen is displayed. Create the closing callback. d->m_SplashscreenClosingCallback = new SplashCloserCallback(d->m_Splashscreen); } return ctkPluginFrameworkLauncher::run(d->m_SplashscreenClosingCallback, QVariant::fromValue(arguments)).toInt(); } void BaseApplication::defineOptions(Poco::Util::OptionSet &options) { Poco::Util::Option helpOption("help", "h", "print this help text"); helpOption.callback(Poco::Util::OptionCallback(this, &BaseApplication::printHelp)); options.addOption(helpOption); Poco::Util::Option newInstanceOption(ARG_NEWINSTANCE.toStdString(), "", "forces a new instance of this application"); newInstanceOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(newInstanceOption); Poco::Util::Option cleanOption(ARG_CLEAN.toStdString(), "", "cleans the plugin cache"); cleanOption.callback(Poco::Util::OptionCallback(d, &Impl::handleClean)); options.addOption(cleanOption); Poco::Util::Option productOption(ARG_PRODUCT.toStdString(), "", "the id of the product to be launched"); productOption.argument("").binding(PROP_PRODUCT.toStdString()); options.addOption(productOption); Poco::Util::Option appOption(ARG_APPLICATION.toStdString(), "", "the id of the application extension to be executed"); appOption.argument("").binding(PROP_APPLICATION.toStdString()); options.addOption(appOption); Poco::Util::Option provOption(ARG_PROVISIONING.toStdString(), "", "the location of a provisioning file"); provOption.argument("").binding(ARG_PROVISIONING.toStdString()); options.addOption(provOption); Poco::Util::Option storageDirOption(ARG_STORAGE_DIR.toStdString(), "", "the location for storing persistent application data"); storageDirOption.argument("").binding(ctkPluginConstants::FRAMEWORK_STORAGE.toStdString()); options.addOption(storageDirOption); Poco::Util::Option consoleLogOption(ARG_CONSOLELOG.toStdString(), "", "log messages to the console"); consoleLogOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(consoleLogOption); Poco::Util::Option debugOption(ARG_DEBUG.toStdString(), "", "enable debug mode"); debugOption.argument("", false).binding(ctkPluginFrameworkLauncher::PROP_DEBUG.toStdString()); options.addOption(debugOption); Poco::Util::Option forcePluginOption(ARG_FORCE_PLUGIN_INSTALL.toStdString(), "", "force installing plug-ins with same symbolic name"); forcePluginOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(forcePluginOption); Poco::Util::Option preloadLibsOption(ARG_PRELOAD_LIBRARY.toStdString(), "", "preload a library"); preloadLibsOption.argument("") .repeatable(true) .callback(Poco::Util::OptionCallback(d, &Impl::handlePreloadLibraryOption)); options.addOption(preloadLibsOption); Poco::Util::Option noRegistryCacheOption(ARG_NO_REGISTRY_CACHE.toStdString(), "", "do not use a cache for the registry"); noRegistryCacheOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(noRegistryCacheOption); Poco::Util::Option noLazyRegistryCacheLoadingOption(ARG_NO_LAZY_REGISTRY_CACHE_LOADING.toStdString(), "", "do not use lazy cache loading for the registry"); noLazyRegistryCacheLoadingOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(noLazyRegistryCacheLoadingOption); Poco::Util::Option registryMultiLanguageOption(ARG_REGISTRY_MULTI_LANGUAGE.toStdString(), "", "enable multi-language support for the registry"); registryMultiLanguageOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(registryMultiLanguageOption); Poco::Util::Option splashScreenOption(ARG_SPLASH_IMAGE.toStdString(), "", "optional picture to use as a splash screen"); splashScreenOption.argument("").binding(ARG_SPLASH_IMAGE.toStdString()); options.addOption(splashScreenOption); Poco::Util::Option xargsOption(ARG_XARGS.toStdString(), "", "Extended argument list"); xargsOption.argument("").binding(ARG_XARGS.toStdString()); options.addOption(xargsOption); Poco::Util::Option logQtMessagesOption(ARG_LOG_QT_MESSAGES.toStdString(), "", "log Qt messages"); logQtMessagesOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(logQtMessagesOption); Poco::Util::Option labelSetPresetOption(ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), "", "use this label set preset for new segmentations"); labelSetPresetOption.argument("").binding(ARG_SEGMENTATION_LABELSET_PRESET.toStdString()); options.addOption(labelSetPresetOption); Poco::Util::Option labelSuggestionsOption(ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "", "use this list of predefined suggestions for segmentation labels"); labelSuggestionsOption.argument("").binding(ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString()); options.addOption(labelSuggestionsOption); Poco::Util::Application::defineOptions(options); } QSharedPointer BaseApplication::getFramework() const { return ctkPluginFrameworkLauncher::getPluginFramework(); } ctkPluginContext *BaseApplication::getFrameworkContext() const { auto framework = getFramework(); return framework ? framework->getPluginContext() : nullptr; } void BaseApplication::initializeSplashScreen(QCoreApplication * application) const { auto pixmapFileNameProp = d->getProperty(ARG_SPLASH_IMAGE); if (!pixmapFileNameProp.isNull()) { auto pixmapFileName = pixmapFileNameProp.toString(); QFileInfo checkFile(pixmapFileName); if (checkFile.exists() && checkFile.isFile()) { QPixmap pixmap(checkFile.absoluteFilePath()); d->m_Splashscreen = new QSplashScreen(pixmap, Qt::WindowStaysOnTopHint); d->m_Splashscreen->show(); application->processEvents(); } } } QHash BaseApplication::getFrameworkProperties() const { return d->m_FWProps; } int BaseApplication::run() { this->init(d->m_Argc, d->m_Argv); return Application::run(); } void BaseApplication::setProperty(const QString &property, const QVariant &value) { d->m_FWProps[property] = value; } QVariant BaseApplication::getProperty(const QString &property) const { return d->getProperty(property); } void BaseApplication::installTranslator(QTranslator* translator) { this->getQApplication()->installTranslator(translator); } bool BaseApplication::isRunning() { auto app = dynamic_cast(this->getQApplication()); if (nullptr != app) app->isRunning(); mitkThrow() << "Method not implemented."; } void BaseApplication::sendMessage(const QByteArray msg) { auto app = dynamic_cast(this->getQApplication()); if (nullptr != app) app->sendMessage(msg); mitkThrow() << "Method not implemented."; } } diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h index f3eebac5e3..1c67809032 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h @@ -1,83 +1,83 @@ /*============================================================================ 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 mitkBoundingShapeVtkMapper2D_h #define mitkBoundingShapeVtkMapper2D_h #include #include #include #include #include #include #include #include -#include +#include namespace mitk { class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper2D final : public VtkMapper { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage() override; bool IsUpdateRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode); vtkSmartPointer m_Actor; vtkSmartPointer m_HandleActor; vtkSmartPointer m_SelectedHandleActor; vtkSmartPointer m_Mapper; vtkSmartPointer m_HandleMapper; vtkSmartPointer m_SelectedHandleMapper; vtkSmartPointer m_Cutter; vtkSmartPointer m_CuttingPlane; unsigned int m_LastSliceNumber; - std::vector> m_Handles; + std::vector> m_Handles; vtkSmartPointer m_PropAssembly; double m_ZoomFactor; private: LocalStorage(const LocalStorage &); LocalStorage &operator=(const LocalStorage &); }; public: static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); mitkClassMacro(BoundingShapeVtkMapper2D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) override; vtkProp *GetVtkProp(BaseRenderer *renderer) override; private: BoundingShapeVtkMapper2D(); ~BoundingShapeVtkMapper2D() override; BoundingShapeVtkMapper2D(const Self &); Self &operator=(const Self &); void GenerateDataForRenderer(BaseRenderer *renderer) override; void Update(mitk::BaseRenderer *renderer) override; class Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h index 4e800576b0..3af3bdfe70 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h @@ -1,56 +1,51 @@ /*============================================================================ 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 mitkBoundingShapeVtkMapper3D_h #define mitkBoundingShapeVtkMapper3D_h #include - #include -#include -#include -#include - namespace mitk { class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper3D : public VtkMapper { public: static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); mitkClassMacro(BoundingShapeVtkMapper3D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) override; void ApplyBoundingShapeProperties(BaseRenderer *renderer, vtkActor *); vtkProp *GetVtkProp(BaseRenderer *renderer) override; // virtual void UpdateVtkTransform(mitk::BaseRenderer* renderer) override; protected: void GenerateDataForRenderer(BaseRenderer *renderer) override; private: BoundingShapeVtkMapper3D(); ~BoundingShapeVtkMapper3D() override; BoundingShapeVtkMapper3D(const Self &); Self &operator=(const Self &); class Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index 9c61cf79ae..27a7210bb2 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,605 +1,605 @@ /*============================================================================ 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 "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usGetModuleContext.h" #include "usModuleRegistry.h" // Properties to allow the user to interact with the base data const char *selectedColorPropertyName = "Bounding Shape.Selected Color"; const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color"; const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID"; const char *boundingShapePropertyName = "Bounding Shape"; namespace mitk { itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent); class BoundingShapeInteractor::Impl { public: Impl() : OriginalInteractionEnabled(false), RotationEnabled(false) { Point3D initialPoint; initialPoint.Fill(0.0); for (int i = 0; i < 6; ++i) Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } ~Impl() {} bool OriginalInteractionEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; std::map DisplayInteractionConfigs; }; } mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl) { } mitk::BoundingShapeInteractor::~BoundingShapeInteractor() { this->RestoreNodeProperties(); delete m_Impl; } void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); // **Function** in the statemachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("deselectHandles", DeselectHandles); CONNECT_FUNCTION("initInteraction", InitInteraction); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("selectHandle", SelectHandle); CONNECT_FUNCTION("scaleObject", ScaleObject); // CONNECT_FUNCTION("rotateObject",RotateObject); } // RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) // void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* // geometry) //{ // mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); // float pointX = 0.0f; // float pointY = 0.0f; // float pointZ = 0.0f; // mitk::Point3D pointOfRotation; // pointOfRotation.Fill(0.0); // this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); // this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); // this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); // pointOfRotation[0] = pointX; // pointOfRotation[1] = pointY; // pointOfRotation[2] = pointZ; // // mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); // // geometry->ExecuteOperation(doOp); // delete doOp; //} void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) { m_Impl->RotationEnabled = rotationEnabled; } void mitk::BoundingShapeInteractor::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode == nullptr) return; // add color properties mitk::ColorProperty::Pointer selectedColor = dynamic_cast(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNull()) newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); if (deselectedColor.IsNull()) - newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); + newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 0.0, 0.0)); newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); newInputNode->SetBoolProperty("pickable", true); mitk::ColorProperty::Pointer initialColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (initialColor.IsNotNull()) { newInputNode->SetColor(initialColor->GetColor()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er) { GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); if (m_Impl->Handles.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->Handles[0].SetPosition(pointLeft); m_Impl->Handles[1].SetPosition(pointRight); m_Impl->Handles[2].SetPosition(pointTop); m_Impl->Handles[3].SetPosition(pointBottom); m_Impl->Handles[4].SetPosition(pointFront); m_Impl->Handles[5].SetPosition(pointBack); // calculate center based on half way of the distance between two opposing cornerpoints center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); } } void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there is another node set, restore it's color if (node == nullptr) return; DataInteractor::SetDataNode(node); // calls DataNodeChanged internally this->DataNodeChanged(); } bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the // center not in the origin) vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer::New(); auto transform = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (geometry->GetExtent(i)); Point3D currentWorldPosition; Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); ScalarType transformedPosition[4]; transformedPosition[0] = currentWorldPosition[0]; transformedPosition[1] = currentWorldPosition[1]; transformedPosition[2] = currentWorldPosition[2]; transformedPosition[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); // check if the world point is within bounds bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); return isInside; } bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent) { Point3D boundingBoxCenter; HandlePositionChanged(interactionEvent, boundingBoxCenter); const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point2D displayCenterPoint; // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep int timeStep = 0; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize; unsigned int handleNum = 0; for (auto &handle : m_Impl->Handles) { Point2D centerpoint; interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point { handle.SetActive(true); m_Impl->ActiveHandle = handle; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { handleNum++; handle.SetActive(false); } this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); } return false; } void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); // disable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *) { this->EnableOriginalInteraction(); // enable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; // get initial position coordinates m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); return true; } void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); Vector3D spacing = geometry->GetSpacing(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); Vector3D interactionMove; // pixel aligned shifting of the bounding box interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0]; interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1]; interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2]; if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured { m_Impl->LastPickedWorldPoint = currentPickedPoint; geometry->SetOrigin(geometry->GetOrigin() + interactionMove); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } return; } void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); Vector3D spacing = geometry->GetSpacing(); // pixel aligned bounding box Vector3D interactionMove; interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); std::vector faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); Vector3D faceNormal; faceNormal[0] = handlePickedPoint[0] - center[0]; faceNormal[1] = handlePickedPoint[1] - center[1]; faceNormal[2] = handlePickedPoint[2] - center[2]; Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal; // calculate cornerpoints from geometry without visualization offset to update actual geometry cornerPoints = GetCornerPoints(geometry, false); num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } bool positionChangeThreshold = true; for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points { if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) { Point3D point = pointscontainer->GetElement(numFaces); if (m_Impl->RotationEnabled) // apply if geometry is rotated and a pixel aligned shift is not possible { point[0] += faceShift[0]; point[1] += faceShift[1]; point[2] += faceShift[2]; } else // shift pixelwise { point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0]; point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1]; point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2]; } if (point == pointscontainer->GetElement(numFaces)) positionChangeThreshold = false; else m_Impl->LastPickedWorldPoint = point; pointscontainer->InsertElement(numFaces, point); } } if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel { auto inverse = mitk::AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverse); for (unsigned int pointid = 0; pointid < 8; pointid++) { pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); } auto bbox = mitk::BoundingBox::New(); bbox->SetPoints(pointscontainer); bbox->ComputeBoundingBox(); mitk::Point3D BBmin = bbox->GetMinimum(); mitk::Point3D BBmax = bbox->GetMaximum(); if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 && std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero { geometry->SetBounds(bbox->GetBounds()); geometry->Modified(); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return; } void mitk::BoundingShapeInteractor::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->SetProperty("layer", mitk::IntProperty::New(99)); inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); EnableOriginalInteraction(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::EnableOriginalInteraction() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (const auto& displayInteractionConfig : m_Impl->DisplayInteractionConfigs) { if (displayInteractionConfig.first) { auto displayActionEventBroadcast = static_cast( us::GetModuleContext()->GetService(displayInteractionConfig.first)); if (nullptr != displayActionEventBroadcast) { // here the regular configuration is loaded again displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second); } } } m_Impl->DisplayInteractionConfigs.clear(); m_Impl->OriginalInteractionEnabled = true; } void mitk::BoundingShapeInteractor::DisableOriginalInteraction() { // dont deactivate twice, else we will clutter the config list ... if (false == m_Impl->OriginalInteractionEnabled) return; // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_Impl->DisplayInteractionConfigs.clear(); auto eventObservers = us::GetModuleContext()->GetServiceReferences(); for (const auto& eventObserver : eventObservers) { auto *displayActionEventBroadcast = dynamic_cast( us::GetModuleContext()->GetService(eventObserver)); if (nullptr != displayActionEventBroadcast) { // remember the original configuration m_Impl->DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml"); } } m_Impl->OriginalInteractionEnabled = false; } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index 4d6afbb5a3..5114a48201 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -1,467 +1,462 @@ /*============================================================================ 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 "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include -#include #include #include #include #include #include -#include #include #include #include -static vtkSmartPointer CreateHandle() -{ - auto handle = vtkSmartPointer::New(); - - handle->SetPhiResolution(8); - handle->SetThetaResolution(16); - - return handle; -} - namespace mitk { class BoundingShapeVtkMapper2D::Impl { public: Impl() { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } std::vector HandlePropertyList; mitk::LocalStorageHandler LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() : m_Actor(vtkSmartPointer::New()), m_HandleActor(vtkSmartPointer::New()), m_SelectedHandleActor(vtkSmartPointer::New()), m_Mapper(vtkSmartPointer::New()), m_HandleMapper(vtkSmartPointer::New()), m_SelectedHandleMapper(vtkSmartPointer::New()), m_Cutter(vtkSmartPointer::New()), m_CuttingPlane(vtkSmartPointer::New()), m_LastSliceNumber(0), m_PropAssembly(vtkSmartPointer::New()), m_ZoomFactor(1.0) { m_Actor->SetMapper(m_Mapper); - m_Actor->GetProperty()->SetOpacity(0.3); m_Actor->VisibilityOn(); m_HandleActor->SetMapper(m_HandleMapper); m_HandleActor->VisibilityOn(); m_SelectedHandleActor->VisibilityOn(); m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0); m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_SelectedHandleMapper->SetTransformCoordinate(tcoord); tcoord->Delete(); m_Cutter->SetCutFunction(m_CuttingPlane); for (int i = 0; i < 6; ++i) - m_Handles.push_back(CreateHandle()); + m_Handles.push_back(vtkSmartPointer::New()); m_PropAssembly->AddPart(m_Actor); m_PropAssembly->AddPart(m_HandleActor); m_PropAssembly->VisibilityOn(); } bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) { const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (m_LastGenerateDataTime < worldGeometry->GetMTime()) return true; unsigned int sliceNumber = renderer->GetSlice(); if (m_LastSliceNumber != sliceNumber) return true; if (mapper && m_LastGenerateDataTime < mapper->GetMTime()) return true; if (dataNode) { if (m_LastGenerateDataTime < dataNode->GetMTime()) return true; mitk::BaseData *data = dataNode->GetData(); if (data && m_LastGenerateDataTime < data->GetMTime()) return true; } return false; } mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer) { this->GenerateDataForRenderer(renderer); } void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D() { delete m_Impl; } void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *renderer) { const DataNode::Pointer node = GetDataNode(); if (node == nullptr) return; LocalStorage *localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); // either update if GeometryData was modified or if the zooming was performed bool needGenerateData = localStorage->IsUpdateRequired( renderer, this, GetDataNode()); // true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() || // localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime(); // //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode()); double scale = renderer->GetScaleFactorMMPerDisplayUnit(); if (std::abs(scale - localStorage->m_ZoomFactor) > 0.001) { localStorage->m_ZoomFactor = scale; needGenerateData = true; } if (needGenerateData) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { localStorage->m_Actor->VisibilityOff(); return; } GeometryData::Pointer shape = static_cast(node->GetData()); if (shape == nullptr) return; mitk::BaseGeometry::Pointer geometry = shape->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints and extent from geometry with visualization offset std::vector cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } // caculate face normals double cubeFaceNormal0[3], cubeFaceNormal1[3], cubeFaceNormal2[3]; double a[3], b[3]; a[0] = (cornerPoints[5][0] - cornerPoints[6][0]); a[1] = (cornerPoints[5][1] - cornerPoints[6][1]); a[2] = (cornerPoints[5][2] - cornerPoints[6][2]); b[0] = (cornerPoints[5][0] - cornerPoints[4][0]); b[1] = (cornerPoints[5][1] - cornerPoints[4][1]); b[2] = (cornerPoints[5][2] - cornerPoints[4][2]); vtkMath::Cross(a, b, cubeFaceNormal0); a[0] = (cornerPoints[0][0] - cornerPoints[6][0]); a[1] = (cornerPoints[0][1] - cornerPoints[6][1]); a[2] = (cornerPoints[0][2] - cornerPoints[6][2]); b[0] = (cornerPoints[0][0] - cornerPoints[2][0]); b[1] = (cornerPoints[0][1] - cornerPoints[2][1]); b[2] = (cornerPoints[0][2] - cornerPoints[2][2]); vtkMath::Cross(a, b, cubeFaceNormal1); a[0] = (cornerPoints[2][0] - cornerPoints[7][0]); a[1] = (cornerPoints[2][1] - cornerPoints[7][1]); a[2] = (cornerPoints[2][2] - cornerPoints[7][2]); b[0] = (cornerPoints[2][0] - cornerPoints[6][0]); b[1] = (cornerPoints[2][1] - cornerPoints[6][1]); b[2] = (cornerPoints[2][2] - cornerPoints[6][2]); vtkMath::Cross(a, b, cubeFaceNormal2); vtkMath::Normalize(cubeFaceNormal0); vtkMath::Normalize(cubeFaceNormal1); vtkMath::Normalize(cubeFaceNormal2); // create cube for rendering bounding box auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1)) { localStorage->m_Actor->VisibilityOff(); localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); return; } // estimate current image plane to decide whether the cube is visible or not const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry())) return; double origin[3]; origin[0] = planeGeometry->GetOrigin()[0]; origin[1] = planeGeometry->GetOrigin()[1]; origin[2] = planeGeometry->GetOrigin()[2]; double displayPlaneNormal[3]; displayPlaneNormal[0] = planeGeometry->GetNormal()[0]; displayPlaneNormal[1] = planeGeometry->GetNormal()[1]; displayPlaneNormal[2] = planeGeometry->GetNormal()[2]; vtkMath::Normalize(displayPlaneNormal); localStorage->m_CuttingPlane->SetOrigin(origin); localStorage->m_CuttingPlane->SetNormal(displayPlaneNormal); // add cube polydata to local storage localStorage->m_Cutter->SetInputData(polydata); localStorage->m_Cutter->SetGenerateCutScalars(1); localStorage->m_Cutter->Update(); if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor)) localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor); if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor)) localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); localStorage->m_HandleMapper->SetTransformCoordinate(tcoord); tcoord->Delete(); if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow { mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else - initialHandleSize = 1.0 / 40.0; + initialHandleSize = 0.02; mitk::Point2D displaySize = renderer->GetDisplaySizeInMM(); double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize; auto appendPoly = vtkSmartPointer::New(); unsigned int handleIdx = 0; // add handles and their assigned properties to the local storage mitk::IntProperty::Pointer activeHandleId = dynamic_cast(node->GetProperty("Bounding Shape.Active Handle ID")); double angle0 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal0))); if (angle0 > 179.0) angle0 -= 180.0; double angle1 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal1))); if (angle1 > 179.0) angle1 -= 180.0; double angle2 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal2))); if (angle2 > 179.0) angle2 -= 180.0; bool visible = false; bool selected = false; for (auto& handle : localStorage->m_Handles) { Point3D handleCenter = m_Impl->HandlePropertyList[handleIdx].GetPosition(); - handle->SetRadius(handleSize); + handle->SetXLength(handleSize); + handle->SetYLength(handleSize); + handle->SetZLength(handleSize); handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]); // show handles only if the corresponding face is aligned to the render window if ( (handleIdx != 0 && handleIdx != 1 && std::abs(angle0) < 0.1) || // handles 0 and 1 (handleIdx != 2 && handleIdx != 3 && std::abs(angle1) < 0.1) || // handles 2 and 3 (handleIdx != 4 && handleIdx != 5 && std::abs(angle2) < 0.1) ) // handles 4 and 5 { if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[handleIdx].GetIndex())) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { handle->Update(); localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput()); localStorage->m_SelectedHandleActor->VisibilityOn(); selected = true; } } visible = true; } ++handleIdx; } if (visible) { appendPoly->Update(); } else { localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); } auto stripper = vtkSmartPointer::New(); stripper->SetInputData(localStorage->m_Cutter->GetOutput()); stripper->Update(); auto cutPolyData = vtkSmartPointer::New(); cutPolyData->SetPoints(stripper->GetOutput()->GetPoints()); cutPolyData->SetPolys(stripper->GetOutput()->GetLines()); localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData); - mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty("color")); - if (selectedColor != nullptr) - { - mitk::Color color = selectedColor->GetColor(); - localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } + + this->ApplyColorAndOpacityProperties(renderer, localStorage->m_Actor); if (activeHandleId != nullptr) { localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0); } else { localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1); } localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); // add parts to the overall storage localStorage->m_PropAssembly->AddPart(localStorage->m_Actor); localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor); if (selected) { localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor); } localStorage->m_PropAssembly->VisibilityOn(); localStorage->m_Actor->VisibilityOn(); localStorage->m_HandleActor->VisibilityOn(); } else { localStorage->m_PropAssembly->VisibilityOff(); localStorage->m_Actor->VisibilityOff(); localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); localStorage->UpdateGenerateDataTime(); } localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly; } -void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) +void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { + auto* property = actor->GetProperty(); + + std::array color = { 1.0, 0.0, 0.0 }; + this->GetDataNode()->GetColor(color.data(), renderer); + property->SetColor(color[0], color[1], color[2]); + + float opacity = 0.2f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index 75d303f210..b07e626fa2 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -1,335 +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 "mitkBoundingShapeVtkMapper3D.h" #include "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include -#include #include namespace mitk { class BoundingShapeVtkMapper3D::Impl { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage() override; LocalStorage(const LocalStorage &) = delete; LocalStorage &operator=(const LocalStorage &) = delete; - std::vector> Handles; + std::vector> Handles; vtkSmartPointer Actor; vtkSmartPointer HandleActor; vtkSmartPointer SelectedHandleActor; vtkSmartPointer PropAssembly; }; public: Impl() : DistanceFromCam(1.0) { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } double DistanceFromCam; std::vector HandlePropertyList; mitk::LocalStorageHandler LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::LocalStorage() : Actor(vtkSmartPointer::New()), HandleActor(vtkSmartPointer::New()), SelectedHandleActor(vtkSmartPointer::New()), PropAssembly(vtkSmartPointer::New()) { for (int i = 0; i < 6; i++) - Handles.push_back(vtkSmartPointer::New()); + Handles.push_back(vtkSmartPointer::New()); } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper3D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper3D::BoundingShapeVtkMapper3D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper3D::~BoundingShapeVtkMapper3D() { delete m_Impl; } -void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) +void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { - //Superclass::ApplyColorAndOpacityProperties(renderer, actor); + auto* property = actor->GetProperty(); + + std::array color = { 1.0, 0.0, 0.0 }; + this->GetDataNode()->GetColor(color.data(), renderer); + property->SetColor(color[0], color[1], color[2]); + + float opacity = 0.2f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); } void mitk::BoundingShapeVtkMapper3D::ApplyBoundingShapeProperties(BaseRenderer *renderer, vtkActor *actor) { if (actor == nullptr) return; auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; bool isVisible = false; dataNode->GetBoolProperty("Bounding Shape.3D Rendering", isVisible, renderer); actor->SetVisibility(isVisible); float lineWidth = 1.0f; dataNode->GetFloatProperty("Bounding Shape.Line.Width", lineWidth, renderer); auto property = actor->GetProperty(); property->SetLineWidth(lineWidth); } void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; vtkCamera *camera = renderer->GetVtkRenderer()->GetActiveCamera(); auto localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); bool needGenerateData = localStorage->GetLastGenerateDataTime() < dataNode->GetMTime(); double distance = camera->GetDistance(); if (std::abs(distance - m_Impl->DistanceFromCam) > mitk::eps) { m_Impl->DistanceFromCam = distance; needGenerateData = true; } if (needGenerateData) { bool isVisible = true; dataNode->GetVisibility(isVisible, renderer); if (!isVisible) { localStorage->Actor->VisibilityOff(); return; } // set the input-object at time t for the mapper auto *geometryData = dynamic_cast(dataNode->GetData()); if (geometryData == nullptr) return; mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints from geometry std::vector cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr) { localStorage->Actor->VisibilityOff(); return; } mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else - initialHandleSize = 1.0 / 40.0; + initialHandleSize = 0.02; double handlesize = ((camera->GetDistance() * std::tan(vtkMath::RadiansFromDegrees(camera->GetViewAngle()))) / 2.0) * initialHandleSize; if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->HandleActor)) localStorage->PropAssembly->RemovePart(localStorage->HandleActor); if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->Actor)) localStorage->PropAssembly->RemovePart(localStorage->Actor); auto selectedhandlemapper = vtkSmartPointer::New(); auto appendPoly = vtkSmartPointer::New(); mitk::IntProperty::Pointer activeHandleId = dynamic_cast(dataNode->GetProperty("Bounding Shape.Active Handle ID")); int i = 0; for (auto &handle : localStorage->Handles) { Point3D handlecenter = m_Impl->HandlePropertyList[i].GetPosition(); handle->SetCenter(handlecenter[0], handlecenter[1], handlecenter[2]); - handle->SetRadius(handlesize); + handle->SetXLength(handlesize); + handle->SetYLength(handlesize); + handle->SetZLength(handlesize); handle->Update(); if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if (activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex()) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { selectedhandlemapper->SetInputData(handle->GetOutput()); localStorage->SelectedHandleActor->SetMapper(selectedhandlemapper); localStorage->SelectedHandleActor->GetProperty()->SetColor(0, 1, 0); localStorage->SelectedHandleActor->GetMapper()->SetInputDataObject(handle->GetOutput()); localStorage->PropAssembly->AddPart(localStorage->SelectedHandleActor); } } i++; } appendPoly->Update(); auto mapper = vtkSmartPointer::New(); mapper->SetInputData(polydata); auto handlemapper = vtkSmartPointer::New(); handlemapper->SetInputData(appendPoly->GetOutput()); localStorage->Actor->SetMapper(mapper); localStorage->Actor->GetMapper()->SetInputDataObject(polydata); - localStorage->Actor->GetProperty()->SetOpacity(0.3); - - mitk::ColorProperty::Pointer selectedColor = dynamic_cast(dataNode->GetProperty("color")); - if (selectedColor != nullptr) - { - mitk::Color color = selectedColor->GetColor(); - localStorage->Actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } localStorage->HandleActor->SetMapper(handlemapper); if (activeHandleId == nullptr) { localStorage->HandleActor->GetProperty()->SetColor(1, 1, 1); } else { localStorage->HandleActor->GetProperty()->SetColor(1, 0, 0); } localStorage->HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); this->ApplyColorAndOpacityProperties(renderer, localStorage->Actor); this->ApplyBoundingShapeProperties(renderer, localStorage->Actor); - - this->ApplyColorAndOpacityProperties(renderer, localStorage->HandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->HandleActor); - - this->ApplyColorAndOpacityProperties(renderer, localStorage->SelectedHandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->SelectedHandleActor); // apply properties read from the PropertyList // this->ApplyProperties(localStorage->m_Actor, renderer); // this->ApplyProperties(localStorage->m_HandleActor, renderer); // this->ApplyProperties(localStorage->m_SelectedHandleActor, renderer); localStorage->Actor->VisibilityOn(); localStorage->HandleActor->VisibilityOn(); localStorage->SelectedHandleActor->VisibilityOn(); localStorage->PropAssembly->AddPart(localStorage->Actor); localStorage->PropAssembly->AddPart(localStorage->HandleActor); localStorage->PropAssembly->VisibilityOn(); localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper3D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->PropAssembly; } diff --git a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp index e6ac3712c0..1e4f18c97f 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp +++ b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp @@ -1,59 +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. ============================================================================*/ #include "mitkCameraIntrinsicsProperty.h" namespace mitk { bool CameraIntrinsicsProperty::IsEqual(const BaseProperty& property) const { return this->m_Value->Equals(static_cast(property).m_Value.GetPointer()); } bool CameraIntrinsicsProperty::Assign(const BaseProperty& property) { this->m_Value = static_cast(property).m_Value; return true; } std::string CameraIntrinsicsProperty::GetValueAsString() const { std::stringstream myStr; myStr << GetValue(); return myStr.str(); } CameraIntrinsicsProperty::CameraIntrinsicsProperty() : BaseProperty() {} CameraIntrinsicsProperty::CameraIntrinsicsProperty(const CameraIntrinsicsProperty& other) : BaseProperty(other) { } CameraIntrinsicsProperty::CameraIntrinsicsProperty( mitk::CameraIntrinsics::Pointer value ) : BaseProperty(), m_Value( value ) {} itk::LightObject::Pointer CameraIntrinsicsProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } +bool CameraIntrinsicsProperty::ToJSON(nlohmann::json&) const +{ + return false; +} + +bool CameraIntrinsicsProperty::FromJSON(const nlohmann::json&) +{ + return false; +} + } // namespace mitk diff --git a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h index c992948156..23d40b6c69 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h +++ b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h @@ -1,73 +1,76 @@ /*============================================================================ 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 mitkCameraIntrinsicsProperty_h #define mitkCameraIntrinsicsProperty_h #include "mitkBaseProperty.h" #include "mitkCameraIntrinsics.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif class MITKCAMERACALIBRATION_EXPORT CameraIntrinsicsProperty : public BaseProperty { public: typedef mitk::CameraIntrinsics::Pointer ValueType; mitkClassMacro(CameraIntrinsicsProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(CameraIntrinsicsProperty, mitk::CameraIntrinsics::Pointer); itkSetMacro(Value, mitk::CameraIntrinsics::Pointer ); itkGetConstMacro(Value, mitk::CameraIntrinsics::Pointer ); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: mitk::CameraIntrinsics::Pointer m_Value; CameraIntrinsicsProperty(); CameraIntrinsicsProperty(const CameraIntrinsicsProperty&); CameraIntrinsicsProperty( mitk::CameraIntrinsics::Pointer value ); private: // purposely not implemented CameraIntrinsicsProperty& operator=(const CameraIntrinsicsProperty&); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty& property) const override; bool Assign(const BaseProperty& property) override; }; #ifdef _MSC_VER # pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index 0ad7a726f5..74cada7281 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -1,74 +1,74 @@ set(TOOL_CPPS "") # temporary suppress warnings in the following files until image accessors are fully integrated. set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) mitk_create_module( INCLUDE_DIRS PUBLIC ${MITK_BINARY_DIR} PRIVATE src/Algorithms src/Controllers src/DataManagement src/Interactions src/IO src/Rendering DEPENDS PUBLIC MitkLog CppMicroServices PACKAGE_DEPENDS PUBLIC Boost + nlohmann_json ITK|IOImageBase+SpatialObjects+Statistics #ITK|Statistics+Transform VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOImage+IOXML PRIVATE ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML - nlohmann_json tinyxml2 ${optional_private_package_depends} # Do not automatically create CppMicroServices initialization code. # Because the VTK "auto-init" functionality injects file-local static # initialization code in every cpp file which includes a VTK header, # static initialization order becomes an issue again. For the Mitk # core library, we need to ensure that the VTK static initialization stuff # happens before the CppMicroServices initialization, since the latter # might already use VTK code which needs to access VTK object factories. # Hence, CppMicroServices initialization code is placed manually within # the mitkCoreActivator.cpp file. NO_INIT ) if(NOT TARGET ${MODULE_TARGET}) message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist") endif() function(_itk_create_factory_register_manager) # In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which # prevents multiple registrations/unregistrations of ITK IO factories # during library loading/unloading (of MITK libraries). However, we need # "one" place where the IO factories are registered at # least once. This could be the application executable, but every executable would # need to take care of that itself. Instead, we allow the auto registration in the # Mitk Core library. set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1) find_package(ITK) include(${ITK_USE_FILE}) if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER) # We manually add the define which will be of target scope. MITK # patches ITK_USE_FILE to remove the directory scoped compile # definition since it would be propagated to other targets in the # same directory scope but these targets might want to *not* # use the ITK factory manager stuff. target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER) endif() endfunction() _itk_create_factory_register_manager() if(BUILD_TESTING) add_subdirectory(TestingHelper) add_subdirectory(test) endif() diff --git a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp index c3df456fad..fb77ebdb61 100644 --- a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp @@ -1,239 +1,240 @@ /*============================================================================ 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. ============================================================================*/ // VTK #include #include #include #include #include #include // MITK #include #include #include #include #include #include #include #include +#include // VTK Testing to compare the rendered image pixel-wise against a reference screen shot #include "vtkTesting.h" mitk::RenderingTestHelper::RenderingTestHelper(int width, int height, AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); } mitk::RenderingTestHelper::RenderingTestHelper( int width, int height, int argc, char *argv[], AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); this->SetInputFileNames(argc, argv); } void mitk::RenderingTestHelper::Initialize(int width, int height, AntiAliasing antiAliasing) { RenderingManager::GetInstance()->SetAntiAliasing(antiAliasing); mitk::UIDGenerator uidGen = mitk::UIDGenerator("UnnamedRenderer_"); m_RenderWindow = mitk::RenderWindow::New(nullptr, uidGen.GetUID().c_str()); auto renderWindow = m_RenderWindow->GetVtkRenderWindow(); if (0 == renderWindow->SupportsOpenGL()) { auto openGLRenderWindow = dynamic_cast(renderWindow); auto message = nullptr != openGLRenderWindow ? openGLRenderWindow->GetOpenGLSupportMessage() : std::string("No details available."); mitkThrowException(mitk::TestNotRunException) << "OpenGL not supported: " << message; } m_DataStorage = mitk::StandaloneDataStorage::New(); m_RenderWindow->GetRenderer()->SetDataStorage(m_DataStorage); this->SetMapperIDToRender2D(); this->GetVtkRenderWindow()->SetSize(width, height); m_RenderWindow->GetRenderer()->Resize(width, height); } mitk::RenderingTestHelper::~RenderingTestHelper() { } void mitk::RenderingTestHelper::SetMapperID(mitk::BaseRenderer::StandardMapperSlot id) { m_RenderWindow->GetRenderer()->SetMapperID(id); } void mitk::RenderingTestHelper::SetMapperIDToRender3D() { this->SetMapperID(mitk::BaseRenderer::Standard3D); mitk::RenderingManager::GetInstance()->InitializeViews( this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll())); } void mitk::RenderingTestHelper::SetMapperIDToRender2D() { this->SetMapperID(mitk::BaseRenderer::Standard2D); } void mitk::RenderingTestHelper::Render() { // if the datastorage is initialized and at least 1 image is loaded render it if (m_DataStorage.IsNotNull() && m_DataStorage->GetAll()->Size() >= 1) { // Prepare the VTK camera before rendering. m_RenderWindow->GetRenderer()->PrepareRender(); this->GetVtkRenderWindow()->Render(); this->GetVtkRenderWindow()->WaitForCompletion(); if (m_AutomaticallyCloseRenderWindow == false) { // Use interaction to stop the test this->GetVtkRenderWindow()->GetInteractor()->Start(); } } else { MITK_ERROR << "No images loaded in data storage!"; } } mitk::DataStorage::Pointer mitk::RenderingTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::RenderingTestHelper::SetInputFileNames(int argc, char *argv[]) { // i is set 1, because 0 is the testname as string // parse parameters for (int i = 1; i < argc; ++i) { // add everything to a list but -T and -V std::string tmp = argv[i]; if ((tmp.compare("-T")) && (tmp.compare("-V"))) { this->AddToStorage(tmp); } else { break; } } } void mitk::RenderingTestHelper::SetViewDirection(mitk::AnatomicalPlane viewDirection) { mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow()) ->GetSliceNavigationController() ->SetDefaultViewDirection(viewDirection); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } void mitk::RenderingTestHelper::ReorientSlices(mitk::Point3D origin, mitk::Vector3D rotation) { mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())->GetSliceNavigationController(); sliceNavigationController->ReorientSlices(origin, rotation); } vtkRenderer *mitk::RenderingTestHelper::GetVtkRenderer() { return m_RenderWindow->GetRenderer()->GetVtkRenderer(); } void mitk::RenderingTestHelper::SetImageProperty(const char *propertyKey, mitk::BaseProperty *property) { this->m_DataStorage->GetNode(mitk::NodePredicateDataType::New("Image"))->SetProperty(propertyKey, property); } vtkRenderWindow *mitk::RenderingTestHelper::GetVtkRenderWindow() { return m_RenderWindow->GetVtkRenderWindow(); } bool mitk::RenderingTestHelper::CompareRenderWindowAgainstReference(int argc, char *argv[], double threshold) { this->Render(); // retVal meanings: (see VTK/Rendering/vtkTesting.h) // 0 = test failed // 1 = test passed // 2 = test not run // 3 = something with vtkInteraction if (vtkTesting::Test(argc, argv, this->GetVtkRenderWindow(), threshold) == 1) return true; else return false; } // method to save a screenshot of the renderwindow (e.g. create a reference screenshot) void mitk::RenderingTestHelper::SaveAsPNG(std::string fileName) { vtkSmartPointer renderer = this->GetVtkRenderer(); bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer()); renderer->GetRenderWindow()->DoubleBufferOff(); vtkSmartPointer magnifier = vtkSmartPointer::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(1); vtkSmartPointer fileWriter = vtkSmartPointer::New(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.c_str()); fileWriter->Write(); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void mitk::RenderingTestHelper::SetAutomaticallyCloseRenderWindow(bool automaticallyCloseRenderWindow) { m_AutomaticallyCloseRenderWindow = automaticallyCloseRenderWindow; } void mitk::RenderingTestHelper::SaveReferenceScreenShot(std::string fileName) { this->SaveAsPNG(fileName); } void mitk::RenderingTestHelper::AddToStorage(const std::string &filename) { try { mitk::IOUtil::Load(filename, *m_DataStorage.GetPointer()); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << "Failed loading test data '" << filename << "': " << e.what(); } } void mitk::RenderingTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index d8412455d1..1aa29e2075 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,329 +1,328 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkTemporalJoinImagesFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCrosshairManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSliceNavigationHelper.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkTimeNavigationController.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkCrosshairData.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp + DataManagement/mitkIPropertyDeserialization.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp - DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp - DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable + DataManagement/mitkLookupTables.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSubGeometry.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp + DataManagement/mitkPropertyDeserialization.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp - DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp Interactions/mitkDisplayActionEventHandlerStd.cpp Interactions/mitkDisplayActionEventHandlerSynchronized.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp - IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLogBackend.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkUtf8Util.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp IO/mitkIOMetaInformationPropertyConstants.cpp IO/mitkIPreferences.cpp IO/mitkPreferences.cpp IO/mitkIPreferencesService.cpp IO/mitkPreferencesService.cpp IO/mitkIPreferencesStorage.cpp IO/mitkXMLPreferencesStorage.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkBaseRendererHelper.cpp Rendering/mitkCrosshairVtkMapper2D.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVideoRecorder.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfigMITKBase.xml Interactions/DisplayConfigPACSBase.xml Interactions/DisplayConfigCrosshair.xml Interactions/DisplayConfigRotation.xml Interactions/DisplayConfigActivateCoupling.xml Interactions/DisplayConfigSwivel.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigBlockLMB.xml Interactions/PointSet.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkAnnotationProperty.h b/Modules/Core/include/mitkAnnotationProperty.h index 310a432405..9c03069090 100644 --- a/Modules/Core/include/mitkAnnotationProperty.h +++ b/Modules/Core/include/mitkAnnotationProperty.h @@ -1,79 +1,83 @@ /*============================================================================ 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 mitkAnnotationProperty_h #define mitkAnnotationProperty_h #include "mitkBaseProperty.h" #include "mitkNumericTypes.h" #include #include #include namespace mitk { /** * \brief Property for annotations * \ingroup DataManagement */ class MITKCORE_EXPORT AnnotationProperty : public BaseProperty { public: mitkClassMacro(AnnotationProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro2Param(AnnotationProperty, const char *, const Point3D &); mitkNewMacro2Param(AnnotationProperty, const std::string &, const Point3D &); mitkNewMacro4Param(AnnotationProperty, const char *, ScalarType, ScalarType, ScalarType); mitkNewMacro4Param(AnnotationProperty, const std::string &, ScalarType, ScalarType, ScalarType); itkGetStringMacro(Label); itkSetStringMacro(Label); const Point3D &GetPosition() const; void SetPosition(const Point3D &position); std::string GetValueAsString() const override; + + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + virtual BaseProperty &operator=(const BaseProperty &other) { return Superclass::operator=(other); } using BaseProperty::operator=; protected: std::string m_Label; Point3D m_Position; AnnotationProperty(); AnnotationProperty(const char *label, const Point3D &position); AnnotationProperty(const std::string &label, const Point3D &position); AnnotationProperty(const char *label, ScalarType x, ScalarType y, ScalarType z); AnnotationProperty(const std::string &label, ScalarType x, ScalarType y, ScalarType z); AnnotationProperty(const AnnotationProperty &other); private: // purposely not implemented AnnotationProperty &operator=(const AnnotationProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index 4d00557d8d..6dff77d92a 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,763 +1,764 @@ /*============================================================================ 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 mitkBaseGeometry_h #define mitkBaseGeometry_h #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 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 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 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(); + const vtkMatrix4x4* GetVtkMatrix() const; //##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 ((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 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; //##Documentation //## @brief One to one mapping of axes to world orientations. //## //## The result is stored in the output argument that must be an array of three int values. //## The elements of the array will be the axis indices that correspond to the sagittal, //## coronal and axial orientations, in this order. It is guaranteed that each axis will //## be mapped to different orientations. //## //## @param axes Output argument that will store the axis indices for each orientation. void MapAxesToOrientations(int axes[]) 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 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 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 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 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 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 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 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, * 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 diff --git a/Modules/Core/include/mitkBaseProperty.h b/Modules/Core/include/mitkBaseProperty.h index 05a305dc50..f99c775f5d 100644 --- a/Modules/Core/include/mitkBaseProperty.h +++ b/Modules/Core/include/mitkBaseProperty.h @@ -1,98 +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 mitkBaseProperty_h #define mitkBaseProperty_h #include #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 assignment was successful. */ bool AssignProperty(const BaseProperty &property); virtual std::string GetValueAsString() const; + /** \brief Serialize property value(s) to JSON. + * + * Rely on exceptions for error handling when implementing serialization. + * + * \return False if not serializable by design, true otherwise. + */ + virtual bool ToJSON(nlohmann::json& j) const = 0; + + /** \brief Deserialize property value(s) from JSON. + * + * Rely on exceptions for error handling when implementing deserialization. + * + * \return False if not deserializable by design, true otherwise. + */ + virtual bool FromJSON(const nlohmann::json& j) = 0; + /** * @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 diff --git a/Modules/Core/include/mitkClippingProperty.h b/Modules/Core/include/mitkClippingProperty.h index 87bdc056b1..9e3f532572 100644 --- a/Modules/Core/include/mitkClippingProperty.h +++ b/Modules/Core/include/mitkClippingProperty.h @@ -1,87 +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 mitkClippingProperty_h #define mitkClippingProperty_h #include "mitkBaseProperty.h" #include "mitkNumericTypes.h" #include #include #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * \brief Property for clipping datasets; currently only * clipping planes are possible * \ingroup DataManagement */ class MITKCORE_EXPORT ClippingProperty : public BaseProperty { public: mitkClassMacro(ClippingProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro2Param(ClippingProperty, const Point3D &, const Vector3D &); bool GetClippingEnabled() const; void SetClippingEnabled(bool enabled); const Point3D &GetOrigin() const; void SetOrigin(const Point3D &origin); const Vector3D &GetNormal() const; void SetNormal(const Vector3D &normal); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: bool m_ClippingEnabled; Point3D m_Origin; Vector3D m_Normal; ClippingProperty(); ClippingProperty(const ClippingProperty &other); ClippingProperty(const Point3D &origin, const Vector3D &normal); private: // purposely not implemented ClippingProperty &operator=(const ClippingProperty &); bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; itk::LightObject::Pointer InternalClone() const override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkColorProperty.h b/Modules/Core/include/mitkColorProperty.h index 0b1809c0fb..8b31242324 100644 --- a/Modules/Core/include/mitkColorProperty.h +++ b/Modules/Core/include/mitkColorProperty.h @@ -1,96 +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. ============================================================================*/ #ifndef mitkColorProperty_h #define mitkColorProperty_h -#include "mitkBaseProperty.h" +#include #include + #include +#include + namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Color Standard RGB color typedef (float) * * Standard RGB color typedef to get rid of template argument (float). * Color range is from 0.0f to 1.0f for each component. * * @ingroup Property */ typedef itk::RGBPixel Color; /** * @brief The ColorProperty class RGB color property * @ingroup DataManagement * * @note If you want to apply the mitk::ColorProperty to an mitk::Image * make sure to set the mitk::RenderingModeProperty to a mode which * supports color (e.g. LEVELWINDOW_COLOR). For an example how to use * the mitk::ColorProperty see mitkImageVtkMapper2DColorTest.cpp in * Core/Code/Rendering. */ class MITKCORE_EXPORT ColorProperty : public BaseProperty { protected: mitk::Color m_Color; ColorProperty(); ColorProperty(const ColorProperty &other); ColorProperty(const float red, const float green, const float blue); ColorProperty(const float color[3]); ColorProperty(const mitk::Color &color); public: mitkClassMacro(ColorProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(ColorProperty, const float *); mitkNewMacro1Param(ColorProperty, const mitk::Color &); mitkNewMacro3Param(ColorProperty, const float, const float, const float); typedef mitk::Color ValueType; const mitk::Color &GetColor() const; const mitk::Color &GetValue() const; std::string GetValueAsString() const override; void SetColor(const mitk::Color &color); void SetValue(const mitk::Color &color); void SetColor(float red, float green, float blue); + bool ToJSON(nlohmann::json &j) const override; + bool FromJSON(const nlohmann::json &j) override; + using BaseProperty::operator=; private: // purposely not implemented ColorProperty &operator=(const ColorProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk +namespace itk +{ + template + void to_json(nlohmann::json& j, const RGBPixel& c) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < 3; ++i) + j.push_back(c[i]); + } + + template + void from_json(const nlohmann::json& j, RGBPixel& c) + { + for (size_t i = 0; i < 3; ++i) + j.at(i).get_to(c[i]); + } +} // namespace itk + #endif diff --git a/Modules/Core/include/mitkCoreServices.h b/Modules/Core/include/mitkCoreServices.h index ce30201441..f02e895e09 100644 --- a/Modules/Core/include/mitkCoreServices.h +++ b/Modules/Core/include/mitkCoreServices.h @@ -1,187 +1,195 @@ /*============================================================================ 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 mitkCoreServices_h #define mitkCoreServices_h #include "MitkCoreExports.h" #include #include #include #include #include #include #include namespace mitk { struct IMimeTypeProvider; class IPropertyAliases; class IPropertyDescriptions; + class IPropertyDeserialization; class IPropertyExtensions; class IPropertyFilters; class IPropertyPersistence; class IPropertyRelations; class IPreferencesService; /** * @brief Access MITK core services. * * This class can be used to conveniently access common * MITK Core service objects. Some getter methods where implementations * exist in the core library are guaranteed to return a non-nullptr service object. * * To ensure that CoreServices::Unget() is called after the caller * has finished using a service object, you should use the CoreServicePointer * helper class which calls Unget() when it goes out of scope: * * \code * CoreServicePointer shaderRepo(CoreServices::GetShaderRepository()); * // Do something with shaderRepo * \endcode * * @see CoreServicePointer */ class MITKCORE_EXPORT CoreServices { public: /** * @brief Get an IPropertyAliases instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyAliases instance. */ static IPropertyAliases *GetPropertyAliases(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyDescriptions instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyDescriptions instance. */ static IPropertyDescriptions *GetPropertyDescriptions(us::ModuleContext *context = us::GetModuleContext()); + /** + * @brief Get an IPropertyDeserialization instance. + * @param context The module context of the module getting the service. + * @return A non-nullptr IPropertyDeserialization instance. + */ + static IPropertyDeserialization* GetPropertyDeserialization(us::ModuleContext* context = us::GetModuleContext()); + /** * @brief Get an IPropertyExtensions instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyExtensions instance. */ static IPropertyExtensions *GetPropertyExtensions(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyFilters instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyFilters instance. */ static IPropertyFilters *GetPropertyFilters(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyPersistence instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyPersistence instance. */ static IPropertyPersistence *GetPropertyPersistence(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyRelations instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyRelations instance. */ static IPropertyRelations *GetPropertyRelations(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IMimeTypeProvider instance. * @param context The module context of the module getting the service. * @return A non-nullptr IMimeTypeProvider instance. */ static IMimeTypeProvider *GetMimeTypeProvider(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPreferencesService instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPreferencesService instance. * @sa IPreferences */ static IPreferencesService *GetPreferencesService(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Unget a previously acquired service instance. * @param service The service instance to be released. * @param context * @return \c true if ungetting the service was successful, \c false otherwise. */ template static bool Unget(S *service, us::ModuleContext *context = us::GetModuleContext()) { return Unget(context, us_service_interface_iid(), service); } private: static bool Unget(us::ModuleContext *context, const std::string &interfaceId, void *service); // purposely not implemented CoreServices(); CoreServices(const CoreServices &); CoreServices &operator=(const CoreServices &); }; /** * @brief A RAII helper class for core service objects. * * This is class is intended for usage in local scopes; it calls * CoreServices::Unget(S*) in its destructor. You should not construct * multiple CoreServicePointer instances using the same service pointer, * unless it is retrieved by a new call to a CoreServices getter method. * * @see CoreServices */ template class MITK_LOCAL CoreServicePointer { public: explicit CoreServicePointer(S *service, us::ModuleContext* context = us::GetModuleContext()) : m_Service(service), m_Context(context) { assert(service); } ~CoreServicePointer() { try { CoreServices::Unget(m_Service, m_Context); } catch (const std::exception &e) { MITK_ERROR << e.what(); } catch (...) { MITK_ERROR << "Ungetting core service failed."; } } S *operator->() const { return m_Service; } private: S *const m_Service; us::ModuleContext* m_Context; }; } #endif diff --git a/Modules/Core/include/mitkEnumerationProperty.h b/Modules/Core/include/mitkEnumerationProperty.h index 251f1cce82..7771762a5b 100644 --- a/Modules/Core/include/mitkEnumerationProperty.h +++ b/Modules/Core/include/mitkEnumerationProperty.h @@ -1,198 +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 mitkEnumerationProperty_h #define mitkEnumerationProperty_h #include #include #include #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) // "multiple assignment operators specified" #endif namespace mitk { /** * This class may be used to store properties similar to enumeration values. * Each enumeration value is identified by an id and a name. Note that both * name and id must be unique. Add enumeration values before you use the * Get/SetValue methods. * * To use this class, create a subclass that adds the possible enumeration * values in its constructor. You should override AddEnum() as protected so * that the user isn't able to add invalid enumeration values. * * As example see mitk::VtkRepresentationProperty or * mitk::VtkInterpolationProperty. * * @ingroup DataManagement */ class MITKCORE_EXPORT EnumerationProperty : public BaseProperty { public: mitkClassMacro(EnumerationProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Represents the unique id which is assigned to each enumeration name. */ typedef unsigned int IdType; /** * Type used to store a mapping from enumeration id to enumeration name. */ typedef std::map EnumIdsContainerType; /** * Type used to store a mapping from enumeration name to enumeration id. */ typedef std::map EnumStringsContainerType; /** * Type used for iterators over all defined enumeration values. */ typedef EnumIdsContainerType::const_iterator EnumConstIterator; /** * Adds an enumeration value into the enumeration. The name and id provided * must be unique. This is checked while adding the new enumeration value. * If it is not unique, false is returned. If addition was successful, true * is returned. * @param name The unique name of the enumeration value * @param id The unique id of the enumeration value * @returns True, if the name/id combination was successfully added to the * enumeration values. Otherwise false. */ virtual bool AddEnum(const std::string &name, const IdType &id); /** * Sets the current value of the enumeration. * @param name The name of the enumeration value to set * @returns True if the value was successfully set. Otherwise false. */ virtual bool SetValue(const std::string &name); /** * Sets the current value of the enumeration. * @param id The id of the enumeration value to set * @returns True, if the value was successfully set. Otherwise false. */ virtual bool SetValue(const IdType &id); /** * Returns the id of the current enumeration value. If it was not set so far, * the return value is unspecified. */ virtual IdType GetValueAsId() const; /** * Returns the name of the current enumeration value. If it was not set so far, * the return value is unspecified. */ std::string GetValueAsString() const override; /** * Clears all enumerations including the current one. */ virtual void Clear(); /** * Determines the number of enumeration values. */ virtual EnumIdsContainerType::size_type Size() const; /** * Provides access to the set of enumeration values. The name can be * accessed with iterator->second, the id via iterator->first. * @returns An iterator over all enumeration values. */ virtual EnumConstIterator Begin() const; /** * Specifies the end of the range of enumeration values. * @returns An iterator pointing past the last enumeration values. */ virtual EnumConstIterator End() const; /** * Returns the name for the given id. * @param id The id for which the name should be determined. * If the id is invalid, the return value is unspecified. * @returns The name of the determined enumeration value. */ virtual std::string GetEnumString(const IdType &id) const; /** * Returns the id for the given name. * @param name The enumeration name for which the id should be determined. * If the name is invalid, the return value is unspecified. * @returns The id of the determined enumeration value. */ virtual IdType GetEnumId(const std::string &name) const; /** * Determines if a given id is valid. * @param id The id to check * @returns True if the given id is valid. Otherwise false. */ virtual bool IsValidEnumerationValue(const IdType &id) const; /** * Determines if a given name is valid. * @param name The name to check * @returns True if the given name is valid. Otherwise false. */ virtual bool IsValidEnumerationValue(const std::string &name) const; const EnumIdsContainerType &GetEnumIds() const; const EnumStringsContainerType &GetEnumStrings() const; EnumIdsContainerType &GetEnumIds(); EnumStringsContainerType &GetEnumStrings(); + /** + * Serializes the property to JSON. + * @note Classes deriving from EnumerationProperty are covered by this implementation and do not + * need to override this method again. + */ + bool ToJSON(nlohmann::json& j) const override; + + /** + * Deserializes the property to JSON. + * @note Classes deriving from EnumerationProperty are covered by this implementation and do not + * need to override this method again. + */ + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; EnumerationProperty & operator=(const EnumerationProperty &) = delete; protected: /** * Default constructor. The current value of the enumeration is undefined. */ EnumerationProperty(); EnumerationProperty(const EnumerationProperty &); bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; mitkCloneMacro(Self); private: IdType m_CurrentValue; - - typedef std::map IdMapForClassNameContainerType; - typedef std::map StringMapForClassNameContainerType; - EnumIdsContainerType m_IdMap; EnumStringsContainerType m_NameMap; }; } #ifdef _MSC_VER # pragma warning(pop) #endif #endif diff --git a/Modules/Core/include/mitkGenericLookupTable.h b/Modules/Core/include/mitkGenericLookupTable.h index c0ad940072..a450e3b125 100644 --- a/Modules/Core/include/mitkGenericLookupTable.h +++ b/Modules/Core/include/mitkGenericLookupTable.h @@ -1,138 +1,158 @@ /*============================================================================ 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 mitkGenericLookupTable_h #define mitkGenericLookupTable_h #include #include #include #include #include #include "mitkNumericTypes.h" #include +#include + namespace mitk { + template class GenericLookupTable; + template void from_json(const nlohmann::json&, GenericLookupTable&); + /** * @brief Template class for generating lookup-tables * * This class template can be instantiated for all classes/internal types that fulfills * these requirements: * - an operator<< so that the properties value can be put into a std::stringstream * - an operator== so that two properties can be checked for equality * * The main purpose of this class is to be used in conjunction with * GenericLookupTableProperty. This enables passing of arbitrary lookup * tables to mappers to configure the rendering process. */ template class GenericLookupTable { public: typedef unsigned int IdentifierType; typedef T ValueType; typedef std::map LookupTableType; typedef GenericLookupTable Self; GenericLookupTable() {} virtual ~GenericLookupTable() {} virtual const char *GetNameOfClass() const { return "GenericLookupTable"; } void SetTableValue(IdentifierType id, ValueType value) { m_LookupTable[id] = value; } bool ValueExists(IdentifierType id) const { auto it = m_LookupTable.find(id); return (it != m_LookupTable.end()); } ValueType GetTableValue(IdentifierType id) const { auto it = m_LookupTable.find(id); if (it != m_LookupTable.end()) return it->second; else throw std::range_error("id does not exist in the lookup table"); } const LookupTableType &GetLookupTable() const { return m_LookupTable; } bool operator==(const Self &lookupTable) const { return (m_LookupTable == lookupTable.m_LookupTable); } bool operator!=(const Self &lookupTable) const { return !(m_LookupTable == lookupTable.m_LookupTable); } virtual Self &operator=(const Self &other) // \TODO: this needs to be unit tested! { if (this == &other) { return *this; } else { m_LookupTable.clear(); m_LookupTable = other.m_LookupTable; return *this; } } + friend void from_json<>(const nlohmann::json&, GenericLookupTable&); + protected: LookupTableType m_LookupTable; }; + + template + void to_json(nlohmann::json& j, const GenericLookupTable& t) + { + j = t.GetLookupTable(); + } + + template + void from_json(const nlohmann::json& j, GenericLookupTable& t) + { + j.get_to(t.m_LookupTable); + } + } // namespace mitk /** * Generates a specialized subclass of mitk::GenericLookupTable. * This way, GetNameOfClass() returns the value provided by LookupTableName. * Please see mitkProperties.h for examples. * @param LookupTableName the name of the instantiation of GenericLookupTable * @param Type the value type of the GenericLookupTable */ #define mitkSpecializeGenericLookupTable(LookupTableName, Type) \ \ class MITKCORE_EXPORT LookupTableName : public GenericLookupTable \ \ { \ public: \ typedef LookupTableName Self; \ typedef GenericLookupTable Superclass; \ virtual const char *GetNameOfClass() const { return #LookupTableName; } \ LookupTableName() {} \ virtual Superclass &operator=(const Superclass &other) { return Superclass::operator=(other); } \ virtual ~LookupTableName() {} \ }; \ \ MITKCORE_EXPORT std::ostream &operator<<(std::ostream &stream, const LookupTableName & /*l*/); /** * Generates the ostream << operator for the lookuptable. This definition * of a global function must be in a cpp file, therefore it is split from the * class declaration macro mitkSpecializeGenericLookupTable. */ #define mitkSpecializeGenericLookupTableOperator(LookupTableName) \ \ std::ostream &mitk::operator<<(std::ostream &stream, const LookupTableName &l) \ \ { \ typedef LookupTableName::LookupTableType::const_iterator IterType; \ IterType e = l.GetLookupTable().end(); \ IterType b = l.GetLookupTable().begin(); \ stream << "["; \ for (IterType i = b; i != e; ++i) \ { \ if (i != b) \ { \ stream << ", "; \ } \ stream << i->first << " -> " << i->second; \ } \ return stream << "]"; \ }; #endif diff --git a/Modules/Core/include/mitkGenericProperty.h b/Modules/Core/include/mitkGenericProperty.h index ceff2ad5e2..b95907e77b 100644 --- a/Modules/Core/include/mitkGenericProperty.h +++ b/Modules/Core/include/mitkGenericProperty.h @@ -1,142 +1,167 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkGenericProperty_h #define mitkGenericProperty_h #include #include #include #include "mitkBaseProperty.h" #include "mitkNumericTypes.h" #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /*! @ brief Template class for generating properties for int, float, bool, etc. This class template can be instantiated for all classes/internal types that fulfills these requirements: - an operator<< so that the properties value can be put into a std::stringstream - an operator== so that two properties can be checked for equality - Note: you must use the macro mitkSpecializeGenericProperty to provide specializations - for concrete types (e.g. BoolProperty). Please see mitkProperties.h for examples. If you - don't use the mitkSpecializeGenericProperty Macro, GetNameOfClass() returns a wrong name. + Note: you must use the macros mitkDeclareGenericProperty and mitkDefineGenericProperty to + provide specializations for concrete types (e.g. BoolProperty). See mitkProperties.h for + examples. If you don't use these macros, GetNameOfClass() will return "GenericProperty", + which will mess up serialization for example. */ template class MITK_EXPORT GenericProperty : public BaseProperty { public: mitkClassMacro(GenericProperty, BaseProperty); mitkNewMacro1Param(GenericProperty, T); itkCloneMacro(Self); typedef T ValueType; itkSetMacro(Value, T); itkGetConstMacro(Value, T); std::string GetValueAsString() const override { std::stringstream myStr; myStr << GetValue(); return myStr.str(); } + bool ToJSON(nlohmann::json&) const override + { + return false; + } + + bool FromJSON(const nlohmann::json&) override + { + return false; + } + using BaseProperty::operator=; protected: GenericProperty() {} GenericProperty(T x) : m_Value(x) {} GenericProperty(const GenericProperty &other) : BaseProperty(other), m_Value(other.m_Value) {} T m_Value; private: // purposely not implemented GenericProperty &operator=(const GenericProperty &); itk::LightObject::Pointer InternalClone() const override { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } bool IsEqual(const BaseProperty &other) const override { return (this->m_Value == static_cast(other).m_Value); } bool Assign(const BaseProperty &other) override { this->m_Value = static_cast(other).m_Value; return true; } }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk /** * Generates a specialized subclass of mitk::GenericProperty. * This way, GetNameOfClass() returns the value provided by PropertyName. * Please see mitkProperties.h for examples. * @param PropertyName the name of the subclass of GenericProperty * @param Type the value type of the GenericProperty * @param Export the export macro for DLL usage */ #define mitkDeclareGenericProperty(PropertyName, Type, Export) \ \ class Export PropertyName : public GenericProperty \ \ { \ public: \ mitkClassMacro(PropertyName, GenericProperty); \ itkFactorylessNewMacro(Self); \ itkCloneMacro(Self); \ mitkNewMacro1Param(PropertyName, Type); \ \ + bool ToJSON(nlohmann::json& j) const override; \ + bool FromJSON(const nlohmann::json& j) override; \ + \ using BaseProperty::operator=; \ \ protected: \ PropertyName(); \ PropertyName(const PropertyName &); \ PropertyName(Type x); \ \ private: \ itk::LightObject::Pointer InternalClone() const override; \ }; #define mitkDefineGenericProperty(PropertyName, Type, DefaultValue) \ mitk::PropertyName::PropertyName() : Superclass(DefaultValue) {} \ mitk::PropertyName::PropertyName(const PropertyName &other) : GenericProperty(other) {} \ mitk::PropertyName::PropertyName(Type x) : Superclass(x) {} \ itk::LightObject::Pointer mitk::PropertyName::InternalClone() const \ { \ itk::LightObject::Pointer result(new Self(*this)); \ result->UnRegister(); \ return result; \ + } \ + bool mitk::PropertyName::ToJSON(nlohmann::json& j) const \ + { \ + j = this->GetValue(); \ + return true; \ + } \ + \ + bool mitk::PropertyName::FromJSON(const nlohmann::json& j) \ + { \ + this->SetValue(j.get()); \ + return true; \ } #endif diff --git a/Modules/Core/include/mitkGroupTagProperty.h b/Modules/Core/include/mitkGroupTagProperty.h index e83ff9b0e5..9d17596bc1 100644 --- a/Modules/Core/include/mitkGroupTagProperty.h +++ b/Modules/Core/include/mitkGroupTagProperty.h @@ -1,62 +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 mitkGroupTagProperty_h #define mitkGroupTagProperty_h #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /*! @brief Property class that has no value. @ingroup DataManagement The GroupTag property is used to tag a datatree node to show, that it is member of a group of datatree nodes. This can be used to build groups of datatreenodes without the need to contain them in a specific hiearchic order in the datatree */ class MITKCORE_EXPORT GroupTagProperty : public BaseProperty { public: mitkClassMacro(GroupTagProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - using BaseProperty::operator=; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + + using BaseProperty::operator=; protected: GroupTagProperty(); GroupTagProperty(const GroupTagProperty &); private: // purposely not implemented GroupTagProperty &operator=(const GroupTagProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkIPropertyDeserialization.h b/Modules/Core/include/mitkIPropertyDeserialization.h new file mode 100644 index 0000000000..7911bceb00 --- /dev/null +++ b/Modules/Core/include/mitkIPropertyDeserialization.h @@ -0,0 +1,61 @@ +/*============================================================================ + +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 mitkIPropertyDeserialization_h +#define mitkIPropertyDeserialization_h + +#include +#include +#include + +#include + +#include +#include + +namespace mitk +{ + /** + * \ingroup MicroServices_Interfaces + * \brief Interface of property deserialization service. + * + * This service allows you to register custom property types (derived from BaseProperty) for deserialization. + * If a property type is not registered, it cannot be deserialized, e.g. when deserializing a PropertyList. + */ + class MITKCORE_EXPORT IPropertyDeserialization + { + public: + virtual ~IPropertyDeserialization(); + + virtual BaseProperty::Pointer CreateInstance(const std::string& className) = 0; + + /** + * \brief Register a custom property type for deserialization. + * + * The module activator of the module defining a property type is a good location to register + * custom property types of that module. See the implementation of MitkCoreActivator for + * examples. + */ + template >> + void RegisterProperty() + { + this->InternalRegisterProperty(T::New()); + } + + protected: + virtual void InternalRegisterProperty(const BaseProperty* property) = 0; + }; +} + +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyDeserialization, "org.mitk.IPropertyDeserialization") + +#endif diff --git a/Modules/Core/include/mitkLevelWindow.h b/Modules/Core/include/mitkLevelWindow.h index f81f33c200..3b0c2006ad 100644 --- a/Modules/Core/include/mitkLevelWindow.h +++ b/Modules/Core/include/mitkLevelWindow.h @@ -1,263 +1,268 @@ /*============================================================================ 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 mitkLevelWindow_h #define mitkLevelWindow_h #include "mitkNumericTypes.h" #include +#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, restricted to the range from -10^300 to 10^300. Higher/lower values are clamped to these boundaries. */ 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 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(); }; + + MITKCORE_EXPORT void to_json(nlohmann::json& j, const LevelWindow& lw); + MITKCORE_EXPORT void from_json(const nlohmann::json& j, LevelWindow& lw); + } // namespace mitk #endif diff --git a/Modules/Core/include/mitkLevelWindowProperty.h b/Modules/Core/include/mitkLevelWindowProperty.h index 4c4325de22..30209a6f26 100755 --- a/Modules/Core/include/mitkLevelWindowProperty.h +++ b/Modules/Core/include/mitkLevelWindowProperty.h @@ -1,85 +1,88 @@ /*============================================================================ 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 mitkLevelWindowProperty_h #define mitkLevelWindowProperty_h #include "mitkBaseProperty.h" #include "mitkLevelWindow.h" namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief The LevelWindowProperty class Property for the mitk::LevelWindow * * @ingroup DataManagement * * @note If you want to apply the mitk::LevelWindowProperty to an mitk::Image, * make sure to 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 LevelWindowProperty : public BaseProperty { protected: LevelWindow m_LevWin; LevelWindowProperty(); LevelWindowProperty(const LevelWindowProperty &other); LevelWindowProperty(const mitk::LevelWindow &levWin); public: mitkClassMacro(LevelWindowProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(LevelWindowProperty, const mitk::LevelWindow &); typedef LevelWindow ValueType; ~LevelWindowProperty() override; const mitk::LevelWindow &GetLevelWindow() const; const mitk::LevelWindow &GetValue() const; void SetLevelWindow(const LevelWindow &levWin); void SetValue(const ValueType &levWin); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: // purposely not implemented LevelWindowProperty &operator=(const LevelWindowProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkLookupTableProperty.h b/Modules/Core/include/mitkLookupTableProperty.h index 8a57cc0e83..3b72837858 100644 --- a/Modules/Core/include/mitkLookupTableProperty.h +++ b/Modules/Core/include/mitkLookupTableProperty.h @@ -1,84 +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. ============================================================================*/ #ifndef mitkLookupTableProperty_h #define mitkLookupTableProperty_h #include "mitkBaseProperty.h" #include "mitkLookupTable.h" #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief The LookupTableProperty class Property to associate mitk::LookupTable * to an mitk::DataNode. * @ingroup DataManagement * * @note If you want to use this property to colorize an mitk::Image, make sure * to set the mitk::RenderingModeProperty to a mode which supports lookup tables * (e.g. LOOKUPTABLE_COLOR). Make sure to check the documentation of the * mitk::RenderingModeProperty. For a code example how to use the mitk::LookupTable * and this property check the mitkImageVtkMapper2DLookupTableTest.cpp in * Core/Code/Testing. */ class MITKCORE_EXPORT LookupTableProperty : public BaseProperty { protected: LookupTable::Pointer m_LookupTable; LookupTableProperty(); LookupTableProperty(const LookupTableProperty &); LookupTableProperty(const mitk::LookupTable::Pointer lut); public: typedef LookupTable::Pointer ValueType; mitkClassMacro(LookupTableProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(LookupTableProperty, const mitk::LookupTable::Pointer); itkGetObjectMacro(LookupTable, LookupTable); ValueType GetValue() const; void SetLookupTable(const mitk::LookupTable::Pointer aLookupTable); void SetValue(const ValueType &); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: // purposely not implemented LookupTableProperty &operator=(const LookupTableProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPoint.h b/Modules/Core/include/mitkPoint.h index 0436d5ae23..0f9c36682b 100644 --- a/Modules/Core/include/mitkPoint.h +++ b/Modules/Core/include/mitkPoint.h @@ -1,135 +1,153 @@ /*============================================================================ 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 mitkPoint_h #define mitkPoint_h #include -#include "mitkArray.h" -#include "mitkEqual.h" -#include "mitkNumericConstants.h" +#include +#include +#include + +#include namespace mitk { //##Documentation //##@brief enumeration of the type a point can be enum PointSpecificationType { PTUNDEFINED = 0, PTSTART, PTCORNER, PTEDGE, PTEND }; template class Point : public itk::Point { public: /** Default constructor has nothing to do. */ explicit Point() : itk::Point() {} /** Pass-through constructors for the Array base class. */ template explicit Point(const Point &r) : itk::Point(r) { } template explicit Point(const TPointValueType r[NPointDimension]) : itk::Point(r) { } template explicit Point(const TPointValueType &v) : itk::Point(v) { } Point(const mitk::Point &r) : itk::Point(r) { } Point(const TCoordRep r[NPointDimension]) : itk::Point(r) {} Point(const TCoordRep &v) : itk::Point(v) {} Point(const itk::Point &p) : itk::Point(p) { } /** * Copies the elements from array array to this. * Note that this method will assign doubles to floats without complaining! * * @param array the array whose values shall be copied. Must overload [] operator. */ template void FillPoint(const ArrayType &array) { itk::FixedArray *thisP = dynamic_cast *>(this); mitk::FillArray(*thisP, array); } /** * Copies the values stored in this point into the array array. * * @param array the array which should store the values of this. */ template void ToArray(ArrayType array) const { mitk::ToArray(array, *this); } }; + template + void to_json(nlohmann::json& j, const Point& p) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < NPointDimension; ++i) + j.push_back(p[i]); + } + + template + void from_json(const nlohmann::json& j, Point& p) + { + for (size_t i = 0; i < NPointDimension; ++i) + j.at(i).get_to(p[i]); + } + typedef Point Point2D; typedef Point Point3D; typedef Point Point4D; typedef Point Point2I; typedef Point Point3I; typedef Point Point4I; /** * @ingroup MITKTestingAPI * * @param point1 Point to compare. * @param point2 Point to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if points are equal. */ template inline bool Equal(const itk::Point &point1, const itk::Point &point2, TCoordRep eps = mitk::eps, bool verbose = false) { bool isEqual = true; typename itk::Point::VectorType diff = point1 - point2; for (unsigned int i = 0; i < NPointDimension; i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(point1, point2, eps, verbose, isEqual); return isEqual; } } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyDeserialization.h b/Modules/Core/include/mitkPropertyDeserialization.h new file mode 100644 index 0000000000..274bb1d22f --- /dev/null +++ b/Modules/Core/include/mitkPropertyDeserialization.h @@ -0,0 +1,41 @@ +/*============================================================================ + +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 mitkPropertyDeserialization_h +#define mitkPropertyDeserialization_h + +#include +#include + +namespace mitk +{ + class PropertyDeserialization : public IPropertyDeserialization + { + public: + PropertyDeserialization(); + virtual ~PropertyDeserialization(); + + PropertyDeserialization(const PropertyDeserialization&) = delete; + PropertyDeserialization& operator=(const PropertyDeserialization&) = delete; + + itk::SmartPointer CreateInstance(const std::string& className) override; + + protected: + void InternalRegisterProperty(const BaseProperty* property) override; + + private: + using MapType = std::map; + MapType m_Map; + }; +} + +#endif diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index 40f05857cf..763715f97b 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -1,254 +1,268 @@ /*============================================================================ 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 mitkPropertyList_h #define mitkPropertyList_h -#include "mitkBaseProperty.h" -#include "mitkGenericProperty.h" -#include "mitkUIDGenerator.h" -#include "mitkIPropertyOwner.h" -#include +#include +#include -#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 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(); + /** + * @brief Serialize the property list to JSON. + * + * @note Properties of a certain type can only be deseralized again if their type has been + * registered via the IPropertyDeserialization core service. + * + * @sa CoreServices + * @sa IPropertyDeserialization::RegisterProperty + */ + void ToJSON(nlohmann::json& j) const; + + /** + * @brief Deserialize the property list from JSON. + * + * @note Properties of a certain type can only be deseralized again if their type has been + * registered via the IPropertyDeserialization core service. + * + * @sa CoreServices + * @sa IPropertyDeserialization::RegisterProperty + */ + void FromJSON(const nlohmann::json& j); + 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 diff --git a/Modules/Core/include/mitkSmartPointerProperty.h b/Modules/Core/include/mitkSmartPointerProperty.h index aac5f37d69..ddf84f66b3 100644 --- a/Modules/Core/include/mitkSmartPointerProperty.h +++ b/Modules/Core/include/mitkSmartPointerProperty.h @@ -1,98 +1,101 @@ /*============================================================================ 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 mitkSmartPointerProperty_h #define mitkSmartPointerProperty_h #include "mitkBaseProperty.h" #include "mitkUIDGenerator.h" #include #include #include #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif //##Documentation //## @brief Property containing a smart-pointer //## @ingroup DataManagement class MITKCORE_EXPORT SmartPointerProperty : public BaseProperty { public: mitkClassMacro(SmartPointerProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(SmartPointerProperty, itk::Object*); typedef itk::Object::Pointer ValueType; itk::Object::Pointer GetSmartPointer() const; ValueType GetValue() const; void SetSmartPointer(itk::Object *); void SetValue(const ValueType &); /// mainly for XML output std::string GetValueAsString() const override; static void PostProcessXMLReading(); /// Return the number of SmartPointerProperties that reference the object given as parameter static unsigned int GetReferenceCountFor(itk::Object *); static std::string GetReferenceUIDFor(itk::Object *); static void RegisterPointerTarget(itk::Object *, const std::string uid); + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: SmartPointerProperty(itk::Object * = nullptr); SmartPointerProperty(const SmartPointerProperty &); itk::Object::Pointer m_SmartPointer; private: // purposely not implemented SmartPointerProperty &operator=(const SmartPointerProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &) const override; bool Assign(const BaseProperty &) override; typedef std::map ReferenceCountMapType; typedef std::map ReferencesUIDMapType; typedef std::map ReadInSmartPointersMapType; typedef std::map ReadInTargetsMapType; /// for each itk::Object* count how many SmartPointerProperties point to it static ReferenceCountMapType m_ReferenceCount; static ReferencesUIDMapType m_ReferencesUID; static ReadInSmartPointersMapType m_ReadInInstances; static ReadInTargetsMapType m_ReadInTargets; /// to generate unique IDs for the objects pointed at (during XML writing) static UIDGenerator m_UIDGenerator; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkStringProperty.h b/Modules/Core/include/mitkStringProperty.h index 04e7d744af..41962df4e1 100644 --- a/Modules/Core/include/mitkStringProperty.h +++ b/Modules/Core/include/mitkStringProperty.h @@ -1,78 +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 mitkStringProperty_h #define mitkStringProperty_h #include #include "mitkBaseProperty.h" #include #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Property for strings * @ingroup DataManagement */ class MITKCORE_EXPORT StringProperty : public BaseProperty { protected: std::string m_Value; StringProperty(const char *string = nullptr); StringProperty(const std::string &s); StringProperty(const StringProperty &); public: mitkClassMacro(StringProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(StringProperty, const char*); mitkNewMacro1Param(StringProperty, const std::string&); itkGetStringMacro(Value); itkSetStringMacro(Value); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + static const char *PATH; using BaseProperty::operator=; private: // purposely not implemented StringProperty &operator=(const StringProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTemporoSpatialStringProperty.h b/Modules/Core/include/mitkTemporoSpatialStringProperty.h index 595c8d1aca..8f6f090f93 100644 --- a/Modules/Core/include/mitkTemporoSpatialStringProperty.h +++ b/Modules/Core/include/mitkTemporoSpatialStringProperty.h @@ -1,136 +1,139 @@ /*============================================================================ 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 #define mitkTemporoSpatialStringProperty_h #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; /** 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; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + 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); + 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/mitkTransferFunctionProperty.h b/Modules/Core/include/mitkTransferFunctionProperty.h index e8bf9439cc..fd3c20d736 100644 --- a/Modules/Core/include/mitkTransferFunctionProperty.h +++ b/Modules/Core/include/mitkTransferFunctionProperty.h @@ -1,79 +1,82 @@ /*============================================================================ 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 mitkTransferFunctionProperty_h #define mitkTransferFunctionProperty_h #include "mitkBaseProperty.h" #include "mitkTransferFunction.h" namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief The TransferFunctionProperty class Property class for the mitk::TransferFunction. * @ingroup DataManagement * * @note If you want to use this property for an mitk::Image, make sure * to set the mitk::RenderingModeProperty to a mode which supports transfer * functions (e.g. COLORTRANSFERFUNCTION_COLOR). Make sure to check the * documentation of the mitk::RenderingModeProperty. For a code example how * to use the mitk::TransferFunction check the * mitkImageVtkMapper2DTransferFunctionTest.cpp in Core/Code/Testing. */ class MITKCORE_EXPORT TransferFunctionProperty : public BaseProperty { public: typedef mitk::TransferFunction::Pointer ValueType; mitkClassMacro(TransferFunctionProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(TransferFunctionProperty, mitk::TransferFunction::Pointer); itkSetMacro(Value, mitk::TransferFunction::Pointer); itkGetConstMacro(Value, mitk::TransferFunction::Pointer); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: mitk::TransferFunction::Pointer m_Value; TransferFunctionProperty(); TransferFunctionProperty(const TransferFunctionProperty &other); TransferFunctionProperty(mitk::TransferFunction::Pointer value); private: // purposely not implemented TransferFunctionProperty &operator=(const TransferFunctionProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkVector.h b/Modules/Core/include/mitkVector.h index fca4c9047d..6c5bc2ddaf 100644 --- a/Modules/Core/include/mitkVector.h +++ b/Modules/Core/include/mitkVector.h @@ -1,239 +1,257 @@ /*============================================================================ 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 mitkVector_h #define mitkVector_h #include #include #include -#include "mitkArray.h" -#include "mitkEqual.h" -#include "mitkExceptionMacro.h" -#include "mitkNumericConstants.h" +#include +#include +#include +#include + +#include namespace mitk { template class Vector : public itk::Vector { public: /** * @brief Default constructor has nothing to do. */ explicit Vector() : itk::Vector() {} /** * @brief Copy constructor. */ explicit Vector(const mitk::Vector &r) : itk::Vector(r) { } /** Pass-through assignment operator for the Vector base class. */ Vector & operator=(const Vector & r) { itk::Vector::operator=(r); return *this; } /** * @brief Constructor to convert from itk::Vector to mitk::Vector. */ Vector(const itk::Vector &r) : itk::Vector(r) { } /** * @brief Constructor to convert an array to mitk::Vector * @param r the array. * @attention must have NVectorDimension valid arguments! */ Vector(const TCoordRep r[NVectorDimension]) : itk::Vector(r) { } /** * Constructor to initialize entire vector to one value. */ Vector(const TCoordRep &v) : itk::Vector(v) {} /** * @brief Constructor for vnl_vectors. * @throws mitk::Exception if vnl_vector.size() != NVectorDimension. */ Vector(const vnl_vector &vnlVector) : itk::Vector() { if (vnlVector.size() != NVectorDimension) mitkThrow() << "when constructing mitk::Vector from vnl_vector: sizes didn't match: mitk::Vector " << NVectorDimension << "; vnl_vector " << vnlVector.size(); for (unsigned int var = 0; (var < NVectorDimension) && (var < vnlVector.size()); ++var) { this->SetElement(var, vnlVector.get(var)); } } /** * @brief Constructor for vnl_vector_fixed. */ Vector(const vnl_vector_fixed &vnlVectorFixed) : itk::Vector() { for (unsigned int var = 0; var < NVectorDimension; ++var) { this->SetElement(var, vnlVectorFixed[var]); } }; /** * Copies the elements from array array to this. * Note that this method will assign doubles to floats without complaining! * * @param array the array whose values shall be copied. Must overload [] operator. */ template void FillVector(const ArrayType &array) { itk::FixedArray *thisP = dynamic_cast *>(this); mitk::FillArray(*thisP, array); } /** * Copies the values stored in this vector into the array array.d * * @param array the array which should store the values of this. */ template void ToArray(ArrayType array) const { mitk::ToArray(array, *this); } /** * @brief User defined conversion of mitk::Vector to vnl_vector. * Note: the conversion to mitk::Vector to vnl_vector_fixed has not been implemented since this * would collide with the conversion vnl_vector to vnl_vector_fixed provided by vnl. */ operator vnl_vector() const { return this->GetVnlVector(); } }; // end mitk::Vector + template + void to_json(nlohmann::json &j, const Vector &v) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < NVectorDimension; ++i) + j.push_back(v[i]); + } + + template + void from_json(const nlohmann::json &j, Vector &v) + { + for (size_t i = 0; i < NVectorDimension; ++i) + j.at(i).get_to(v[i]); + } + // convenience typedefs for often used mitk::Vector representations. typedef Vector Vector2D; typedef Vector Vector3D; typedef Vector Vector4D; // other vector types used in MITK typedef vnl_vector VnlVector; // The equal methods to compare vectors for equality are below: /** * @ingroup MITKTestingAPI * * @param vector1 Vector to compare. * @param vector2 Vector to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if vectors are equal. */ template inline bool Equal(const itk::Vector &vector1, const itk::Vector &vector2, TCoordRep eps = mitk::eps, bool verbose = false) { bool isEqual = true; typename itk::Vector::VectorType diff = vector1 - vector2; for (unsigned int i = 0; i < NPointDimension; i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); return isEqual; } /** * @ingroup MITKTestingAPI * * @param vector1 Vector to compare. * @param vector2 Vector to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if vectors are equal. */ inline bool Equal(const mitk::VnlVector &vector1, const mitk::VnlVector &vector2, ScalarType eps = mitk::eps, bool verbose = false) { bool isEqual = true; mitk::VnlVector diff = vector1 - vector2; for (unsigned int i = 0; i < diff.size(); i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); return isEqual; } /** * @ingroup MITKTestingAPI * * @param vector1 Vector to compare. * @param vector2 Vector to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if vectors are equal. */ template inline bool Equal(const vnl_vector_fixed &vector1, const vnl_vector_fixed &vector2, TCoordRep eps = mitk::eps, bool verbose = false) { vnl_vector_fixed diff = vector1 - vector2; bool isEqual = true; for (unsigned int i = 0; i < diff.size(); i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); return isEqual; } } // end namespace mitk #endif diff --git a/Modules/Core/include/mitkVectorProperty.h b/Modules/Core/include/mitkVectorProperty.h index be0b00f767..547a5d5caf 100644 --- a/Modules/Core/include/mitkVectorProperty.h +++ b/Modules/Core/include/mitkVectorProperty.h @@ -1,133 +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 mitkVectorProperty_h #define mitkVectorProperty_h // MITK #include #include // STL #include namespace mitk { /** \brief Helper for VectorProperty to determine a good ITK ClassName. This template is specialized for special instantiations that need a serializer of this VectorProperty. */ template struct VectorPropertyDataType { static const char *prefix() { return "Invalid"; } }; /** \brief Providing a std::vector as property. Templated over the data type of the std::vector that is held by this class. Nothing special about data handling, setting and getting of std::vectors is implemented by-value. When checking the declaration of this class, you'll notice that it does not use the mitkClassMacro but instead writes all of its definition manually. This is in order to specifically override the GetNameOfClass() method without having to inherit again from the template (see comments in code). */ template class MITKCORE_EXPORT VectorProperty : public BaseProperty { public: typedef std::vector VectorType; // Manually expand most of mitkClassMacro: // mitkClassMacro(VectorProperty, mitk::BaseProperty); // This manual expansion is done to override explicitly // the GetNameOfClass() and GetStaticNameOfClass() methods typedef VectorProperty Self; typedef BaseProperty SuperClass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; std::vector GetClassHierarchy() const override { return mitk::GetClassHierarchy(); } /// This function must return different /// strings in function of the template parameter! /// Serialization depends on this feature. static const char *GetStaticNameOfClass() { // concatenate a prefix dependent on the template type and our own classname static std::string nameOfClass = std::string(VectorPropertyDataType::prefix()) + "VectorProperty"; return nameOfClass.c_str(); } const char *GetNameOfClass() const override { return this->GetStaticNameOfClass(); } itkFactorylessNewMacro(Self); itkCloneMacro(Self); /// Returns the property value as a std::string. /// /// Since VectorProperty potentially holds many /// elements, it implements this function in a way /// that only the first and the last couple of /// elements really appear in the string. /// Missing central elements are indicated by /// an ellipsis ("...") std::string GetValueAsString() const override; /// returns a const reference to the contained vector virtual const VectorType &GetValue() const; /// sets the content vector virtual void SetValue(const VectorType ¶meter_vector); + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + private: /// purposely not implemented VectorProperty &operator=(const Self &); /// creates a copy of it self itk::LightObject::Pointer InternalClone() const override; /// compares two properties. bool IsEqual(const BaseProperty &an_other_property) const override; /// assigns the content of an_other_property to this bool Assign(const BaseProperty &an_other_property) override; /// property content VectorType m_PropertyContent; }; /// This should be used in .h files. #define MITK_DECLARE_VECTOR_PROPERTY(TYPE, PREFIX) \ \ typedef VectorProperty PREFIX##VectorProperty; \ \ template <> \ \ struct VectorPropertyDataType \ { \ static const char *prefix() { return #PREFIX; } \ }; /// This should be used in a .cpp file #define MITK_DEFINE_VECTOR_PROPERTY(TYPE) template class VectorProperty; MITK_DECLARE_VECTOR_PROPERTY(double, Double) MITK_DECLARE_VECTOR_PROPERTY(int, Int) } // namespace #endif diff --git a/Modules/Core/include/mitkWeakPointerProperty.h b/Modules/Core/include/mitkWeakPointerProperty.h index aba93c9c37..db11e06a19 100644 --- a/Modules/Core/include/mitkWeakPointerProperty.h +++ b/Modules/Core/include/mitkWeakPointerProperty.h @@ -1,78 +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 mitkWeakPointerProperty_h #define mitkWeakPointerProperty_h #include "itkWeakPointer.h" #include "mitkBaseProperty.h" #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif //##Documentation //## @brief Property containing a smart-pointer //## //## @ingroup DataManagement class MITKCORE_EXPORT WeakPointerProperty : public BaseProperty { public: mitkClassMacro(WeakPointerProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(WeakPointerProperty, itk::Object*); ~WeakPointerProperty() override; typedef itk::WeakPointer ValueType; ValueType GetWeakPointer() const; ValueType GetValue() const; void SetWeakPointer(itk::Object *pointer); void SetValue(const ValueType &value); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: itk::WeakPointer m_WeakPointer; WeakPointerProperty(const WeakPointerProperty &); WeakPointerProperty(itk::Object *pointer = nullptr); private: // purposely not implemented WeakPointerProperty &operator=(const WeakPointerProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp b/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp index 303e0c80f0..d3463856e3 100644 --- a/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp @@ -1,98 +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. ============================================================================*/ #include "mitkAnnotationProperty.h" mitk::AnnotationProperty::AnnotationProperty() : m_Position(0.0) { } mitk::AnnotationProperty::AnnotationProperty(const char *label, const Point3D &position) : m_Label(""), m_Position(position) { if (label != nullptr) { m_Label = label; } } mitk::AnnotationProperty::AnnotationProperty(const std::string &label, const Point3D &position) : m_Label(label), m_Position(position) { } mitk::AnnotationProperty::AnnotationProperty(const char *label, ScalarType x, ScalarType y, ScalarType z) : m_Label("") { if (label != nullptr) { m_Label = label; } m_Position[0] = x; m_Position[1] = y; m_Position[2] = z; } mitk::AnnotationProperty::AnnotationProperty(const std::string &label, ScalarType x, ScalarType y, ScalarType z) : m_Label(label) { m_Position[0] = x; m_Position[1] = y; m_Position[2] = z; } mitk::AnnotationProperty::AnnotationProperty(const mitk::AnnotationProperty &other) : BaseProperty(other), m_Label(other.m_Label), m_Position(other.m_Position) { } const mitk::Point3D &mitk::AnnotationProperty::GetPosition() const { return m_Position; } void mitk::AnnotationProperty::SetPosition(const mitk::Point3D &position) { if (m_Position != position) { m_Position = position; this->Modified(); } } bool mitk::AnnotationProperty::IsEqual(const BaseProperty &property) const { return ((this->m_Label == static_cast(property).m_Label) && (this->m_Position == static_cast(property).m_Position)); } bool mitk::AnnotationProperty::Assign(const BaseProperty &property) { this->m_Label = static_cast(property).m_Label; this->m_Position = static_cast(property).m_Position; return true; } std::string mitk::AnnotationProperty::GetValueAsString() const { std::stringstream myStr; myStr << this->GetLabel() << this->GetPosition(); return myStr.str(); } itk::LightObject::Pointer mitk::AnnotationProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } + +bool mitk::AnnotationProperty::ToJSON(nlohmann::json& j) const +{ + j = nlohmann::json{ + {"Label", this->GetLabel()}, + {"Position", this->GetPosition()}}; + + return true; +} + +bool mitk::AnnotationProperty::FromJSON(const nlohmann::json& j) +{ + this->SetLabel(j["Label"].get()); + this->SetPosition(j["Position"].get()); + return true; +} diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index 6a3a8352e7..183e402aad 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1185 +1,1190 @@ /*============================================================================ 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(); } +const vtkMatrix4x4* mitk::BaseGeometry::GetVtkMatrix() const +{ + 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; } void mitk::BaseGeometry::MapAxesToOrientations(int axes[]) const { auto affineTransform = this->GetIndexToWorldTransform(); auto matrix = affineTransform->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); auto inverseMatrix = matrix.GetInverse(); bool mapped[3] = {false, false, false}; // We need to allow an epsilon difference to ignore rounding. const double eps = 0.0001; for (int orientation = 0; orientation < 3; ++orientation) { auto absX = std::abs(inverseMatrix[0][orientation]); auto absY = std::abs(inverseMatrix[1][orientation]); auto absZ = std::abs(inverseMatrix[2][orientation]); // First we check if there is a single maximum value. If there is, we found the axis // that corresponds to the given orientation. If there is no single maximum value, // we choose one from the the two or three axes that have the maximum value, but we // need to make sure that we do not map the same axis to different orientations. // Equal values are valid if the volume is rotated by exactly 45 degrees around one // axis. If the volume is rotated by 45 degrees around two axes, you will get single // maximum values at the same axes for two different orientations. In this case, // the axis is mapped to one of the orientations, and for the other orientation we // choose a different axis that has not been mapped yet, even if it is not a maximum. if (absX > absY + eps) { if (absX > absZ + eps) { // x is the greatest int axis = !mapped[0] ? 0 : !mapped[1] ? 1 : 2; axes[orientation] = axis; mapped[axis] = true; } else { // z is the greatest OR x and z are equal and greater than y int axis = !mapped[2] ? 2 : !mapped[0] ? 0 : 1; axes[orientation] = axis; mapped[axis] = true; } } else if (absY > absX + eps) { if (absY > absZ + eps) { // y is the greatest int axis = !mapped[1] ? 1 : !mapped[2] ? 2 : 0; axes[orientation] = axis; mapped[axis] = true; } else { // z is the greatest OR y and z are equal and greater than x int axis = !mapped[2] ? 2 : !mapped[1] ? 1 : 0; axes[orientation] = axis; mapped[axis] = true; } } else { if (absZ > absX + eps) { // z is the greatest int axis = !mapped[2] ? 2 : !mapped[0] ? 0 : 1; axes[orientation] = axis; mapped[axis] = true; } else { // x and y are equal and greater than z OR x and y and z are equal int axis = !mapped[0] ? 0 : !mapped[1] ? 1 : 2; axes[orientation] = axis; mapped[axis] = true; } } } assert(mapped[0] && mapped[1] && mapped[2]); } 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 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 "<Modified(); } } const Point3D &ClippingProperty::GetOrigin() const { return m_Origin; } void ClippingProperty::SetOrigin(const Point3D &origin) { if (m_Origin != origin) { m_Origin = origin; this->Modified(); } } const Vector3D &ClippingProperty::GetNormal() const { return m_Normal; } void ClippingProperty::SetNormal(const Vector3D &normal) { if (m_Normal != normal) { m_Normal = normal; this->Modified(); } } bool ClippingProperty::IsEqual(const BaseProperty &property) const { return ((this->m_ClippingEnabled == static_cast(property).m_ClippingEnabled) && (this->m_Origin == static_cast(property).m_Origin) && (this->m_Normal == static_cast(property).m_Normal)); } bool ClippingProperty::Assign(const BaseProperty &property) { this->m_ClippingEnabled = static_cast(property).m_ClippingEnabled; this->m_Origin = static_cast(property).m_Origin; this->m_Normal = static_cast(property).m_Normal; return true; } std::string ClippingProperty::GetValueAsString() const { std::stringstream myStr; myStr << this->GetClippingEnabled() << this->GetOrigin() << this->GetNormal(); return myStr.str(); } + bool ClippingProperty::ToJSON(nlohmann::json& j) const + { + j = nlohmann::json{ + {"Enabled", this->GetClippingEnabled()}, + {"Origin", this->GetOrigin()}, + {"Normal", this->GetNormal()}}; + + return true; + } + + bool ClippingProperty::FromJSON(const nlohmann::json& j) + { + this->SetClippingEnabled(j["Enabled"].get()); + this->SetOrigin(j["Origin"].get()); + this->SetNormal(j["Normal"].get()); + + return true; + } + itk::LightObject::Pointer ClippingProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkColorProperty.cpp b/Modules/Core/src/DataManagement/mitkColorProperty.cpp index 9288a7343e..25d48f86a3 100644 --- a/Modules/Core/src/DataManagement/mitkColorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkColorProperty.cpp @@ -1,90 +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. ============================================================================*/ #include "mitkColorProperty.h" #include +#include mitk::ColorProperty::ColorProperty() : m_Color(0.0f) { } mitk::ColorProperty::ColorProperty(const mitk::ColorProperty &other) : BaseProperty(other), m_Color(other.m_Color) { } mitk::ColorProperty::ColorProperty(const float color[3]) : m_Color(color) { } mitk::ColorProperty::ColorProperty(const float red, const float green, const float blue) { m_Color.Set(red, green, blue); } mitk::ColorProperty::ColorProperty(const mitk::Color &color) : m_Color(color) { } bool mitk::ColorProperty::IsEqual(const BaseProperty &property) const { return this->m_Color == static_cast(property).m_Color; } bool mitk::ColorProperty::Assign(const BaseProperty &property) { this->m_Color = static_cast(property).m_Color; return true; } const mitk::Color &mitk::ColorProperty::GetColor() const { return m_Color; } void mitk::ColorProperty::SetColor(const mitk::Color &color) { if (m_Color != color) { m_Color = color; Modified(); } } void mitk::ColorProperty::SetValue(const mitk::Color &color) { SetColor(color); } void mitk::ColorProperty::SetColor(float red, float green, float blue) { float tmp[3] = {red, green, blue}; SetColor(mitk::Color(tmp)); } std::string mitk::ColorProperty::GetValueAsString() const { std::stringstream myStr; myStr.imbue(std::locale::classic()); myStr << GetValue(); return myStr.str(); } const mitk::Color &mitk::ColorProperty::GetValue() const { return GetColor(); } +bool mitk::ColorProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetColor(); + return true; +} + +bool mitk::ColorProperty::FromJSON(const nlohmann::json& j) +{ + this->SetColor(j.get()); + return true; +} + itk::LightObject::Pointer mitk::ColorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp index e6e3d27852..2019bf5375 100644 --- a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp @@ -1,166 +1,183 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include +#include mitk::EnumerationProperty::EnumerationProperty() : m_CurrentValue(0) { } mitk::EnumerationProperty::EnumerationProperty(const EnumerationProperty &other) : BaseProperty(other), m_CurrentValue(other.m_CurrentValue), m_IdMap(other.m_IdMap), m_NameMap(other.m_NameMap) { } bool mitk::EnumerationProperty::AddEnum(const std::string &name, const IdType &id) { if (false == this->IsValidEnumerationValue(id) && false == this->IsValidEnumerationValue(name)) { this->GetEnumIds().insert(std::make_pair(id, name)); this->GetEnumStrings().insert(std::make_pair(name, id)); return true; } return false; } bool mitk::EnumerationProperty::SetValue(const std::string &name) { if (this->IsValidEnumerationValue(name)) { m_CurrentValue = this->GetEnumId(name); this->Modified(); return true; } return false; } bool mitk::EnumerationProperty::SetValue(const IdType &id) { if (this->IsValidEnumerationValue(id)) { m_CurrentValue = id; this->Modified(); return true; } return false; } mitk::EnumerationProperty::IdType mitk::EnumerationProperty::GetValueAsId() const { return m_CurrentValue; } std::string mitk::EnumerationProperty::GetValueAsString() const { return this->GetEnumString(m_CurrentValue); } void mitk::EnumerationProperty::Clear() { this->GetEnumIds().clear(); this->GetEnumStrings().clear(); m_CurrentValue = 0; } mitk::EnumerationProperty::EnumIdsContainerType::size_type mitk::EnumerationProperty::Size() const { return this->GetEnumIds().size(); } mitk::EnumerationProperty::EnumConstIterator mitk::EnumerationProperty::Begin() const { return this->GetEnumIds().cbegin(); } mitk::EnumerationProperty::EnumConstIterator mitk::EnumerationProperty::End() const { return this->GetEnumIds().cend(); } std::string mitk::EnumerationProperty::GetEnumString(const IdType &id) const { return this->IsValidEnumerationValue(id) ? this->GetEnumIds().find(id)->second : std::string("invalid enum id or enums empty"); } mitk::EnumerationProperty::IdType mitk::EnumerationProperty::GetEnumId(const std::string &name) const { return this->IsValidEnumerationValue(name) ? this->GetEnumStrings().find(name)->second : 0; } bool mitk::EnumerationProperty::IsEqual(const BaseProperty &property) const { const auto &other = static_cast(property); return this->Size() == other.Size() && this->GetValueAsId() == other.GetValueAsId() && std::equal(this->Begin(), this->End(), other.Begin()); } bool mitk::EnumerationProperty::Assign(const BaseProperty &property) { const auto &other = static_cast(property); this->GetEnumIds() = other.GetEnumIds(); this->GetEnumStrings() = other.GetEnumStrings(); m_CurrentValue = other.m_CurrentValue; return true; } bool mitk::EnumerationProperty::IsValidEnumerationValue(const IdType &id) const { return this->GetEnumIds().end() != this->GetEnumIds().find(id); } bool mitk::EnumerationProperty::IsValidEnumerationValue(const std::string &name) const { return this->GetEnumStrings().end() != this->GetEnumStrings().find(name); } mitk::EnumerationProperty::EnumIdsContainerType & mitk::EnumerationProperty::GetEnumIds() { return m_IdMap; } const mitk::EnumerationProperty::EnumIdsContainerType & mitk::EnumerationProperty::GetEnumIds() const { return m_IdMap; } mitk::EnumerationProperty::EnumStringsContainerType & mitk::EnumerationProperty::GetEnumStrings() { return m_NameMap; } const mitk::EnumerationProperty::EnumStringsContainerType & mitk::EnumerationProperty::GetEnumStrings() const { return m_NameMap; } + +bool mitk::EnumerationProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetValueAsString(); + return true; +} + +bool mitk::EnumerationProperty::FromJSON(const nlohmann::json& j) +{ + auto name = j.get(); + + if (!this->SetValue(name)) + mitkThrow() << '"' << name << "\" is not a valid enumeration value for " << this->GetNameOfClass() << '!'; + + return true; +} diff --git a/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp b/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp index 2fc2aa5e05..272bbec8ff 100644 --- a/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp @@ -1,39 +1,51 @@ /*============================================================================ 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 "mitkGroupTagProperty.h" +#include mitk::GroupTagProperty::GroupTagProperty() : mitk::BaseProperty() { } mitk::GroupTagProperty::GroupTagProperty(const GroupTagProperty &other) : mitk::BaseProperty(other) { } bool mitk::GroupTagProperty::IsEqual(const BaseProperty & /*property*/) const { // if other property is also a GroupTagProperty, then it is equal to us, because tags have no value themselves return true; } bool mitk::GroupTagProperty::Assign(const BaseProperty & /*property*/) { return true; } +bool mitk::GroupTagProperty::ToJSON(nlohmann::json& j) const +{ + j = nlohmann::json::object(); + return true; +} + +bool mitk::GroupTagProperty::FromJSON(const nlohmann::json&) +{ + return true; +} + itk::LightObject::Pointer mitk::GroupTagProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkLine.cpp b/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp similarity index 78% rename from Modules/Core/src/DataManagement/mitkLine.cpp rename to Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp index 4d6ae904bc..bb3e4b2c12 100644 --- a/Modules/Core/src/DataManagement/mitkLine.cpp +++ b/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp @@ -1,13 +1,17 @@ /*============================================================================ 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 "mitkLine.h" +#include "mitkIPropertyDeserialization.h" + +mitk::IPropertyDeserialization::~IPropertyDeserialization() +{ +} diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index c3d8841919..be8a030d6a 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -1,510 +1,548 @@ /*============================================================================ 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 +#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; upperBound = std::clamp(upperBound, -1e300, 1e300); lowerBound = std::clamp(lowerBound, -1e300, 1e300); 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 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 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; } } + +namespace mitk +{ + void to_json(nlohmann::json& j, const LevelWindow& lw) + { + j = nlohmann::json{ + {"Fixed", lw.IsFixed()}, + {"IsFloatingImage", lw.IsFloatingValues()}, + {"CurrentSettings", { + {"Level", lw.GetLevel()}, + {"Window", lw.GetWindow()}}}, + {"DefaultSettings", { + {"Level", lw.GetDefaultLevel()}, + {"Window", lw.GetDefaultWindow()}}}, + {"CurrentRange", { + {"Min", lw.GetRangeMin()}, + {"Max", lw.GetRangeMax()}}}}; + } + + void from_json(const nlohmann::json& j, LevelWindow& lw) + { + lw.SetRangeMinMax( + j["CurrentRange"]["Min"].get(), + j["CurrentRange"]["Max"].get()); + + lw.SetDefaultLevelWindow( + j["DefaultSettings"]["Level"].get(), + j["DefaultSettings"]["Window"].get()); + + lw.SetLevelWindow( + j["CurrentSettings"]["Level"].get(), + j["CurrentSettings"]["Window"].get()); + + lw.SetFixed(j["Fixed"].get()); + lw.SetFloatingValues(j["IsFloatingImage"].get()); + } +} diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp index 5376d63183..2f82af0ba7 100755 --- a/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp @@ -1,80 +1,92 @@ /*============================================================================ 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 "mitkLevelWindowProperty.h" mitk::LevelWindowProperty::LevelWindowProperty() { } mitk::LevelWindowProperty::LevelWindowProperty(const mitk::LevelWindowProperty &other) : BaseProperty(other), m_LevWin(other.m_LevWin) { } mitk::LevelWindowProperty::LevelWindowProperty(const mitk::LevelWindow &levWin) { SetLevelWindow(levWin); } mitk::LevelWindowProperty::~LevelWindowProperty() { } bool mitk::LevelWindowProperty::IsEqual(const BaseProperty &property) const { return this->m_LevWin == static_cast(property).m_LevWin; } bool mitk::LevelWindowProperty::Assign(const BaseProperty &property) { this->m_LevWin = static_cast(property).m_LevWin; return true; } const mitk::LevelWindow &mitk::LevelWindowProperty::GetLevelWindow() const { return m_LevWin; } const mitk::LevelWindow &mitk::LevelWindowProperty::GetValue() const { return GetLevelWindow(); } void mitk::LevelWindowProperty::SetLevelWindow(const mitk::LevelWindow &levWin) { if (m_LevWin != levWin) { m_LevWin = levWin; Modified(); } } void mitk::LevelWindowProperty::SetValue(const ValueType &levWin) { SetLevelWindow(levWin); } std::string mitk::LevelWindowProperty::GetValueAsString() const { std::stringstream myStr; myStr << "L:" << m_LevWin.GetLevel() << " W:" << m_LevWin.GetWindow(); return myStr.str(); } +bool mitk::LevelWindowProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetLevelWindow(); + return true; +} + +bool mitk::LevelWindowProperty::FromJSON(const nlohmann::json& j) +{ + this->SetLevelWindow(j.get()); + return true; +} + itk::LightObject::Pointer mitk::LevelWindowProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp index c079e4f0c4..43bf40cf23 100644 --- a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp @@ -1,73 +1,153 @@ /*============================================================================ 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 "mitkLookupTableProperty.h" +#include mitk::LookupTableProperty::LookupTableProperty() { mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); this->SetLookupTable(lut); } mitk::LookupTableProperty::LookupTableProperty(const LookupTableProperty &other) : mitk::BaseProperty(other), m_LookupTable(other.m_LookupTable) { } mitk::LookupTableProperty::LookupTableProperty(const mitk::LookupTable::Pointer lut) { this->SetLookupTable(lut); } bool mitk::LookupTableProperty::IsEqual(const BaseProperty &property) const { return *(this->m_LookupTable) == *(static_cast(property).m_LookupTable); } bool mitk::LookupTableProperty::Assign(const BaseProperty &property) { this->m_LookupTable = static_cast(property).m_LookupTable; return true; } std::string mitk::LookupTableProperty::GetValueAsString() const { std::stringstream ss; ss << m_LookupTable; return ss.str(); } mitk::LookupTableProperty::ValueType mitk::LookupTableProperty::GetValue() const { return m_LookupTable; } void mitk::LookupTableProperty::SetLookupTable(const mitk::LookupTable::Pointer aLookupTable) { if ((m_LookupTable != aLookupTable) || (*m_LookupTable != *aLookupTable)) { m_LookupTable = aLookupTable; Modified(); } } void mitk::LookupTableProperty::SetValue(const ValueType &value) { SetLookupTable(value); } +bool mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const +{ + auto lut = this->GetValue()->GetVtkLookupTable(); + + auto table = nlohmann::json::array(); + auto numTableValues = lut->GetNumberOfTableValues(); + + for (decltype(numTableValues) i = 0; i < numTableValues; ++i) + { + auto value = nlohmann::json::array(); + + for (size_t j = 0; j < 4; ++j) + value.push_back(lut->GetTableValue(i)[j]); + + table.push_back(value); + } + + j = nlohmann::json::object(); + j["NumberOfColors"] = static_cast(lut->GetNumberOfTableValues()); + j["Scale"] = lut->GetScale(); + j["Ramp"] = lut->GetRamp(); + j["HueRange"] = nlohmann::json::array({ lut->GetHueRange()[0], lut->GetHueRange()[1] }); + j["ValueRange"] = nlohmann::json::array({ lut->GetValueRange()[0], lut->GetValueRange()[1] }); + j["SaturationRange"] = nlohmann::json::array({ lut->GetSaturationRange()[0], lut->GetSaturationRange()[1] }); + j["AlphaRange"] = nlohmann::json::array({ lut->GetAlphaRange()[0], lut->GetAlphaRange()[1] }); + j["TableRange"] = nlohmann::json::array({ lut->GetTableRange()[0], lut->GetTableRange()[1] }); + j["Table"] = table; + + return true; +} + +bool mitk::LookupTableProperty::FromJSON(const nlohmann::json& j) +{ + auto lut = vtkSmartPointer::New(); + + lut->SetNumberOfTableValues(j["NumberOfColors"].get()); + lut->SetScale(j["Scale"].get()); + lut->SetScale(j["Ramp"].get()); + + std::array range; + + j["HueRange"][0].get_to(range[0]); + j["HueRange"][1].get_to(range[1]); + lut->SetHueRange(range.data()); + + j["ValueRange"][0].get_to(range[0]); + j["ValueRange"][1].get_to(range[1]); + lut->SetValueRange(range.data()); + + j["SaturationRange"][0].get_to(range[0]); + j["SaturationRange"][1].get_to(range[1]); + lut->SetSaturationRange(range.data()); + + j["AlphaRange"][0].get_to(range[0]); + j["AlphaRange"][1].get_to(range[1]); + lut->SetAlphaRange(range.data()); + + j["TableRange"][0].get_to(range[0]); + j["TableRange"][1].get_to(range[1]); + lut->SetTableRange(range.data()); + + std::array rgba; + vtkIdType i = 0; + + for (const auto& value : j["Table"]) + { + for (size_t j = 0; j < 4; ++j) + value[j].get_to(rgba[j]); + + lut->SetTableValue(i++, rgba.data()); + } + + auto mitkLut = LookupTable::New(); + mitkLut->SetVtkLookupTable(lut); + this->SetLookupTable(mitkLut); + + return true; +} + itk::LightObject::Pointer mitk::LookupTableProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp new file mode 100644 index 0000000000..251f2b9ff6 --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp @@ -0,0 +1,37 @@ +/*============================================================================ + +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 + +mitk::PropertyDeserialization::PropertyDeserialization() +{ +} + +mitk::PropertyDeserialization::~PropertyDeserialization() +{ +} + +mitk::BaseProperty::Pointer mitk::PropertyDeserialization::CreateInstance(const std::string& className) +{ + auto it = m_Map.find(className); + + if (it != m_Map.end()) + return static_cast(it->second->CreateAnother().GetPointer()); + + return nullptr; +} + +void mitk::PropertyDeserialization::InternalRegisterProperty(const BaseProperty* property) +{ + m_Map[property->GetNameOfClass()] = property; +} diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index d15eac3f6e..88d5d7e34c 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -1,376 +1,422 @@ /*============================================================================ 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" +#include +#include +#include +#include +#include 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 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)); } + +void mitk::PropertyList::ToJSON(nlohmann::json& j) const +{ + j = nlohmann::json::object(); + + for (const auto& [name, property] : m_Properties) + { + nlohmann::json serializedProperty; + + if (property->ToJSON(serializedProperty)) + j[property->GetNameOfClass()][name] = serializedProperty; + } + + auto test = PropertyList::New(); + test->FromJSON(j); +} + +void mitk::PropertyList::FromJSON(const nlohmann::json& j) +{ + mitk::CoreServicePointer service(mitk::CoreServices::GetPropertyDeserialization()); + PropertyMap properties; + + auto jObject = j.get(); + + for (const auto& [type, serializedProperties] : jObject) + { + auto prototype = service->CreateInstance(type); + + if (prototype.IsNull()) + { + MITK_ERROR << "Cannot create instance(s) of class \"" << type << "\"!"; + continue; + } + + auto serializedPropertiesObject = serializedProperties.get(); + + for (const auto& [name, serializedProperty] : serializedPropertiesObject) + { + BaseProperty::Pointer property = static_cast(prototype->CreateAnother().GetPointer()); + property->FromJSON(serializedProperty); + properties[name] = property; + } + } + + m_Properties = properties; +} diff --git a/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp b/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp index 0ca671f20d..dad9fa882d 100644 --- a/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp @@ -1,136 +1,146 @@ /*============================================================================ 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 "mitkSmartPointerProperty.h" mitk::SmartPointerProperty::ReferenceCountMapType mitk::SmartPointerProperty::m_ReferenceCount; mitk::SmartPointerProperty::ReferencesUIDMapType mitk::SmartPointerProperty::m_ReferencesUID; mitk::SmartPointerProperty::ReadInSmartPointersMapType mitk::SmartPointerProperty::m_ReadInInstances; mitk::SmartPointerProperty::ReadInTargetsMapType mitk::SmartPointerProperty::m_ReadInTargets; mitk::UIDGenerator mitk::SmartPointerProperty::m_UIDGenerator("POINTER_"); void mitk::SmartPointerProperty::PostProcessXMLReading() { for (auto iter = m_ReadInInstances.begin(); iter != m_ReadInInstances.end(); ++iter) { if (m_ReadInTargets.find(iter->second) != m_ReadInTargets.end()) { iter->first->SetSmartPointer(m_ReadInTargets[iter->second]); } } m_ReadInInstances.clear(); } /// \return The number of SmartPointerProperties that point to @param object unsigned int mitk::SmartPointerProperty::GetReferenceCountFor(itk::Object *object) { if (m_ReferenceCount.find(object) != m_ReferenceCount.end()) { return m_ReferenceCount[object]; } else { return 0; } } void mitk::SmartPointerProperty::RegisterPointerTarget(itk::Object *object, const std::string uid) { m_ReadInTargets[uid] = object; } std::string mitk::SmartPointerProperty::GetReferenceUIDFor(itk::Object *object) { if (m_ReferencesUID.find(object) != m_ReferencesUID.end()) { return m_ReferencesUID[object]; } else { return std::string("invalid"); } } bool mitk::SmartPointerProperty::IsEqual(const BaseProperty &property) const { return this->m_SmartPointer == static_cast(property).m_SmartPointer; } bool mitk::SmartPointerProperty::Assign(const BaseProperty &property) { this->m_SmartPointer = static_cast(property).m_SmartPointer; return true; } mitk::SmartPointerProperty::SmartPointerProperty(itk::Object *pointer) { SetSmartPointer(pointer); } mitk::SmartPointerProperty::SmartPointerProperty(const SmartPointerProperty &other) : BaseProperty(other), m_SmartPointer(other.m_SmartPointer) { } itk::Object::Pointer mitk::SmartPointerProperty::GetSmartPointer() const { return m_SmartPointer; } itk::Object::Pointer mitk::SmartPointerProperty::GetValue() const { return this->GetSmartPointer(); } void mitk::SmartPointerProperty::SetSmartPointer(itk::Object *pointer) { if (m_SmartPointer.GetPointer() != pointer) { // keep track of referenced objects if (m_SmartPointer.GetPointer() && --m_ReferenceCount[m_SmartPointer.GetPointer()] == 0) // if there is no reference left, delete entry { m_ReferenceCount.erase(m_SmartPointer.GetPointer()); m_ReferencesUID.erase(m_SmartPointer.GetPointer()); } if (pointer && ++m_ReferenceCount[pointer] == 1) // first reference --> generate UID { m_ReferencesUID[pointer] = m_UIDGenerator.GetUID(); } // change pointer m_SmartPointer = pointer; Modified(); } } void mitk::SmartPointerProperty::SetValue(const ValueType &value) { this->SetSmartPointer(value.GetPointer()); } std::string mitk::SmartPointerProperty::GetValueAsString() const { if (m_SmartPointer.IsNotNull()) return m_ReferencesUID[m_SmartPointer.GetPointer()]; else return std::string("nullptr"); } +bool mitk::SmartPointerProperty::ToJSON(nlohmann::json&) const +{ + return false; +} + +bool mitk::SmartPointerProperty::FromJSON(const nlohmann::json&) +{ + return false; +} + itk::LightObject::Pointer mitk::SmartPointerProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkStringProperty.cpp b/Modules/Core/src/DataManagement/mitkStringProperty.cpp index 767d6101c1..cc1ecf9e69 100644 --- a/Modules/Core/src/DataManagement/mitkStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkStringProperty.cpp @@ -1,51 +1,64 @@ /*============================================================================ 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 "mitkStringProperty.h" +#include const char *mitk::StringProperty::PATH = "path"; mitk::StringProperty::StringProperty(const char *string) : m_Value() { if (string) m_Value = string; } mitk::StringProperty::StringProperty(const std::string &s) : m_Value(s) { } mitk::StringProperty::StringProperty(const StringProperty &other) : BaseProperty(other), m_Value(other.m_Value) { } bool mitk::StringProperty::IsEqual(const BaseProperty &property) const { return this->m_Value == static_cast(property).m_Value; } bool mitk::StringProperty::Assign(const BaseProperty &property) { this->m_Value = static_cast(property).m_Value; return true; } std::string mitk::StringProperty::GetValueAsString() const { return m_Value; } +bool mitk::StringProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetValueAsString(); + return true; +} + +bool mitk::StringProperty::FromJSON(const nlohmann::json& j) +{ + this->SetValue(j.get()); + return true; +} + itk::LightObject::Pointer mitk::StringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index a3e0b1f549..4764497413 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -1,493 +1,426 @@ /*============================================================================ 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; +using CondensedTimeKeyType = std::pair; +using CondensedTimePointsType = std::map; + +using CondensedSliceKeyType = std::pair; +using CondensedSlicesType = std::map; + +namespace +{ + /** 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; + } +} 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" instead of "t":2 -template -std::basic_string CreateJSONEscapes(const std::basic_string &s) +bool mitk::TemporoSpatialStringProperty::ToJSON(nlohmann::json& j) const { - 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 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" 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 inconvenience and subtle changes in behavior. - CondensedSlicesType uncondensedSlices = CondenseTimePointValuesOfProperty(tsProp); - - //now condense the slices + // 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" at a higher rate. + // We didn't want to change the internal structure of the property as it would + // introduce API inconvenience and subtle changes in behavior. + auto uncondensedSlices = CondenseTimePointValuesOfProperty(this); CondensedSlicesType condensedSlices; + if(!uncondensedSlices.empty()) { - CondensedTimePointsType& masterSlice = uncondensedSlices.begin()->second; - CondensedSliceKeyType masterSliceKey = uncondensedSlices.begin()->first; + auto& masterSlice = uncondensedSlices.begin()->second; + auto 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; } + auto values = nlohmann::json::array(); - 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 << ", "; + auto value = nlohmann::json::object(); + value["z"] = minSliceID; + if (minSliceID != maxSliceID) - { - stream << "\"zmax\":" << maxSliceID << ", "; - } - stream << "\"t\":" << minTimePointID << ", "; + value["zmax"] = maxSliceID; + + value["t"] = minTimePointID; + if (minTimePointID != maxTimePointID) - { - stream << "\"tmax\":" << maxTimePointID << ", "; - } + value["tmax"] = maxTimePointID; + + value["value"] = t.second; - const auto& value = t.second; - stream << "\"value\":\"" << CreateJSONEscapes(value) << "\"}"; + values.push_back(value); } } - stream << "]}"; + j = nlohmann::json{{"values", values}}; - return stream.str(); + return true; } -mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty( - const std::string &value) +bool mitk::TemporoSpatialStringProperty::FromJSON(const nlohmann::json& j) { - if (value.empty()) - return nullptr; - - mitk::TemporoSpatialStringProperty::Pointer prop = mitk::TemporoSpatialStringProperty::New(); - - auto root = json::parse(value); - - for (const auto& element : root["values"]) + for (const auto& element : j["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); + this->SetValue(currentT, currentZ, value); } } } + return true; +} + +std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop) +{ + const auto *tsProp = dynamic_cast(prop); + + if (!tsProp) + mitkThrow() << "Cannot serialize properties of types other than TemporoSpatialStringProperty."; + + nlohmann::json j; + tsProp->ToJSON(j); + + return j.dump(); +} + +mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(const std::string &value) +{ + if (value.empty()) + return nullptr; + + auto prop = mitk::TemporoSpatialStringProperty::New(); + + auto root = nlohmann::json::parse(value); + prop->FromJSON(root); + return prop.GetPointer(); } diff --git a/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp b/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp index 3e5bce2ca6..20a59af2a7 100644 --- a/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp @@ -1,53 +1,125 @@ /*============================================================================ 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 "mitkTransferFunctionProperty.h" +#include namespace mitk { bool TransferFunctionProperty::IsEqual(const BaseProperty &property) const { return *(this->m_Value) == *(static_cast(property).m_Value); } bool TransferFunctionProperty::Assign(const BaseProperty &property) { this->m_Value = static_cast(property).m_Value; return true; } std::string TransferFunctionProperty::GetValueAsString() const { std::stringstream myStr; myStr << GetValue(); return myStr.str(); } TransferFunctionProperty::TransferFunctionProperty() : BaseProperty(), m_Value(mitk::TransferFunction::New()) {} TransferFunctionProperty::TransferFunctionProperty(const TransferFunctionProperty &other) : BaseProperty(other), m_Value(other.m_Value->Clone()) { } TransferFunctionProperty::TransferFunctionProperty(mitk::TransferFunction::Pointer value) : BaseProperty(), m_Value(value) { } + bool TransferFunctionProperty::ToJSON(nlohmann::json& j) const + { + auto tf = this->GetValue(); + + auto scalarOpacity = nlohmann::json::array(); + + for (const auto& point : tf->GetScalarOpacityPoints()) + scalarOpacity.push_back(point); + + auto gradientOpacity = nlohmann::json::array(); + + for (const auto& point : tf->GetGradientOpacityPoints()) + gradientOpacity.push_back(point); + + auto* ctf = tf->GetColorTransferFunction(); + auto size = ctf->GetSize(); + + std::array value; + auto color = nlohmann::json::array(); + + for (int i = 0; i < size; ++i) + { + ctf->GetNodeValue(i, value.data()); + color.push_back(value); + } + + j = nlohmann::json{ + {"ScalarOpacity", scalarOpacity}, + {"GradientOpacity", gradientOpacity}, + {"Color", color}}; + + return true; + } + + bool TransferFunctionProperty::FromJSON(const nlohmann::json& j) + { + auto tf = TransferFunction::New(); + TransferFunction::ControlPoints::value_type point; + + tf->ClearScalarOpacityPoints(); + + for (const auto& opacity : j["ScalarOpacity"]) + { + opacity.get_to(point); + tf->AddScalarOpacityPoint(point.first, point.second); + } + + tf->ClearGradientOpacityPoints(); + + for (const auto& opacity : j["GradientOpacity"]) + { + opacity.get_to(point); + tf->AddGradientOpacityPoint(point.first, point.second); + } + + auto* ctf = tf->GetColorTransferFunction(); + ctf->RemoveAllPoints(); + + std::array value; + + for (const auto& color : j["Color"]) + { + color.get_to(value); + ctf->AddRGBPoint(value[0], value[1], value[2], value[3], value[4], value[5]); + } + + this->SetValue(tf); + + return true; + } + itk::LightObject::Pointer TransferFunctionProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } } // namespace mitk diff --git a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp index d2fa075f52..f6e986629f 100644 --- a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp @@ -1,90 +1,105 @@ /*============================================================================ 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 "mitkVectorProperty.h" +#include namespace mitk { template bool VectorProperty::IsEqual(const BaseProperty &property) const { return this->m_PropertyContent == static_cast(property).m_PropertyContent; } template bool VectorProperty::Assign(const BaseProperty &property) { this->m_PropertyContent = static_cast(property).m_PropertyContent; return true; } template itk::LightObject::Pointer VectorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); return result; } template std::string VectorProperty::GetValueAsString() const { const size_t displayBlockLength = 3; size_t beginningElementsCount = displayBlockLength; size_t endElementsCount = displayBlockLength; if (m_PropertyContent.size() <= 2 * displayBlockLength) { beginningElementsCount = m_PropertyContent.size(); endElementsCount = 0; } // return either a block of all items // if the total number of maximum 2*displayBlockLength // // or return the first and last "displayBlockLength" // number of items separated by "[...]"; std::stringstream string_collector; for (size_t i = 0; i < beginningElementsCount; i++) string_collector << m_PropertyContent[i] << "\n"; if (endElementsCount) string_collector << "[... " << m_PropertyContent.size() - 2 * displayBlockLength << " more]\n"; for (size_t i = m_PropertyContent.size() - endElementsCount; i < m_PropertyContent.size(); ++i) string_collector << m_PropertyContent[i] << "\n"; std::string return_value = string_collector.str(); // remove last '\n' if (!return_value.empty()) return_value.erase(return_value.size() - 1); return return_value; } template void VectorProperty::SetValue(const VectorType &newValue) { m_PropertyContent = newValue; } template const typename VectorProperty::VectorType &VectorProperty::GetValue() const { return m_PropertyContent; } + template + bool VectorProperty::ToJSON(nlohmann::json& j) const + { + j = this->GetValue(); + return true; + } + + template + bool VectorProperty::FromJSON(const nlohmann::json& j) + { + this->SetValue(j.get()); + return true; + } + // Explicit instantiation for defined types. MITK_DEFINE_VECTOR_PROPERTY(double) MITK_DEFINE_VECTOR_PROPERTY(int) MITK_DEFINE_VECTOR_PROPERTY(unsigned int) MITK_DEFINE_VECTOR_PROPERTY(std::string) } // namespace mitk diff --git a/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp b/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp index 33249b73cf..e3b8f46fde 100644 --- a/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp @@ -1,75 +1,85 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkWeakPointerProperty.h" bool mitk::WeakPointerProperty::IsEqual(const BaseProperty &property) const { return this->m_WeakPointer == static_cast(property).m_WeakPointer; } bool mitk::WeakPointerProperty::Assign(const BaseProperty &property) { this->m_WeakPointer = static_cast(property).m_WeakPointer; return true; } mitk::WeakPointerProperty::WeakPointerProperty(itk::Object *pointer) : m_WeakPointer(pointer) { } mitk::WeakPointerProperty::WeakPointerProperty(const WeakPointerProperty &other) : mitk::BaseProperty(other), m_WeakPointer(other.m_WeakPointer) { } mitk::WeakPointerProperty::~WeakPointerProperty() { } std::string mitk::WeakPointerProperty::GetValueAsString() const { std::stringstream ss; ss << m_WeakPointer.GetPointer(); return ss.str(); } mitk::WeakPointerProperty::ValueType mitk::WeakPointerProperty::GetWeakPointer() const { return m_WeakPointer.GetPointer(); } mitk::WeakPointerProperty::ValueType mitk::WeakPointerProperty::GetValue() const { return GetWeakPointer(); } void mitk::WeakPointerProperty::SetWeakPointer(itk::Object *pointer) { if (m_WeakPointer.GetPointer() != pointer) { m_WeakPointer = pointer; Modified(); } } void mitk::WeakPointerProperty::SetValue(const ValueType &value) { SetWeakPointer(value.GetPointer()); } +bool mitk::WeakPointerProperty::ToJSON(nlohmann::json&) const +{ + return false; +} + +bool mitk::WeakPointerProperty::FromJSON(const nlohmann::json&) +{ + return false; +} + itk::LightObject::Pointer mitk::WeakPointerProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/IO/mitkImageGenerator.cpp b/Modules/Core/src/IO/mitkImageGenerator.cpp deleted file mode 100644 index f92933ddeb..0000000000 --- a/Modules/Core/src/IO/mitkImageGenerator.cpp +++ /dev/null @@ -1,11 +0,0 @@ -/*============================================================================ - -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. - -============================================================================*/ diff --git a/Modules/Core/src/Rendering/mitkAnnotation.cpp b/Modules/Core/src/Rendering/mitkAnnotation.cpp index c9ac002908..9568fc0541 100644 --- a/Modules/Core/src/Rendering/mitkAnnotation.cpp +++ b/Modules/Core/src/Rendering/mitkAnnotation.cpp @@ -1,348 +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. ============================================================================*/ -#include "mitkAnnotation.h" -#include "usGetModuleContext.h" +#include +#include +#include const std::string mitk::Annotation::US_INTERFACE_NAME = "org.mitk.services.Annotation"; const std::string mitk::Annotation::US_PROPKEY_AnnotationNAME = US_INTERFACE_NAME + ".name"; const std::string mitk::Annotation::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::Annotation::US_PROPKEY_MODIFIED = US_INTERFACE_NAME + ".modified"; const std::string mitk::Annotation::US_PROPKEY_RENDERER_ID = US_INTERFACE_NAME + ".rendererId"; const std::string mitk::Annotation::US_PROPKEY_AR_ID = US_INTERFACE_NAME + ".arId"; mitk::Annotation::Annotation() : m_PropertyListModifiedObserverTag(0) { m_PropertyList = mitk::PropertyList::New(); itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::Annotation::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); this->SetName(this->GetNameOfClass()); this->SetVisibility(true); this->SetOpacity(1.0); } void mitk::Annotation::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { AnnotationModified(); } mitk::Annotation::~Annotation() { this->UnRegisterMicroservice(); } void mitk::Annotation::SetUSProperty(const std::string &propertyKey, us::Any value) { if (this->m_ServiceRegistration) { us::ServiceProperties props; std::vector propertyKeys; m_ServiceRegistration.GetReference().GetPropertyKeys(propertyKeys); for (const std::string &key : propertyKeys) { props[key] = m_ServiceRegistration.GetReference().GetProperty(key); } props[propertyKey] = value; m_ServiceRegistration.SetProperties(props); } } void mitk::Annotation::SetProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::Annotation::ReplaceProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->ReplaceProperty(propertyKey, propertyValue); } void mitk::Annotation::AddProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey) == nullptr)) { SetProperty(propertyKey, propertyValue); } } void mitk::Annotation::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::Annotation::GetProperty(const std::string &propertyKey) const { mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty(propertyKey); if (property.IsNotNull()) return property; // only to satisfy compiler! return nullptr; } bool mitk::Annotation::GetBoolProperty(const std::string &propertyKey, bool &boolValue) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::Annotation::GetIntProperty(const std::string &propertyKey, int &intValue) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::Annotation::GetFloatProperty(const std::string &propertyKey, float &floatValue) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::Annotation::GetStringProperty(const std::string &propertyKey, std::string &string) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } void mitk::Annotation::SetIntProperty(const std::string &propertyKey, int intValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); Modified(); } void mitk::Annotation::SetBoolProperty(const std::string &propertyKey, bool boolValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); Modified(); } void mitk::Annotation::SetFloatProperty(const std::string &propertyKey, float floatValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); Modified(); } void mitk::Annotation::SetDoubleProperty(const std::string &propertyKey, double doubleValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); Modified(); } void mitk::Annotation::SetStringProperty(const std::string &propertyKey, const std::string &stringValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); Modified(); } std::string mitk::Annotation::GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } void mitk::Annotation::SetName(const std::string &name) { this->SetStringProperty("name", name); } bool mitk::Annotation::GetName(std::string &nodeName, const std::string &propertyKey) const { return GetStringProperty(propertyKey, nodeName); } void mitk::Annotation::SetText(std::string text) { SetStringProperty("Text", text.c_str()); } std::string mitk::Annotation::GetText() const { std::string text; GetStringProperty("Text", text); return text; } void mitk::Annotation::SetFontSize(int fontSize) { SetIntProperty("FontSize", fontSize); } int mitk::Annotation::GetFontSize() const { int fontSize = 1; GetIntProperty("FontSize", fontSize); return fontSize; } bool mitk::Annotation::GetVisibility(bool &visible, const std::string &propertyKey) const { return GetBoolProperty(propertyKey, visible); } bool mitk::Annotation::IsVisible(const std::string &propertyKey, bool defaultIsOn) const { return IsOn(propertyKey, defaultIsOn); } bool mitk::Annotation::GetColor(float rgb[], const std::string &propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } void mitk::Annotation::SetColor(const mitk::Color &color, const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetColor(float red, float green, float blue, const std::string &propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, propertyKey); } void mitk::Annotation::SetColor(const float rgb[], const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); this->m_PropertyList->SetProperty(propertyKey, prop); } bool mitk::Annotation::GetOpacity(float &opacity, const std::string &propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } void mitk::Annotation::SetOpacity(float opacity, const std::string &propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetVisibility(bool visible, const std::string &propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); this->m_PropertyList->SetProperty(propertyKey, prop); Modified(); } bool mitk::Annotation::BaseLocalStorage::IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Annotation *Annotation) { if (m_LastGenerateDataTime < Annotation->GetMTime()) return true; if (m_LastGenerateDataTime < Annotation->GetPropertyList()->GetMTime()) return true; if (renderer && m_LastGenerateDataTime < renderer->GetTimeStepUpdateTime()) return true; return false; } mitk::Annotation::Bounds mitk::Annotation::GetBoundsOnDisplay(mitk::BaseRenderer *) const { mitk::Annotation::Bounds bounds; bounds.Position[0] = bounds.Position[1] = bounds.Size[0] = bounds.Size[1] = 0; return bounds; } void mitk::Annotation::SetBoundsOnDisplay(mitk::BaseRenderer *, const mitk::Annotation::Bounds &) { } void mitk::Annotation::SetForceInForeground(bool forceForeground) { m_ForceInForeground = forceForeground; } bool mitk::Annotation::IsForceInForeground() const { return m_ForceInForeground; } mitk::PropertyList *mitk::Annotation::GetPropertyList() const { return m_PropertyList; } std::string mitk::Annotation::GetMicroserviceID() { return this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID).ToString(); } void mitk::Annotation::RegisterAsMicroservice(us::ServiceProperties props) { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); us::ModuleContext *context = us::GetModuleContext(); // Define ServiceProps mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.Annotation.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); m_ServiceRegistration = context->RegisterService(this, props); } void mitk::Annotation::UnRegisterMicroservice() { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } void mitk::Annotation::AnnotationModified() { Modified(); this->SetUSProperty(US_PROPKEY_MODIFIED, this->GetMTime()); } diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 594cdf5490..a5c00479ad 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -1,368 +1,437 @@ /*============================================================================ 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 "mitkCoreActivator.h" #include #include // File IO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkLegacyFileWriterService.h" #include #include #include // PropertyRelationRules #include // Micro Services #include #include #include #include #include #include #include #include #include #include +// Properties +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). // To ensure that the code is called *before* the CppMicroServices // static initialization code (which triggers the Activator::Start // method), we include the ITK header here. #include -void HandleMicroServicesMessages(us::MsgType type, const char *msg) +namespace { - switch (type) + void HandleMicroServicesMessages(us::MsgType type, const char* msg) { + switch (type) + { case us::DebugMsg: MITK_DEBUG << msg; break; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; case us::ErrorMsg: MITK_ERROR << msg; break; + } } -} -void AddMitkAutoLoadPaths(const std::string &programPath) -{ - us::ModuleSettings::AddAutoLoadPath(programPath); -#ifdef __APPLE__ - // Walk up three directories since that is where the .dylib files are located - // for build trees. - std::string additionalPath = programPath; - bool addPath = true; - for (int i = 0; i < 3; ++i) + void AddMitkAutoLoadPaths(const std::string& programPath) { - std::size_t index = additionalPath.find_last_of('/'); - if (index != std::string::npos) + us::ModuleSettings::AddAutoLoadPath(programPath); +#ifdef __APPLE__ + // Walk up three directories since that is where the .dylib files are located + // for build trees. + std::string additionalPath = programPath; + bool addPath = true; + for (int i = 0; i < 3; ++i) { - additionalPath = additionalPath.substr(0, index); + std::size_t index = additionalPath.find_last_of('/'); + if (index != std::string::npos) + { + additionalPath = additionalPath.substr(0, index); + } + else + { + addPath = false; + break; + } } - else + if (addPath) { - addPath = false; - break; + us::ModuleSettings::AddAutoLoadPath(additionalPath); } +#endif } - if (addPath) + + void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) { - us::ModuleSettings::AddAutoLoadPath(additionalPath); - } -#endif -} + mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); -void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) -{ - mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); + auto info = mitk::PropertyPersistenceInfo::New(); + if (propPath.IsExplicit()) + { + std::string name = mitk::PropertyKeyPathToPropertyName(propPath); + std::string key = name; + std::replace(key.begin(), key.end(), '.', '_'); + info->SetNameAndKey(name, key); + } + else + { + std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); + std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); + std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); + std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); + info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); + } - auto info = mitk::PropertyPersistenceInfo::New(); - if (propPath.IsExplicit()) - { - std::string name = mitk::PropertyKeyPathToPropertyName(propPath); - std::string key = name; - std::replace(key.begin(), key.end(), '.', '_'); - info->SetNameAndKey(name, key); + persistenceService->AddInfo(info); } - else + + void RegisterProperties() { - std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); - std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); - std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); - std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); - info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); + mitk::CoreServicePointer service(mitk::CoreServices::GetPropertyDeserialization()); + + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); } - - persistenceService->AddInfo(info); } class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) bool SupportsDimension(unsigned long dim) override { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext *context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } // m_RenderingManager = mitk::RenderingManager::New(); // context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); + m_PropertyDeserialization.reset(new mitk::PropertyDeserialization); + context->RegisterService(m_PropertyDeserialization.get()); + m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService(m_PropertyFilters.get()); m_PropertyPersistence.reset(new mitk::PropertyPersistence); context->RegisterService(m_PropertyPersistence.get()); m_PropertyRelations.reset(new mitk::PropertyRelations); context->RegisterService(m_PropertyRelations.get()); m_PreferencesService.reset(new mitk::PreferencesService); context->RegisterService(m_PreferencesService.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); m_FileReaders.push_back(new mitk::GeometryDataReaderService()); m_FileWriters.push_back(new mitk::GeometryDataWriterService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); //add properties that should be persistent (if possible/supported by the writer) AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_VERSION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_OPTIONS_ANY()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("","").AddAnyElement()); + RegisterProperties(); + /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); } void MitkCoreActivator::Unload(us::ModuleContext *) { for (auto &elem : m_FileReaders) { delete elem; } for (auto &elem : m_FileWriters) { delete elem; } for (auto &elem : m_FileIOs) { delete elem; } for (auto &elem : m_LegacyWriters) { delete elem; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); for (std::vector::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { delete *mimeTypeIter; } } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types std::vector mimeTypes = mitk::IOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for (auto &allobject : allobjects) { auto *io = dynamic_cast(allobject.GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast(io)) continue; // Use a custom mime-type for GDCMImageIO below if (dynamic_cast(allobject.GetPointer())) { // MITK provides its own DICOM reader (which internally uses GDCMImageIO). continue; } if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << (allobject)->GetNameOfClass(); } } FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); mitk::ItkImageIO *niftiIO = new mitk::ItkImageIO(mitk::IOMimeTypes::NIFTI_MIMETYPE(), itkNiftiIO.GetPointer(), 0); m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast(i->GetPointer()); if (io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Writer"; mitk::IFileWriter *writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). US_INITIALIZE_MODULE diff --git a/Modules/Core/src/mitkCoreActivator.h b/Modules/Core/src/mitkCoreActivator.h index 19c513bba7..d28f97a70a 100644 --- a/Modules/Core/src/mitkCoreActivator.h +++ b/Modules/Core/src/mitkCoreActivator.h @@ -1,82 +1,84 @@ /*============================================================================ 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 mitkCoreActivator_h #define mitkCoreActivator_h // File IO #include #include #include #include #include #include #include +#include #include #include #include #include #include // Micro Services #include #include #include #include #include /* * This is the module activator for the "Mitk" module. It registers core services * like ... */ class MitkCoreActivator : public us::ModuleActivator { public: void Load(us::ModuleContext *context) override; void Unload(us::ModuleContext *) override; private: void HandleModuleEvent(const us::ModuleEvent moduleEvent); void RegisterDefaultMimeTypes(); void RegisterItkReaderWriter(); void RegisterVtkReaderWriter(); void RegisterLegacyWriter(); // mitk::RenderingManager::Pointer m_RenderingManager; std::unique_ptr m_PlanePositionManager; std::unique_ptr m_PropertyAliases; std::unique_ptr m_PropertyDescriptions; + std::unique_ptr m_PropertyDeserialization; std::unique_ptr m_PropertyExtensions; std::unique_ptr m_PropertyFilters; std::unique_ptr m_PropertyPersistence; std::unique_ptr m_PropertyRelations; std::unique_ptr m_MimeTypeProvider; std::unique_ptr m_PreferencesService; // File IO std::vector m_FileReaders; std::vector m_FileWriters; std::vector m_FileIOs; std::vector m_LegacyWriters; std::vector m_DefaultMimeTypes; us::ServiceRegistration m_MimeTypeProviderReg; us::ModuleContext *m_Context; }; #endif diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp index 720730347f..96dce2bcde 100644 --- a/Modules/Core/src/mitkCoreServices.cpp +++ b/Modules/Core/src/mitkCoreServices.cpp @@ -1,133 +1,139 @@ /*============================================================================ 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 "mitkCoreServices.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include namespace mitk { std::mutex &s_ContextToServicesMapMutex() { static std::mutex mutex; return mutex; } std::map> &s_ContextToServicesMap() { static std::map> serviceMap; return serviceMap; } template static S *GetCoreService(us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); S *coreService = nullptr; us::ServiceReference serviceRef = context->GetServiceReference(); if (serviceRef) { coreService = context->GetService(serviceRef); } assert(coreService && "Asserting non-nullptr MITK core service"); { std::lock_guard l(s_ContextToServicesMapMutex()); s_ContextToServicesMap()[context].insert(std::make_pair(coreService, serviceRef)); } return coreService; } IPropertyAliases *CoreServices::GetPropertyAliases(us::ModuleContext *context) { return GetCoreService(context); } IPropertyDescriptions *CoreServices::GetPropertyDescriptions(us::ModuleContext *context) { return GetCoreService(context); } + IPropertyDeserialization* CoreServices::GetPropertyDeserialization(us::ModuleContext* context) + { + return GetCoreService(context); + } + IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyFilters *CoreServices::GetPropertyFilters(us::ModuleContext *context) { return GetCoreService(context); } IPropertyPersistence *CoreServices::GetPropertyPersistence(us::ModuleContext *context) { return GetCoreService(context); } IPropertyRelations *CoreServices::GetPropertyRelations(us::ModuleContext *context) { return GetCoreService(context); } IMimeTypeProvider *CoreServices::GetMimeTypeProvider(us::ModuleContext *context) { return GetCoreService(context); } IPreferencesService *CoreServices::GetPreferencesService(us::ModuleContext *context) { return GetCoreService(context); } bool CoreServices::Unget(us::ModuleContext *context, const std::string & /*interfaceId*/, void *service) { bool success = false; std::lock_guard l(s_ContextToServicesMapMutex()); auto iter = s_ContextToServicesMap().find(context); if (iter != s_ContextToServicesMap().end()) { auto iter2 = iter->second.find(service); if (iter2 != iter->second.end()) { us::ServiceReferenceU serviceRef = iter2->second; if (serviceRef) { success = context->UngetService(serviceRef); if (success) { iter->second.erase(iter2); } } } } return success; } } diff --git a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp index ae79c6260b..48db4613df 100644 --- a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp +++ b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp @@ -1,242 +1,245 @@ /*============================================================================ 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 "mitkTemporoSpatialStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkTemporoSpatialStringPropertyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkTemporoSpatialStringPropertyTestSuite); MITK_TEST(GetValue); MITK_TEST(HasValue); MITK_TEST(SetValue); MITK_TEST(IsUniform); MITK_TEST(serializeTemporoSpatialStringPropertyToJSON); MITK_TEST(deserializeJSONToTemporoSpatialStringProperty); CPPUNIT_TEST_SUITE_END(); private: std::string refJSON; std::string refJSON_legacy; std::string refPartlyCondensibleJSON; std::string refCondensibleJSON; mitk::TemporoSpatialStringProperty::Pointer refProp; mitk::TemporoSpatialStringProperty::Pointer refPartlyCondensibleProp; mitk::TemporoSpatialStringProperty::Pointer refCondensibleProp; public: void setUp() override { refJSON_legacy = "{\"values\":[{\"t\":0, \"z\":0, \"value\":\"v_0_0\"}, {\"t\":3, \"z\":0, \"value\":\"v_3_0\"}, " "{\"t\":3, \"z\":2, \"value\":\"v_3_2\"}, {\"t\":6, \"z\":1, \"value\":\"v_6_1\"}]}"; refJSON = "{\"values\":[{\"z\":0, \"t\":0, \"value\":\"v_0_0\"}, {\"z\":0, \"t\":3, \"value\":\"v_3_0\"}, {\"z\":1, \"t\":6, \"value\":\"v_6_1\"}, {\"z\":2, \"t\":3, \"value\":\"v_3_2\"}]}"; refPartlyCondensibleJSON = "{\"values\":[{\"z\":0, \"t\":0, \"value\":\"0\"}, {\"z\":1, \"t\":0, \"tmax\":1, \"value\":\"0\"}, {\"z\":1, \"t\":3, \"tmax\":5, \"value\":\"0\"}, {\"z\":1, \"t\":6, \"value\":\"otherValue\"}, {\"z\":2, \"t\":6, \"value\":\"0\"}]}"; refCondensibleJSON = "{\"values\":[{\"z\":0, \"zmax\":2, \"t\":0, \"tmax\":1, \"value\":\"1\"}]}"; refProp = mitk::TemporoSpatialStringProperty::New(); refProp->SetValue(0, 0, "v_0_0"); refProp->SetValue(3, 0, "v_3_0"); refProp->SetValue(3, 2, "v_3_2"); refProp->SetValue(6, 1, "v_6_1"); refPartlyCondensibleProp = mitk::TemporoSpatialStringProperty::New(); refPartlyCondensibleProp->SetValue(0, 0, "0"); refPartlyCondensibleProp->SetValue(0, 1, "0"); refPartlyCondensibleProp->SetValue(1, 1, "0"); refPartlyCondensibleProp->SetValue(3, 1, "0"); refPartlyCondensibleProp->SetValue(4, 1, "0"); refPartlyCondensibleProp->SetValue(5, 1, "0"); refPartlyCondensibleProp->SetValue(6, 1, "otherValue"); refPartlyCondensibleProp->SetValue(6, 2, "0"); refCondensibleProp = mitk::TemporoSpatialStringProperty::New(); refCondensibleProp->SetValue(0, 0, "1"); refCondensibleProp->SetValue(1, 0, "1"); refCondensibleProp->SetValue(0, 1, "1"); refCondensibleProp->SetValue(1, 1, "1"); refCondensibleProp->SetValue(0, 2, "1"); refCondensibleProp->SetValue(1, 2, "1"); } void tearDown() override {} void GetValue() { CPPUNIT_ASSERT(refProp->GetValue() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 0) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 2) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValue(3, 1, false, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 5, false, true) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValueBySlice(0) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueBySlice(4, true) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(3) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(6) == "v_6_1"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(5, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueAsString() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[1] == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[2] == 6); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0).size() == 2); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0)[1] == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(1).size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(1)[0] == 6); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(5).size() == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices().size() == 3); CPPUNIT_ASSERT(refProp->GetAvailableSlices()[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices()[1] == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices()[2] == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3).size() == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[1] == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(2).size() == 0); } void HasValue() { CPPUNIT_ASSERT(refProp->HasValue()); CPPUNIT_ASSERT(refProp->HasValue(3, 0)); CPPUNIT_ASSERT(refProp->HasValue(3, 2)); CPPUNIT_ASSERT(refProp->HasValue(3, 1, false, true)); CPPUNIT_ASSERT(refProp->HasValue(3, 5, false, true)); CPPUNIT_ASSERT(!refProp->HasValue(3, 1)); CPPUNIT_ASSERT(!refProp->HasValue(3, 5)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, true)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, false)); CPPUNIT_ASSERT(!refProp->HasValue(4, 2, false, true)); CPPUNIT_ASSERT(refProp->HasValueBySlice(0)); CPPUNIT_ASSERT(refProp->HasValueBySlice(4, true)); CPPUNIT_ASSERT(!refProp->HasValueBySlice(4)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(3)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(6)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(5, true)); CPPUNIT_ASSERT(!refProp->HasValueByTimeStep(5)); } void SetValue() { CPPUNIT_ASSERT_NO_THROW(refProp->SetValue(8, 9, "v_8_9")); CPPUNIT_ASSERT(refProp->GetValue(8, 9) == "v_8_9"); CPPUNIT_ASSERT_NO_THROW(refProp->SetValue("newValue")); CPPUNIT_ASSERT(refProp->GetValue(0, 0) == "newValue"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); } void IsUniform() { CPPUNIT_ASSERT(!refProp->IsUniform()); CPPUNIT_ASSERT(!refPartlyCondensibleProp->IsUniform()); CPPUNIT_ASSERT(refCondensibleProp->IsUniform()); } void serializeTemporoSpatialStringPropertyToJSON() { - std::string data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refProp); - CPPUNIT_ASSERT(refJSON == - data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); + auto data = nlohmann::json::parse(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refProp)); + auto ref = nlohmann::json::parse(refJSON); - data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refPartlyCondensibleProp); - CPPUNIT_ASSERT(refPartlyCondensibleJSON == - data); + CPPUNIT_ASSERT(ref == data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); - data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refCondensibleProp); - CPPUNIT_ASSERT(refCondensibleJSON == - data); + data = nlohmann::json::parse(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refPartlyCondensibleProp)); + ref = nlohmann::json::parse(refPartlyCondensibleJSON); + + CPPUNIT_ASSERT(ref == data); + + data = nlohmann::json::parse(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refCondensibleProp)); + ref = nlohmann::json::parse(refCondensibleJSON); + + CPPUNIT_ASSERT(ref == data); } void deserializeJSONToTemporoSpatialStringProperty() { mitk::BaseProperty::Pointer prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON); auto *tsProp = dynamic_cast(prop.GetPointer()); CPPUNIT_ASSERT( tsProp->GetValue(0, 0) == "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); CPPUNIT_ASSERT( tsProp->GetValue(3, 0) == "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); CPPUNIT_ASSERT( tsProp->GetValue(3, 2) == "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); CPPUNIT_ASSERT( tsProp->GetValue(6, 1) == "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON_legacy); tsProp = dynamic_cast(prop.GetPointer()); CPPUNIT_ASSERT( tsProp->GetValue(0, 0) == "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); CPPUNIT_ASSERT( tsProp->GetValue(3, 0) == "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); CPPUNIT_ASSERT( tsProp->GetValue(3, 2) == "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); CPPUNIT_ASSERT( tsProp->GetValue(6, 1) == "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refPartlyCondensibleJSON); tsProp = dynamic_cast(prop.GetPointer()); CPPUNIT_ASSERT(tsProp->GetValue(0, 0) =="0"); CPPUNIT_ASSERT(tsProp->GetValue(0, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(1, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(3, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(4, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(5, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(6, 1) == "otherValue"); CPPUNIT_ASSERT(tsProp->GetValue(6, 2) == "0"); CPPUNIT_ASSERT(*tsProp == *refPartlyCondensibleProp); prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refCondensibleJSON); tsProp = dynamic_cast(prop.GetPointer()); CPPUNIT_ASSERT(tsProp->GetValue(0, 0) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(1, 0) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(0, 1) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(1, 1) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(0, 2) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(1, 2) == "1"); CPPUNIT_ASSERT(*tsProp == *refCondensibleProp); } }; MITK_TEST_SUITE_REGISTRATION(mitkTemporoSpatialStringProperty) diff --git a/Modules/DataTypesExt/include/mitkBoundingObject.h b/Modules/DataTypesExt/include/mitkBoundingObject.h index 9a7c6128a7..9487df2037 100644 --- a/Modules/DataTypesExt/include/mitkBoundingObject.h +++ b/Modules/DataTypesExt/include/mitkBoundingObject.h @@ -1,64 +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 mitkBoundingObject_h #define mitkBoundingObject_h #include "MitkDataTypesExtExports.h" #include namespace mitk { //##Documentation //## @brief superclass of all bounding objects (cylinder, cuboid,...) //## //## Manages generic functions and provides an interface for IsInside() //## calculates a generic bounding box //## @ingroup Data class MITKDATATYPESEXT_EXPORT BoundingObject : public mitk::Surface // BaseData { public: mitkClassMacro(BoundingObject, mitk::Surface); virtual bool IsInside(const mitk::Point3D &p) const = 0; virtual mitk::ScalarType GetVolume(); itkGetMacro(Positive, bool); itkSetMacro(Positive, bool); itkBooleanMacro(Positive); //##Documentation //## @brief Sets the Geometry3D of the bounding object to fit the given //## geometry. //## //## The fit is done once, so if the given geometry changes it will //## \em not effect the bounding object. virtual void FitGeometry(BaseGeometry *aGeometry3D); protected: BoundingObject(); ~BoundingObject() override; - bool WriteXMLData(XMLWriter &xmlWriter); - //##Documentation //## \brief If \a true, the Boundingobject describes a positive volume, //## if \a false a negative volume. //## bool m_Positive; private: BoundingObject(const BoundingObject &); BoundingObject &operator=(const BoundingObject &); }; } #endif diff --git a/Modules/ModelFit/include/mitkScalarListLookupTable.h b/Modules/ModelFit/include/mitkScalarListLookupTable.h index 92c1541736..b7bed70910 100644 --- a/Modules/ModelFit/include/mitkScalarListLookupTable.h +++ b/Modules/ModelFit/include/mitkScalarListLookupTable.h @@ -1,91 +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 mitkScalarListLookupTable_h #define mitkScalarListLookupTable_h #include #include #include +#include #include "MitkModelFitExports.h" namespace mitk { /** * @brief Data class for modelfit properties that store a map of lists (e.g. static * parameters). */ class MITKMODELFIT_EXPORT ScalarListLookupTable { public: typedef std::string KeyType; typedef std::vector ValueType; typedef std::map LookupTableType; typedef std::pair EntryType; ScalarListLookupTable() {} virtual ~ScalarListLookupTable() {} virtual const char* GetNameOfClass() const; /** * @brief Sets the list at the given map key to the given value. * @param key The name of the list (i.e. map key). * @param value The list to be added (i.e. map value). */ void SetTableValue(const KeyType& key, const ValueType& value); /** * @brief Returns true if the list with the given name exists. * @param key The name of the desired list (i.e. map key) * @return true if the list exists or false otherwise. */ bool ValueExists(const KeyType& key) const; /** * @brief Returns the list with the given name. * @param key The name (i.e. map key) of the desired list. * @return The list with the given name. * @throw std::range_error If the list doesn't exist. */ const ValueType& GetTableValue(const KeyType& key) const; /** * @brief Returns the map of lists. * @return The map of lists. */ const LookupTableType& GetLookupTable() const; void SetLookupTable(const LookupTableType& table); bool operator==(const ScalarListLookupTable& lookupTable) const; bool operator!=(const ScalarListLookupTable& lookupTable) const; virtual ScalarListLookupTable& operator=(const ScalarListLookupTable& other); protected: LookupTableType m_LookupTable; }; /** * @brief Adds the string representation of the given ScalarListLookupTable to the * given stream. * @param stream The stream to which the map's string representation should be added. * @param l The map whose string representation should be added to the stream. * @return The given stream with the added string representation of the map. */ MITKMODELFIT_EXPORT std::ostream& operator<<(std::ostream& stream, const ScalarListLookupTable& l); + + MITKMODELFIT_EXPORT void to_json(nlohmann::json& j, const ScalarListLookupTable& lut); + MITKMODELFIT_EXPORT void from_json(const nlohmann::json& j, ScalarListLookupTable& lut); } #endif diff --git a/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp b/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp index 35d1c336ac..fa8c3b5a3b 100644 --- a/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp +++ b/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp @@ -1,116 +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. ============================================================================*/ #include "mitkScalarListLookupTable.h" #include #include +#include + +namespace mitk +{ + void to_json(nlohmann::json& j, const ScalarListLookupTable& lut) + { + j = lut.GetLookupTable(); + } + + void from_json(const nlohmann::json& j, ScalarListLookupTable& lut) + { + lut.SetLookupTable(j.get()); + } +} + const char* mitk::ScalarListLookupTable::GetNameOfClass() const { return "ScalarListLookupTable"; } void mitk::ScalarListLookupTable::SetTableValue(const KeyType& key, const ValueType& value) { m_LookupTable[key] = value; } bool mitk::ScalarListLookupTable::ValueExists(const KeyType& key) const { LookupTableType::const_iterator it = m_LookupTable.find(key); return (it != m_LookupTable.end()); } const mitk::ScalarListLookupTable::ValueType& mitk::ScalarListLookupTable::GetTableValue(const KeyType& key) const { LookupTableType::const_iterator it = m_LookupTable.find(key); if (it != m_LookupTable.end()) { return it->second; } else { throw std::range_error("id does not exist in the lookup table"); } } const mitk::ScalarListLookupTable::LookupTableType& mitk::ScalarListLookupTable::GetLookupTable() const { return m_LookupTable; } void mitk::ScalarListLookupTable::SetLookupTable(const LookupTableType& table) { m_LookupTable = table; }; bool mitk::ScalarListLookupTable::operator==(const mitk::ScalarListLookupTable& lookupTable) const { return (m_LookupTable == lookupTable.m_LookupTable); } bool mitk::ScalarListLookupTable::operator!=(const mitk::ScalarListLookupTable& lookupTable) const { return !(m_LookupTable == lookupTable.m_LookupTable); } mitk::ScalarListLookupTable& mitk::ScalarListLookupTable::operator=(const ScalarListLookupTable& other) { if (this == &other) { return *this; } else { m_LookupTable = other.m_LookupTable; return *this; } } std::ostream& mitk::operator<<(std::ostream& stream, const ScalarListLookupTable& l) { typedef ScalarListLookupTable::LookupTableType::const_iterator MapIterType; typedef ScalarListLookupTable::ValueType::const_iterator VectorIterType; MapIterType mapStart = l.GetLookupTable().begin(); MapIterType mapEnd = l.GetLookupTable().end(); stream << "["; for (MapIterType i = mapStart; i != mapEnd; ++i) { if (i != mapStart) { stream << ", "; } stream << i->first << " -> ["; VectorIterType vectorStart = i->second.begin(); VectorIterType vectorEnd = i->second.end(); for (VectorIterType j = vectorStart; j != vectorEnd; ++j) { if (j != vectorStart) { stream << ", "; } stream << *j; } stream << "]"; } return stream << "]"; }; diff --git a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp index 4793618497..692892d094 100644 --- a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp +++ b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp @@ -1,424 +1,425 @@ /*============================================================================ 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 "mitkProperties.h" #include "mitkStandaloneDataStorage.h" #include "mitkModelFitInfo.h" #include "mitkModelFitConstants.h" #include "mitkModelFitException.h" #include "mitkModelFitResultRelationRule.h" #include #include +#include mitk::modelFit::ModelFitInfo::UIDType ensureModelFitUID(mitk::BaseData * data) { mitk::BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str()); std::string propUID = ""; if (uidProp.IsNotNull()) { propUID = uidProp->GetValueAsString(); } else { mitk::UIDGenerator generator; propUID = generator.GetUID(); data->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(propUID)); } return propUID; }; mitk::DataNode::Pointer generateModelFitTestNode() { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetName("Param1"); auto testImage = mitk::Image::New(); node->SetData(testImage); testImage->SetProperty("modelfit.testEmpty", mitk::StringProperty::New("")); testImage->SetProperty("modelfit.testValid", mitk::StringProperty::New("test")); ensureModelFitUID(testImage); testImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); testImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName")); testImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); testImage->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("input UID")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); testImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); testImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); testImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); testImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); return node; } class mitkModelFitInfoTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkModelFitInfoTestSuite); MITK_TEST(CheckModelFitInfo); MITK_TEST(CheckGetMandatoryProperty); MITK_TEST(CheckCreateFitInfoFromNode_Legacy); MITK_TEST(CheckCreateFitInfoFromNode); MITK_TEST(CheckGetNodesOfFit); MITK_TEST(CheckGetFitUIDsOfNode); CPPUNIT_TEST_SUITE_END(); mitk::StandaloneDataStorage::Pointer m_Storage; mitk::Image::Pointer m_ParamImage; mitk::Image::Pointer m_ParamImage2; mitk::Image::Pointer m_ParamImage_legacy; mitk::Image::Pointer m_ParamImage2_legacy; mitk::DataNode::Pointer m_ParamImageNode; mitk::DataNode::Pointer m_ParamImageNode2; mitk::DataNode::Pointer m_ParamImageNode_legacy; mitk::DataNode::Pointer m_ParamImageNode2_legacy; public: void setUp() override { m_Storage = mitk::StandaloneDataStorage::New(); //create input node mitk::DataNode::Pointer inputNode = mitk::DataNode::New(); inputNode->SetName("Input"); mitk::Image::Pointer image = mitk::Image::New(); inputNode->SetData(image); mitk::modelFit::ModelFitInfo::UIDType inputUID = ensureModelFitUID(image); m_Storage->Add(inputNode); mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); parents->push_back(inputNode); ///////////////////////////////////////////////////// //Create nodes for a fit (new style using rules) ///////////////////////////////////////////////////// auto rule = mitk::ModelFitResultRelationRule::New(); //create first result for FitLegacy m_ParamImageNode = mitk::DataNode::New(); m_ParamImage = mitk::Image::New(); m_ParamImageNode->SetData(m_ParamImage); m_ParamImageNode->SetName("Param1"); m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit")); m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1")); m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); rule->Connect(m_ParamImage, image); m_Storage->Add(m_ParamImageNode, parents); //create second result for Fit m_ParamImageNode2 = mitk::DataNode::New(); m_ParamImageNode2->SetName("Param2"); m_ParamImage2 = mitk::Image::New(); m_ParamImageNode2->SetData(m_ParamImage2); m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); rule->Connect(m_ParamImage2, image); m_Storage->Add(m_ParamImageNode2, parents); ///////////////////////////////////////////////////// //Create nodes for a fit in legacy mode (old fit uid) ///////////////////////////////////////////////////// //create first result for FitLegacy m_ParamImageNode_legacy = mitk::DataNode::New(); m_ParamImage_legacy = mitk::Image::New(); m_ParamImageNode_legacy->SetData(m_ParamImage_legacy); m_ParamImageNode_legacy->SetName("Param1_legacy"); ensureModelFitUID(m_ParamImage_legacy); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1_legacy")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); m_Storage->Add(m_ParamImageNode_legacy, parents); //create second result for FitLegacy m_ParamImageNode2_legacy = mitk::DataNode::New(); m_ParamImageNode2_legacy->SetName("Param2_legacy"); m_ParamImage2_legacy = mitk::Image::New(); m_ParamImageNode2_legacy->SetData(m_ParamImage2_legacy); ensureModelFitUID(m_ParamImage2_legacy); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2_legacy")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); m_Storage->Add(m_ParamImageNode2_legacy, parents); ///////////////////////////////////////////////////// //Create nodes for a fit on other input auto anotherInputNode = mitk::DataNode::New(); anotherInputNode->SetName("AnotherInput"); auto anotherImage = mitk::Image::New(); anotherInputNode->SetData(anotherImage); m_Storage->Add(anotherInputNode); parents = mitk::DataStorage::SetOfObjects::New(); parents->push_back(anotherInputNode); mitk::DataNode::Pointer node3 = mitk::DataNode::New(); node3->SetName("Param_Other"); mitk::Image::Pointer paramImage3 = mitk::Image::New(); node3->SetData(paramImage3); paramImage3->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit2")); paramImage3->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName2")); paramImage3->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_2")); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass_B")); paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param_Other")); paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); rule->Connect(paramImage3, anotherImage); m_Storage->Add(node3, parents); } void tearDown() override { } void CheckModelFitInfo() { mitk::modelFit::Parameter::Pointer p = mitk::modelFit::Parameter::New(); p->name = "p"; mitk::modelFit::ModelFitInfo::Pointer fit = mitk::modelFit::ModelFitInfo::New(); fit->AddParameter(p); CPPUNIT_ASSERT_MESSAGE("AddParameter unsuccessfully adds a parameter.", fit->GetParameters().size() == 1); mitk::modelFit::Parameter::ConstPointer resultParam = fit->GetParameter("test", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns NULL for wrong parameter.", resultParam.IsNull()); resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter.", resultParam == p); p->type = mitk::modelFit::Parameter::CriterionType; resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::CriterionType); CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter with a non-default type.", resultParam == p); fit->DeleteParameter("test", mitk::modelFit::Parameter::CriterionType); CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter fails for wrong parameter.", fit->GetParameters().size() == 1); fit->DeleteParameter("p", mitk::modelFit::Parameter::CriterionType); CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter successfully removes a parameter.", fit->GetParameters().size() == 0); } void CheckGetMandatoryProperty() { mitk::DataNode::Pointer node = generateModelFitTestNode(); mitk::DataNode::Pointer invalideNode = mitk::DataNode::New(); CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testInvalid"), mitk::modelFit::ModelFitException); CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testEmpty"), mitk::modelFit::ModelFitException); CPPUNIT_ASSERT_MESSAGE("Testing if GetMandatoryProperty returns the correct value.", mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testValid") == "test"); } void CheckCreateFitInfoFromNode_Legacy() { CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("FitLegacy", nullptr).IsNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull()); mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("FitLegacy", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.", resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && resultFit->uid == "FitLegacy" && resultFit->fitName == "MyName1" && resultFit->modelType == "TestModels" && resultFit->modelName == "TestModel_1" && resultFit->function == "" && resultFit->functionClassID == "ModelClass" && resultFit->x == "myX" && resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && resultFit->xAxisUnit == "h" && resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && resultFit->yAxisUnit == "kg" && resultFit->GetParameters().size() == 2); mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1_legacy", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1_legacy" && param->unit == "b" && param->image == m_ParamImage_legacy); mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2_legacy", mitk::modelFit::Parameter::DerivedType); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2_legacy" && param2->unit == "a" && param2->image == m_ParamImage2_legacy); } void CheckCreateFitInfoFromNode() { CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("Fit", nullptr).IsNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull()); mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("Fit", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.", resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && resultFit->uid == "Fit" && resultFit->fitName == "MyName1" && resultFit->modelType == "TestModels" && resultFit->modelName == "TestModel_1" && resultFit->function == "" && resultFit->functionClassID == "ModelClass" && resultFit->x == "myX" && resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && resultFit->xAxisUnit == "h" && resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && resultFit->yAxisUnit == "kg" && resultFit->GetParameters().size() == 2); mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1" && param->unit == "b" && param->image == m_ParamImage); mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2", mitk::modelFit::Parameter::DerivedType); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2" && param2->unit == "a" && param2->image == m_ParamImage2); } void CheckGetNodesOfFit() { auto nodes = mitk::modelFit::GetNodesOfFit("Fit", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit", nodes->Size() == 2); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode.GetPointer()) != nodes->end()); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2.GetPointer()) != nodes->end()); nodes = mitk::modelFit::GetNodesOfFit("FitLegacy", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for FitLegacy", nodes->Size() == 2); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode_legacy.GetPointer()) != nodes->end()); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2_legacy.GetPointer()) != nodes->end()); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit2", mitk::modelFit::GetNodesOfFit("Fit2", m_Storage)->Size() == 1); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unkown fits.", mitk::modelFit::GetNodesOfFit("unkown_fit", m_Storage)->Size() == 0); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for illegal calls.", mitk::modelFit::GetNodesOfFit("unkown_fit", nullptr).IsNull()); } void CheckGetFitUIDsOfNode() { auto testNode = m_Storage->GetNamedNode("Input"); mitk::modelFit::NodeUIDSetType uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.", uidSet.size() == 2 && uidSet.find("Fit") != uidSet.end() && uidSet.find("FitLegacy") != uidSet.end()); testNode = m_Storage->GetNamedNode("AnotherInput"); uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.", uidSet.size() == 1 && uidSet.find("Fit2") != uidSet.end()); uidSet = mitk::modelFit::GetFitUIDsOfNode(nullptr, m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid node.", uidSet.size() == 0); uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, nullptr); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid storage.", uidSet.size() == 0); } }; MITK_TEST_SUITE_REGISTRATION(mitkModelFitInfo) \ No newline at end of file diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 89d0588465..b027952284 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,77 +1,78 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Log Core CommandLine CoreCmdApps AppUtil LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOM DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics ContourModel SurfaceInterpolation Segmentation QtWidgets QtWidgetsExt ImageStatisticsUI SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI US USUI DICOMUI Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI RT RTUI IOExt XNAT TubeGraph BoundingShape RenderWindowManagerUI SemanticRelations SemanticRelationsUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI DICOMPM REST RESTService DICOMweb + ROI ) diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h index 0d64a124df..968452be5d 100644 --- a/Modules/Multilabel/mitkLabel.h +++ b/Modules/Multilabel/mitkLabel.h @@ -1,106 +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 mitkLabel_h #define mitkLabel_h #include "MitkMultilabelExports.h" #include #include +#include #include namespace mitk { //## //##Documentation //## @brief A data structure describing a label. //## @ingroup Data //## class MITKMULTILABEL_EXPORT Label : public PropertyList { public: mitkClassMacro(Label, mitk::PropertyList); typedef unsigned short PixelType; itkNewMacro(Self); mitkNewMacro2Param(Self, PixelType, const std::string&); /// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535 static const PixelType MAX_LABEL_VALUE; void SetLocked(bool locked); bool GetLocked() const; void SetVisible(bool visible); bool GetVisible() const; void SetOpacity(float opacity); float GetOpacity() const; void SetName(const std::string &name); std::string GetName() const; void SetCenterOfMassIndex(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassIndex() const; void SetCenterOfMassCoordinates(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassCoordinates() const; void SetColor(const mitk::Color &); const mitk::Color &GetColor() const; void SetValue(PixelType pixelValue); PixelType GetValue() const; void SetLayer(unsigned int layer); unsigned int GetLayer() const; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; using itk::Object::Modified; void Modified() { Superclass::Modified(); } Label(); Label(PixelType value, const std::string& name); ~Label() override; protected: void PrintSelf(std::ostream &os, itk::Indent indent) const override; Label(const Label &other); private: itk::LightObject::Pointer InternalClone() const override; }; /** * @brief Equal A function comparing two labels for beeing equal in data * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - Lebel equality via Equal-PropetyList * * @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 */ MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide, const mitk::Label &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif diff --git a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp index f6e48f7dcc..40ad9545b1 100644 --- a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp @@ -1,308 +1,307 @@ /*============================================================================ 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 "mitkIGTLDeviceSource.h" #include "mitkIGTLDevice.h" #include "mitkIGTLMessage.h" -//#include "mitkIGTTimeStamp.h" -//#include "mitkIGTException.h" +#include //Microservices #include #include #include #include //itk #include const std::string mitk::IGTLDeviceSource::US_PROPKEY_IGTLDEVICENAME = mitk::IGTLMessageSource::US_INTERFACE_NAME + ".igtldevicename"; mitk::IGTLDeviceSource::IGTLDeviceSource() : mitk::IGTLMessageSource(), m_IGTLDevice(nullptr) { this->SetName("IGTLDeviceSource (no defined type)"); } mitk::IGTLDeviceSource::~IGTLDeviceSource() { if (m_IGTLDevice.IsNotNull()) { if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running) { this->StopCommunication(); } if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Ready) { this->Disconnect(); } this->RemoveObservers(); m_IGTLDevice = nullptr; } } void mitk::IGTLDeviceSource::GenerateData() { if (m_IGTLDevice.IsNull()) return; /* update output with message from the device */ IGTLMessage* msgOut = this->GetOutput(); assert(msgOut); igtl::MessageBase::Pointer msgIn = dynamic_cast(m_IGTLDevice->GetNextImage2dMessage().GetPointer()); if (msgIn.IsNotNull()) { assert(msgIn); msgOut->SetMessage(msgIn); msgOut->SetName(msgIn->GetDeviceName()); } // else // { // MITK_ERROR("IGTLDeviceSource") << "Could not get the latest message."; // } } void mitk::IGTLDeviceSource::RemoveObservers() { if (this->m_IGTLDevice.IsNotNull()) { this->m_IGTLDevice->RemoveObserver(m_IncomingMessageObserverTag); this->m_IGTLDevice->RemoveObserver(m_IncomingCommandObserverTag); this->m_IGTLDevice->RemoveObserver(m_LostConnectionObserverTag); } } void mitk::IGTLDeviceSource::SetIGTLDevice(mitk::IGTLDevice* igtlDevice) { MITK_DEBUG << "Setting IGTLDevice to " << igtlDevice; if (this->m_IGTLDevice.GetPointer() != igtlDevice) { //check if we want to override the device if (this->m_IGTLDevice.IsNotNull()) { //the device was set previously => we need to reset the observers this->RemoveObservers(); } //set the device this->m_IGTLDevice = igtlDevice; this->CreateOutputs(); std::stringstream name; // create a human readable name for the source name << "OIGTL Device Source ( " << igtlDevice->GetName() << " )"; this->SetName(name.str()); //setup a observer that listens to new messages and new commands typedef itk::SimpleMemberCommand DeviceSrcCommand; DeviceSrcCommand::Pointer msgReceivedCommand = DeviceSrcCommand::New(); msgReceivedCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnIncomingMessage); this->m_IncomingMessageObserverTag = this->m_IGTLDevice->AddObserver(mitk::MessageReceivedEvent(), msgReceivedCommand); DeviceSrcCommand::Pointer cmdReceivedCommand = DeviceSrcCommand::New(); cmdReceivedCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnIncomingCommand); this->m_IncomingCommandObserverTag = this->m_IGTLDevice->AddObserver(mitk::CommandReceivedEvent(), cmdReceivedCommand); DeviceSrcCommand::Pointer connectionLostCommand = DeviceSrcCommand::New(); connectionLostCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnLostConnection); this->m_LostConnectionObserverTag = this->m_IGTLDevice->AddObserver(mitk::LostConnectionEvent(), connectionLostCommand); } } void mitk::IGTLDeviceSource::CreateOutputs() { //if outputs are set then delete them if (this->GetNumberOfOutputs() > 0) { for (int numOP = this->GetNumberOfOutputs() - 1; numOP >= 0; numOP--) this->RemoveOutput(numOP); this->Modified(); } //fill the outputs if a valid OpenIGTLink device is set if (m_IGTLDevice.IsNull()) return; this->SetNumberOfIndexedOutputs(1); if (this->GetOutput(0) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(0); this->SetNthOutput(0, newOutput); this->Modified(); } } void mitk::IGTLDeviceSource::Connect() { if (m_IGTLDevice.IsNull()) { throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); } if (this->IsConnected()) { return; } try { m_IGTLDevice->OpenConnection(); } catch (mitk::Exception &e) { throw std::runtime_error(std::string("mitk::IGTLDeviceSource: Could not open" "connection to OpenIGTLink device. Error: ") + e.GetDescription()); } } void mitk::IGTLDeviceSource::StartCommunication() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running) return; if (m_IGTLDevice->StartCommunication() == false) throw std::runtime_error("mitk::IGTLDeviceSource: " "Could not start communication"); } void mitk::IGTLDeviceSource::Disconnect() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->CloseConnection() == false) throw std::runtime_error("mitk::IGTLDeviceSource: Could not close connection" " to OpenIGTLink device"); } void mitk::IGTLDeviceSource::StopCommunication() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->StopCommunication() == false) throw std::runtime_error("mitk::IGTLDeviceSource: " "Could not stop communicating"); } void mitk::IGTLDeviceSource::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } void mitk::IGTLDeviceSource::SetInput(unsigned int idx, const IGTLMessage* msg) { if (msg == nullptr) // if an input is set to nullptr, remove it { this->RemoveInput(idx); } else { // ProcessObject is not const-correct so a const_cast is required here this->ProcessObject::SetNthInput(idx, const_cast(msg)); } // this->CreateOutputsForAllInputs(); } bool mitk::IGTLDeviceSource::IsConnected() { if (m_IGTLDevice.IsNull()) return false; return (m_IGTLDevice->GetState() == mitk::IGTLDevice::Ready) || (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running); } bool mitk::IGTLDeviceSource::IsCommunicating() { if (m_IGTLDevice.IsNull()) return false; return m_IGTLDevice->GetState() == mitk::IGTLDevice::Running; } void mitk::IGTLDeviceSource::RegisterAsMicroservice() { // Get Context us::ModuleContext* context = us::GetModuleContext(); // Define ServiceProps us::ServiceProperties props; mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.IGTLDeviceSource.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); props[US_PROPKEY_DEVICENAME] = this->GetName(); props[US_PROPKEY_IGTLDEVICENAME] = m_Name; props[US_PROPKEY_DEVICETYPE] = m_Type; m_ServiceRegistration = context->RegisterService(this, props); MITK_INFO << "Registered new DeviceSource as microservice: " << uidGen.GetUID(); } void mitk::IGTLDeviceSource::OnIncomingMessage() { } void mitk::IGTLDeviceSource::OnIncomingCommand() { } void mitk::IGTLDeviceSource::OnLostConnection() { } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(void) const { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(unsigned int idx) const { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(idx)); } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(std::string msgName) const { const DataObjectPointerArray& inputs = const_cast(this)->GetInputs(); for (DataObjectPointerArray::const_iterator it = inputs.begin(); it != inputs.end(); ++it) if (std::string(msgName) == (static_cast(it->GetPointer()))->GetName()) return static_cast(it->GetPointer()); return nullptr; } itk::ProcessObject::DataObjectPointerArraySizeType mitk::IGTLDeviceSource::GetInputIndex(std::string msgName) { DataObjectPointerArray outputs = this->GetInputs(); for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i) if (msgName == (static_cast(outputs.at(i).GetPointer()))->GetName()) return i; throw std::invalid_argument("output name does not exist"); } diff --git a/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp b/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp index d58ba5cf27..f14ab932c2 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp +++ b/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp @@ -1,63 +1,63 @@ /*============================================================================ 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 mitkPythonActivator_h #define mitkPythonActivator_h // Microservices +#include "mitkPythonService.h" #include #include "usModuleContext.h" -#include "mitkPythonService.h" #include namespace mitk { /// /// installs the PythonService /// runs all initial commands (setting env paths etc) /// class PythonActivator : public us::ModuleActivator { public: void Load(us::ModuleContext* context) override { MITK_DEBUG << "PythonActivator::Load"; // Registering PythonService as MicroService m_PythonService = itk::SmartPointer(new PythonService()); us::ServiceProperties _PythonServiceProps; _PythonServiceProps["Name"] = std::string("PythonService"); m_PythonServiceRegistration = context->RegisterService(m_PythonService.GetPointer(), _PythonServiceProps); } void Unload(us::ModuleContext*) override { MITK_DEBUG("PythonActivator") << "PythonActivator::Unload"; MITK_DEBUG("PythonActivator") << "m_PythonService GetReferenceCount " << m_PythonService->GetReferenceCount(); m_PythonServiceRegistration.Unregister(); m_PythonService->Delete(); MITK_DEBUG("PythonActivator") << "m_PythonService GetReferenceCount " << m_PythonService->GetReferenceCount(); } ~PythonActivator() override { } private: itk::SmartPointer m_PythonService; us::ServiceRegistration m_PythonServiceRegistration; }; } US_EXPORT_MODULE_ACTIVATOR(mitk::PythonActivator) #endif diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.h b/Modules/Python/autoload/PythonService/mitkPythonService.h index 31338dd8e1..c98f71a64c 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.h +++ b/Modules/Python/autoload/PythonService/mitkPythonService.h @@ -1,107 +1,111 @@ /*============================================================================ 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 mitkPythonService_h #define mitkPythonService_h #include -#include "mitkIPythonService.h" +#ifdef snprintf +#undef snprintf +#endif + +#include +#include #include -#include "mitkSurface.h" namespace mitk { /// /// implementation of the IPythonService using ctkabstractpythonmanager /// \see IPythonService class PythonService: public itk::LightObject, public mitk::IPythonService { public: /// /// instantiate python manager here PythonService(); /// /// empty implementation... ~PythonService() override; /// /// \see IPythonService::Execute() std::string Execute( const std::string& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) override; /// /// \see IPythonService::ExecuteScript() void ExecuteScript(const std::string &pathToPythonScript) override; /// /// \see IPythonService::PythonErrorOccured() bool PythonErrorOccured() const override; /// /// \see IPythonService::GetVariableStack() std::vector GetVariableStack() const override; /// /// \see IPythonService::DoesVariableExist() bool DoesVariableExist(const std::string& name) const override; /// /// \see IPythonService::GetVariable() std::string GetVariable(const std::string& name) const override; /// /// \see IPythonService::AddPythonCommandObserver() void AddPythonCommandObserver( PythonCommandObserver* observer ) override; /// /// \see IPythonService::RemovePythonCommandObserver() void RemovePythonCommandObserver( PythonCommandObserver* observer ) override; /// /// \see IPythonService::NotifyObserver() void NotifyObserver( const std::string& command ) override; /// /// \see IPythonService::IsItkPythonWrappingAvailable() bool IsSimpleItkPythonWrappingAvailable() override; /// /// \see IPythonService::CopyToPythonAsItkImage() bool CopyToPythonAsSimpleItkImage( mitk::Image* image, const std::string& varName ) override; /// /// \see IPythonService::CopyItkImageFromPython() mitk::Image::Pointer CopySimpleItkImageFromPython( const std::string& varName ) override; /// /// \see IPythonService::IsOpenCvPythonWrappingAvailable() bool IsOpenCvPythonWrappingAvailable() override; /// /// \see IPythonService::CopyToPythonAsCvImage() bool CopyToPythonAsCvImage( mitk::Image* image, const std::string& varName ) override; /// /// \see IPythonService::CopyCvImageFromPython() mitk::Image::Pointer CopyCvImageFromPython( const std::string& varName ) override; /// /// \see IPythonService::IsVtkPythonWrappingAvailable() bool IsVtkPythonWrappingAvailable() override; /// /// \see IPythonService::CopyToPythonAsVtkPolyData() bool CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& varName ) override; /// /// \see IPythonService::CopyVtkPolyDataFromPython() mitk::Surface::Pointer CopyVtkPolyDataFromPython( const std::string& varName ) override; /// /// \return the ctk abstract python manager instance ctkAbstractPythonManager* GetPythonManager() override; void AddRelativeSearchDirs(std::vector< std::string > dirs) override; void AddAbsoluteSearchDirs(std::vector< std::string > dirs) override; protected: private: QList m_Observer; ctkAbstractPythonManager m_PythonManager; bool m_ItkWrappingAvailable; bool m_OpenCVWrappingAvailable; bool m_VtkWrappingAvailable; bool m_ErrorOccured; }; } #endif diff --git a/Modules/Python/mitkIPythonService.h b/Modules/Python/mitkIPythonService.h index eb9c4d1dad..e53457d24a 100644 --- a/Modules/Python/mitkIPythonService.h +++ b/Modules/Python/mitkIPythonService.h @@ -1,153 +1,155 @@ /*============================================================================ 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 mitkIPythonService_h #define mitkIPythonService_h // mitk +#include +#include #include -#include "mitkImage.h" + //for microservices #include -#include "mitkSurface.h" + #include class ctkAbstractPythonManager; namespace mitk { /// /// describes a python variable (data container) /// \see IPythonService::GetVariableStack() /// struct PythonVariable { std::string m_Name; std::string m_Type; std::string m_Value; }; /// /// a PythonCommandObserver gets informed as soon as a python command was issued /// \see IPythonService::AddPythonCommandObserver() /// class PythonCommandObserver { public: virtual void CommandExecuted(const std::string& pythonCommand) = 0; }; /// /// The central service for issuing Python Code /// The class also enables to transfer mitk images to python as itk::Image and vice versa /// \see IPythonService::GetVariableStack() /// class MITKPYTHON_EXPORT IPythonService { public: /// /// Constant representing a single line command /// \see IPythonService::Execute() static const int SINGLE_LINE_COMMAND = 0; /// /// Constant representing a command in which the commands are seperated by new lines, i.e. "\\n" /// \see IPythonService::Execute() static const int MULTI_LINE_COMMAND = 1; /// /// Constant representing a single line command x which is run as "eval(x)" /// \see IPythonService::Execute() static const int EVAL_COMMAND = 2; /// /// Executes a python command. /// \return A variant containing the return value as string of the python code (if any) virtual std::string Execute( const std::string& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) = 0; /// /// Executes a python script. virtual void ExecuteScript( const std::string& pathToPythonScript ) = 0; /// /// \return true if the last call to Execute...() resulted in an error, false otherwise virtual bool PythonErrorOccured() const = 0; /// /// \return The list of variables in the __main__ namespace virtual std::vector GetVariableStack() const = 0; /// /// \return true if a variable with this name is defined in the __main__ namespace, false otherwise virtual bool DoesVariableExist(const std::string& name) const = 0; /// /// \return value of variable with this name as string, empty string if variable does not exist virtual std::string GetVariable(const std::string& name) const = 0; /// /// adds a command observer which is informed after a command was issued with "Execute" virtual void AddPythonCommandObserver( PythonCommandObserver* observer ) = 0; /// /// removes a specific command observer virtual void RemovePythonCommandObserver( PythonCommandObserver* observer ) = 0; /// /// notify all observer. this should only be used if it can be garantueed that the /// current python interpreter instance got another command from anywhere else /// the the Execute() method of this service, e.g. the shell widget uses this function /// since it does not use Execute() virtual void NotifyObserver( const std::string& command ) = 0; /// /// \return true, if itk wrapping is available, false otherwise virtual bool IsSimpleItkPythonWrappingAvailable() = 0; /// /// copies an mitk image as itk image into the python interpreter process /// the image will be available as "varName" in python if everythin worked /// \return true if image was copied, else false virtual bool CopyToPythonAsSimpleItkImage( mitk::Image* image, const std::string& varName ) = 0; /// /// copies an itk image from the python process that is named "varName" /// \return the image or 0 if copying was not possible virtual mitk::Image::Pointer CopySimpleItkImageFromPython( const std::string& varName ) = 0; /// /// \return true, if OpenCv wrapping is available, false otherwise virtual bool IsOpenCvPythonWrappingAvailable() = 0; /// /// \see CopyToPythonAsItkImage() virtual bool CopyToPythonAsCvImage( mitk::Image* image, const std::string& varName ) = 0; /// /// \see CopyCvImageFromPython() virtual mitk::Image::Pointer CopyCvImageFromPython( const std::string& varName ) = 0; /// /// \return true, if vtk wrapping is available, false otherwise virtual bool IsVtkPythonWrappingAvailable() = 0; /// /// \see CopyToPythonAsItkImage() virtual bool CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& varName ) = 0; /// /// \see CopyCvImageFromPython() virtual mitk::Surface::Pointer CopyVtkPolyDataFromPython( const std::string& varName ) = 0; /// \return the ctk abstract python manager instance virtual ctkAbstractPythonManager* GetPythonManager() = 0; /// /// nothing to do here virtual ~IPythonService(); // leer in mitkIPythonService.cpp implementieren // force us module loading by linking static std::string ForceLoadModule(); virtual void AddRelativeSearchDirs(std::vector< std::string > dirs) = 0; virtual void AddAbsoluteSearchDirs(std::vector< std::string > dirs) = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IPythonService, "org.mitk.services.IPythonService") #endif diff --git a/Modules/QtPython/QmitkCtkPythonShell.cpp b/Modules/QtPython/QmitkCtkPythonShell.cpp index ebdbbf36d8..dec9e9168a 100644 --- a/Modules/QtPython/QmitkCtkPythonShell.cpp +++ b/Modules/QtPython/QmitkCtkPythonShell.cpp @@ -1,94 +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. ============================================================================*/ #include "QmitkCtkPythonShell.h" #include +#ifdef snprintf +#undef snprintf +#endif + +#include #include #include #include #include -#include "mitkIPythonService.h" #include #include #include struct QmitkCtkPythonShellData { mitk::IPythonService* m_PythonService; us::ServiceReference m_PythonServiceRef; }; QmitkCtkPythonShell::QmitkCtkPythonShell(QWidget* parent) : ctkPythonConsole(parent), d( new QmitkCtkPythonShellData ) { this->setWelcomeTextColor(Qt::green); this->setPromptColor(Qt::gray); this->setStdinTextColor(Qt::white); this->setCommandTextColor(Qt::white); this->setOutputTextColor(Qt::white); MITK_DEBUG("QmitkCtkPythonShell") << "retrieving IPythonService"; us::ModuleContext* context = us::GetModuleContext(); d->m_PythonServiceRef = context->GetServiceReference(); d->m_PythonService = dynamic_cast ( context->GetService(d->m_PythonServiceRef) ); MITK_DEBUG("QmitkCtkPythonShell") << "checking IPythonService"; Q_ASSERT( d->m_PythonService ); MITK_DEBUG("QmitkCtkPythonShell") << "initialize m_PythonService"; this->initialize( d->m_PythonService->GetPythonManager() ); MITK_DEBUG("QmitkCtkPythonShell") << "m_PythonService initialized"; mitk::IPythonService::ForceLoadModule(); } QmitkCtkPythonShell::~QmitkCtkPythonShell() { us::ModuleContext* context = us::GetModuleContext(); context->UngetService( d->m_PythonServiceRef ); delete d; } void QmitkCtkPythonShell::dragEnterEvent(QDragEnterEvent *event) { event->accept(); } void QmitkCtkPythonShell::dropEvent(QDropEvent *event) { QList urls = event->mimeData()->urls(); for(int i = 0; i < urls.size(); i++) { d->m_PythonService->Execute( urls[i].toString().toStdString(), mitk::IPythonService::SINGLE_LINE_COMMAND ); } } bool QmitkCtkPythonShell::canInsertFromMimeData(const QMimeData *) const { return true; } void QmitkCtkPythonShell::executeCommand(const QString& command) { MITK_DEBUG("QmitkCtkPythonShell") << "executing command " << command.toStdString(); d->m_PythonService->Execute(command.toStdString(),mitk::IPythonService::MULTI_LINE_COMMAND); d->m_PythonService->NotifyObserver(command.toStdString()); } void QmitkCtkPythonShell::Paste(const QString &command) { if( this->isVisible() ) { this->exec( command ); //this->executeCommand( command ); } } diff --git a/Modules/QtWidgets/resource/GeometryDataIcon.svg b/Modules/QtWidgets/resource/GeometryDataIcon.svg new file mode 100644 index 0000000000..090de6c5ef --- /dev/null +++ b/Modules/QtWidgets/resource/GeometryDataIcon.svg @@ -0,0 +1,19 @@ + + + + + +image/svg+xml + + + + + + + + + + + + + diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index 377d75c396..f284857d3e 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -1,34 +1,36 @@ Binaerbilder_48.png Images_48.png PointSet_48.png Segmentation_48.png Surface_48.png mm_pointer.png mm_scroll.png mm_zoom.png mm_contrast.png mm_pan.png LabelSetImage_48.png mwLayout.png mwSynchronized.png mwDesynchronized.png mwMITK.png mwPACS.png star-solid.svg history-solid.svg tree_inspector.svg list-solid.svg favorite_add.svg favorite_remove.svg hourglass-half-solid.svg times.svg reset.svg lock.svg unlock.svg invisible.svg visible.svg SegmentationTaskListIcon.svg + ROIIcon.svg + GeometryDataIcon.svg diff --git a/Modules/QtWidgets/resource/ROIIcon.svg b/Modules/QtWidgets/resource/ROIIcon.svg new file mode 100644 index 0000000000..dc1421047b --- /dev/null +++ b/Modules/QtWidgets/resource/ROIIcon.svg @@ -0,0 +1,15 @@ + + + + + +image/svg+xml + + + + + + + + + diff --git a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp index bd279a298a..ab142499b0 100644 --- a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp +++ b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp @@ -1,175 +1,183 @@ /*============================================================================ 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 "QmitkNodeDescriptorManager.h" #include #include #include #include #include #include #include #include #include QmitkNodeDescriptorManager* QmitkNodeDescriptorManager::GetInstance() { static QmitkNodeDescriptorManager instance; return &instance; } void QmitkNodeDescriptorManager::Initialize() { auto isImage = mitk::NodePredicateDataType::New("Image"); AddDescriptor(new QmitkNodeDescriptor(tr("Image"), QString(":/Qmitk/Images_48.png"), isImage, this)); auto isMultiComponentImage = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateProperty::New("Image.Displayed Component")); AddDescriptor(new QmitkNodeDescriptor(tr("MultiComponentImage"), QString(": / Qmitk / Images_48.png"), isMultiComponentImage, this)); auto isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); auto isBinaryImage = mitk::NodePredicateAnd::New(isBinary, isImage); AddDescriptor(new QmitkNodeDescriptor(tr("ImageMask"), QString(":/Qmitk/Binaerbilder_48.png"), isBinaryImage, this)); auto isLabelSetImage = mitk::NodePredicateDataType::New("LabelSetImage"); AddDescriptor(new QmitkNodeDescriptor(tr("LabelSetImage"), QString(":/Qmitk/LabelSetImage_48.png"), isLabelSetImage, this)); auto segmentationTaskListIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/SegmentationTaskListIcon.svg")); auto isSegmentationTaskList = mitk::NodePredicateDataType::New("SegmentationTaskList"); AddDescriptor(new QmitkNodeDescriptor("SegmentationTaskList", segmentationTaskListIcon, isSegmentationTaskList, this)); + auto roiIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/ROIIcon.svg")); + auto isROI = mitk::NodePredicateDataType::New("ROI"); + AddDescriptor(new QmitkNodeDescriptor("ROI", roiIcon, isROI, this)); + + auto geometryDataIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/GeometryDataIcon.svg")); + auto isGeometryData = mitk::NodePredicateDataType::New("GeometryData"); + AddDescriptor(new QmitkNodeDescriptor("GeometryData", geometryDataIcon, isGeometryData, this)); + auto isPointSet = mitk::NodePredicateDataType::New("PointSet"); AddDescriptor(new QmitkNodeDescriptor(tr("PointSet"), QString(":/Qmitk/PointSet_48.png"), isPointSet, this)); auto isSurface = mitk::NodePredicateDataType::New("Surface"); AddDescriptor(new QmitkNodeDescriptor(tr("Surface"), QString(":/Qmitk/Surface_48.png"), isSurface, this)); auto isNotBinary = mitk::NodePredicateNot::New(isBinary); auto isNoneBinaryImage = mitk::NodePredicateAnd::New(isImage, isNotBinary); AddDescriptor(new QmitkNodeDescriptor(tr("NoneBinaryImage"), QString(":/Qmitk/Images_48.png"), isNoneBinaryImage, this)); } void QmitkNodeDescriptorManager::AddDescriptor(QmitkNodeDescriptor* descriptor) { descriptor->setParent(this); m_NodeDescriptors.push_back(descriptor); } void QmitkNodeDescriptorManager::RemoveDescriptor(QmitkNodeDescriptor* descriptor) { int index = m_NodeDescriptors.indexOf(descriptor); if (index != -1) { m_NodeDescriptors.removeAt(index); descriptor->setParent(nullptr); delete descriptor; } } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetDescriptor(const mitk::DataNode* node) const { QmitkNodeDescriptor* descriptor = m_UnknownDataNodeDescriptor; for (QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->CheckNode(node)) descriptor = *it; } return descriptor; } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetDescriptor(const QString& className) const { QmitkNodeDescriptor* descriptor = nullptr; if (className == "Unknown") { return m_UnknownDataNodeDescriptor; } else { for (QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->GetNameOfClass() == className) descriptor = *it; } } return descriptor; } QList QmitkNodeDescriptorManager::GetActions(const mitk::DataNode* node) const { QList actions = m_UnknownDataNodeDescriptor->GetBatchActions(); actions.append(m_UnknownDataNodeDescriptor->GetActions()); QmitkNodeDescriptor* lastDescriptor = m_UnknownDataNodeDescriptor; for (QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->CheckNode(node)) { actions.append(lastDescriptor->GetSeparator()); lastDescriptor = *it; actions.append(lastDescriptor->GetBatchActions()); actions.append(lastDescriptor->GetActions()); } } return actions; } QList QmitkNodeDescriptorManager::GetActions(const QList& nodes) const { QList actions = m_UnknownDataNodeDescriptor->GetBatchActions(); QmitkNodeDescriptor* lastDescriptor = m_UnknownDataNodeDescriptor; // find all descriptors for the nodes (unique) QSet nodeDescriptors; for (const auto& node : nodes) { for (QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->CheckNode(node)) { nodeDescriptors.insert(*it); } } } // add all actions for the found descriptors for (const auto& nodeDescriptor : nodeDescriptors) { actions.append(lastDescriptor->GetSeparator()); lastDescriptor = nodeDescriptor; actions.append(lastDescriptor->GetBatchActions()); } return actions; } QmitkNodeDescriptorManager::QmitkNodeDescriptorManager() : m_UnknownDataNodeDescriptor(new QmitkNodeDescriptor("Unknown", QString(":/Qmitk/DataTypeUnknown_48.png"), nullptr, this)) { Initialize(); } QmitkNodeDescriptorManager::~QmitkNodeDescriptorManager() { // delete m_UnknownDataNodeDescriptor; // qDeleteAll(m_NodeDescriptors); } QmitkNodeDescriptor *QmitkNodeDescriptorManager::GetUnknownDataNodeDescriptor() const { return m_UnknownDataNodeDescriptor; } diff --git a/Modules/ROI/CMakeLists.txt b/Modules/ROI/CMakeLists.txt new file mode 100644 index 0000000000..e4ab9d0abe --- /dev/null +++ b/Modules/ROI/CMakeLists.txt @@ -0,0 +1,6 @@ +mitk_create_module( + DEPENDS MitkCore + PACKAGE_DEPENDS VTK|RenderingAnnotation +) + +add_subdirectory(autoload/IO) diff --git a/Modules/ROI/autoload/IO/CMakeLists.txt b/Modules/ROI/autoload/IO/CMakeLists.txt new file mode 100644 index 0000000000..984bfe1eaa --- /dev/null +++ b/Modules/ROI/autoload/IO/CMakeLists.txt @@ -0,0 +1,5 @@ +mitk_create_module(ROIIO + DEPENDS PUBLIC MitkROI MitkSceneSerialization + PACKAGE_DEPENDS PRIVATE nlohmann_json + AUTOLOAD_WITH MitkCore +) diff --git a/Modules/ROI/autoload/IO/files.cmake b/Modules/ROI/autoload/IO/files.cmake new file mode 100644 index 0000000000..cd11c12ddf --- /dev/null +++ b/Modules/ROI/autoload/IO/files.cmake @@ -0,0 +1,13 @@ +set(H_FILES + include/mitkROIIOMimeTypes.h + src/mitkROIIO.h + src/mitkROIIOModuleActivator.h + src/mitkROISerializer.h +) + +set(CPP_FILES + mitkROIIO.cpp + mitkROIIOMimeTypes.cpp + mitkROIIOModuleActivator.cpp + mitkROISerializer.cpp +) diff --git a/Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h b/Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h new file mode 100644 index 0000000000..75bdad237e --- /dev/null +++ b/Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h @@ -0,0 +1,38 @@ +/*============================================================================ + +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 mitkROIIOMimeTypes_h +#define mitkROIIOMimeTypes_h + +#include +#include + +namespace mitk +{ + namespace MitkROIIOMimeTypes + { + class MITKROIIO_EXPORT MitkROIMimeType : public CustomMimeType + { + public: + MitkROIMimeType(); + + bool AppliesTo(const std::string& path) const override; + MitkROIMimeType* Clone() const override; + }; + + MITKROIIO_EXPORT MitkROIMimeType ROI_MIMETYPE(); + MITKROIIO_EXPORT std::string ROI_MIMETYPE_NAME(); + MITKROIIO_EXPORT std::vector Get(); + } +} + +#endif diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp new file mode 100644 index 0000000000..ade9c8016d --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -0,0 +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 "mitkROIIO.h" +#include +#include +#include + +#include +#include + +namespace +{ + int CheckFileFormat(const nlohmann::json& json) + { + if ("MITK ROI" != json["FileFormat"].get()) + mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; + + auto version = json["Version"].get(); + + if (1 != version) + mitkThrow() << "Unknown file format version (expected version 1)!"; + + return version; + } + + mitk::Vector3D GetSize(const mitk::BaseGeometry* geometry) + { + auto bounds = geometry->GetBounds(); + + mitk::Vector3D result; + result[0] = bounds[1]; + result[1] = bounds[3]; + result[2] = bounds[5]; + + return result; + } + + void SetSize(mitk::BaseGeometry* geometry, const mitk::Vector3D& size) + { + mitk::BaseGeometry::BoundsArrayType bounds({ 0.0, size[0], 0.0, size[1], 0.0, size[2] }); + geometry->SetBounds(bounds); + } + + mitk::TimeGeometry::Pointer ReadGeometry(const nlohmann::json& jGeometry) + { + auto geometry = mitk::Geometry3D::New(); + geometry->ImageGeometryOn(); + + if (!jGeometry.is_object()) + mitkThrow() << "Geometry is expected to be a JSON object."; + + if (jGeometry.contains("Origin")) + geometry->SetOrigin(jGeometry["Origin"].get()); + + if (jGeometry.contains("Spacing")) + geometry->SetSpacing(jGeometry["Spacing"].get()); + + if (jGeometry.contains("Size")) + SetSize(geometry, jGeometry["Size"].get()); + + auto timeSteps = jGeometry.contains("TimeSteps") + ? jGeometry["TimeSteps"].get() + : 1; + + auto result = mitk::ProportionalTimeGeometry::New(); + result->Initialize(geometry, timeSteps); + + return result; + } + + nlohmann::json WriteGeometry(const mitk::TimeGeometry* timeGeometry) + { + auto geometry = timeGeometry->GetGeometryForTimeStep(0); + + nlohmann::json result = { + { "Origin", geometry->GetOrigin() }, + { "Spacing", geometry->GetSpacing() }, + { "Size", GetSize(geometry) } + }; + + auto timeSteps = timeGeometry->CountTimeSteps(); + + if (timeSteps > 1) + result["TimeSteps"] = timeSteps; + + return result; + } +} + +mitk::ROIIO::ROIIO() + : AbstractFileIO(ROI::GetStaticNameOfClass(), MitkROIIOMimeTypes::ROI_MIMETYPE(), "MITK ROI") +{ + this->RegisterService(); +} + +std::vector mitk::ROIIO::DoRead() +{ + auto *stream = this->GetInputStream(); + std::ifstream fileStream; + + if (nullptr == stream) + { + auto filename = this->GetInputLocation(); + + if (filename.empty() || !std::filesystem::exists(filename)) + mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!"; + + fileStream.open(filename); + + if (!fileStream.is_open()) + mitkThrow() << "Could not open file \"" << filename << "\" for reading!"; + + stream = &fileStream; + } + + auto result = ROI::New(); + + try + { + auto j = nlohmann::json::parse(*stream); + + CheckFileFormat(j); + + auto geometry = ReadGeometry(j["Geometry"]); + result->SetTimeGeometry(geometry); + + if (j.contains("Name")) + result->SetProperty("name", mitk::StringProperty::New(j["Name"].get())); + + if (j.contains("Caption")) + result->SetProperty("caption", mitk::StringProperty::New(j["Caption"].get())); + + for (const auto& roi : j["ROIs"]) + result->AddElement(roi.get()); + } + catch (const nlohmann::json::exception &e) + { + mitkThrow() << e.what(); + } + + return { result }; +} + +void mitk::ROIIO::Write() +{ + auto input = dynamic_cast(this->GetInput()); + + if (input == nullptr) + mitkThrow() << "Invalid input for writing!"; + + if (input->GetNumberOfElements() == 0) + mitkThrow() << "No ROIs found!"; + + auto* stream = this->GetOutputStream(); + std::ofstream fileStream; + + if (stream == nullptr) + { + auto filename = this->GetOutputLocation(); + + if (filename.empty()) + mitkThrow() << "Neither an output stream nor an output filename was specified!"; + + fileStream.open(filename); + + if (!fileStream.is_open()) + mitkThrow() << "Could not open file \"" << filename << "\" for writing!"; + + stream = &fileStream; + } + + if (!stream->good()) + mitkThrow() << "Stream for writing is not good!"; + + nlohmann::ordered_json j = { + { "FileFormat", "MITK ROI" }, + { "Version", 1 } + }; + + if (auto name = input->GetProperty("name"); name.IsNotNull()) + j["Name"] = name->GetValueAsString(); + + j["Geometry"] = WriteGeometry(input->GetTimeGeometry()); + + auto caption = input->GetConstProperty("caption"); + + if (caption.IsNotNull()) + j["Caption"] = caption->GetValueAsString(); + + nlohmann::json rois; + + for (const auto& roi : *input) + rois.push_back(roi.second); + + j["ROIs"] = rois; + + *stream << std::setw(2) << j << std::endl; +} + +mitk::ROIIO* mitk::ROIIO::IOClone() const +{ + return new ROIIO(*this); +} diff --git a/Modules/Core/src/DataManagement/mitkVector.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.h similarity index 52% rename from Modules/Core/src/DataManagement/mitkVector.cpp rename to Modules/ROI/autoload/IO/src/mitkROIIO.h index a9ca6ccfb5..dee6434802 100644 --- a/Modules/Core/src/DataManagement/mitkVector.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.h @@ -1,13 +1,36 @@ /*============================================================================ 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 "mitkVector.h" +#ifndef mitkROIIO_h +#define mitkROIIO_h + +#include + +namespace mitk +{ + class ROIIO : public AbstractFileIO + { + public: + ROIIO(); + + using AbstractFileReader::Read; + void Write() override; + + protected: + std::vector DoRead() override; + + private: + ROIIO* IOClone() const override; + }; +} + +#endif diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp b/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp new file mode 100644 index 0000000000..91b8032fe5 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp @@ -0,0 +1,75 @@ +/*============================================================================ + +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 + +mitk::MitkROIIOMimeTypes::MitkROIMimeType::MitkROIMimeType() + : CustomMimeType(ROI_MIMETYPE_NAME()) +{ + this->AddExtension("json"); + this->SetCategory("MITK ROI"); + this->SetComment("MITK ROI"); +} + +bool mitk::MitkROIIOMimeTypes::MitkROIMimeType::AppliesTo(const std::string& path) const +{ + bool result = CustomMimeType::AppliesTo(path); + + if (!std::filesystem::exists(path)) // T18572 + return result; + + std::ifstream file(path); + + if (!file.is_open()) + return false; + + auto json = nlohmann::json::parse(file, nullptr, false); + + if (json.is_discarded() || !json.is_object()) + return false; + + if ("MITK ROI" != json.value("FileFormat", "")) + return false; + + if (1 != json.value("Version", 0)) + return false; + + return true; +} + +mitk::MitkROIIOMimeTypes::MitkROIMimeType* mitk::MitkROIIOMimeTypes::MitkROIMimeType::Clone() const +{ + return new MitkROIMimeType(*this); +} + +mitk::MitkROIIOMimeTypes::MitkROIMimeType mitk::MitkROIIOMimeTypes::ROI_MIMETYPE() +{ + return MitkROIMimeType(); +} + +std::string mitk::MitkROIIOMimeTypes::ROI_MIMETYPE_NAME() +{ + return IOMimeTypes::DEFAULT_BASE_NAME() + ".roi"; +} + +std::vector mitk::MitkROIIOMimeTypes::Get() +{ + std::vector mimeTypes; + mimeTypes.push_back(ROI_MIMETYPE().Clone()); + return mimeTypes; +} diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp new file mode 100644 index 0000000000..2c9304f004 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp @@ -0,0 +1,37 @@ +/*============================================================================ + +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 "mitkROIIOModuleActivator.h" + +#include +#include "mitkROIIO.h" + +#include + +US_EXPORT_MODULE_ACTIVATOR(mitk::ROIIOModuleActivator) + +void mitk::ROIIOModuleActivator::Load(us::ModuleContext* context) +{ + auto mimeTypes = MitkROIIOMimeTypes::Get(); + + us::ServiceProperties props; + props[us::ServiceConstants::SERVICE_RANKING()] = 10; + + for (auto mimeType : mimeTypes) + context->RegisterService(mimeType, props); + + m_FileIOs.push_back(std::make_shared()); +} + +void mitk::ROIIOModuleActivator::Unload(us::ModuleContext*) +{ +} diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h new file mode 100644 index 0000000000..d7c3378a01 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h @@ -0,0 +1,37 @@ +/*============================================================================ + +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 + +namespace mitk +{ + class AbstractFileIO; + + class ROIIOModuleActivator : public us::ModuleActivator + { + public: + ROIIOModuleActivator() = default; + ~ROIIOModuleActivator() override = default; + + ROIIOModuleActivator(const ROIIOModuleActivator&) = delete; + ROIIOModuleActivator& operator=(const ROIIOModuleActivator&) = delete; + + void Load(us::ModuleContext* context) override; + void Unload(us::ModuleContext*) override; + + private: + std::vector> m_FileIOs; + }; +} diff --git a/Modules/ROI/autoload/IO/src/mitkROISerializer.cpp b/Modules/ROI/autoload/IO/src/mitkROISerializer.cpp new file mode 100644 index 0000000000..6148f54324 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROISerializer.cpp @@ -0,0 +1,55 @@ +/*============================================================================ + +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 "mitkROISerializer.h" + +#include +#include + +MITK_REGISTER_SERIALIZER(ROISerializer) + +mitk::ROISerializer::ROISerializer() +{ +} + +mitk::ROISerializer::~ROISerializer() +{ +} + +std::string mitk::ROISerializer::Serialize() +{ + auto roi = dynamic_cast(m_Data.GetPointer()); + + if (nullptr == roi) + { + MITK_ERROR << "Object at " << (const void*)this->m_Data << " is not an mitk::ROI. Cannot serialize as MITK ROI."; + return ""; + } + + auto filename = this->GetUniqueFilenameInWorkingDirectory(); + filename += "_" + m_FilenameHint + ".json"; + + std::string path = m_WorkingDirectory; + path += IOUtil::GetDirectorySeparator() + filename; + + try + { + IOUtil::Save(roi, path); + } + catch (std::exception& e) + { + MITK_ERROR << "Error serializing object at " << (const void*)this->m_Data << " to " << path << ": " << e.what(); + return ""; + } + + return filename; +} diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp b/Modules/ROI/autoload/IO/src/mitkROISerializer.h similarity index 51% copy from Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp copy to Modules/ROI/autoload/IO/src/mitkROISerializer.h index 0902061f1c..0daf876a01 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROISerializer.h @@ -1,26 +1,34 @@ /*============================================================================ 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 +#ifndef mitkROISerializer_h +#define mitkROISerializer_h -#include "mitkPluginActivator.h" -#include "QmitkImageCropperView.h" +#include -void mitk::mitkPluginActivator::start(ctkPluginContext* context) +namespace mitk { - RegisterBoundingShapeObjectFactory(); - BERRY_REGISTER_EXTENSION_CLASS(QmitkImageCropperView, context) + class ROISerializer : public BaseDataSerializer + { + public: + mitkClassMacro(ROISerializer, BaseDataSerializer) + itkFactorylessNewMacro(Self) + + std::string Serialize() override; + + protected: + ROISerializer(); + ~ROISerializer() override; + }; } -void mitk::mitkPluginActivator::stop(ctkPluginContext*) -{ -} +#endif diff --git a/Modules/ROI/files.cmake b/Modules/ROI/files.cmake new file mode 100644 index 0000000000..1de29e9b16 --- /dev/null +++ b/Modules/ROI/files.cmake @@ -0,0 +1,17 @@ +set(H_FILES + include/mitkROI.h + include/mitkROIMapper2D.h + include/mitkROIMapper3D.h + include/mitkROIMapperLocalStorage.h + src/mitkROIMapperHelper.h + src/mitkROIObjectFactory.h +) + +set(CPP_FILES + mitkROI.cpp + mitkROIMapper2D.cpp + mitkROIMapper3D.cpp + mitkROIMapperHelper.cpp + mitkROIMapperLocalStorage.cpp + mitkROIObjectFactory.cpp +) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h new file mode 100644 index 0000000000..25fb2ad7bc --- /dev/null +++ b/Modules/ROI/include/mitkROI.h @@ -0,0 +1,202 @@ +/*============================================================================ + +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 mitkROI_h +#define mitkROI_h + +#include +#include + +namespace mitk +{ + /** \brief A collection of region of interests (ROIs). + * + * \note This class is considered experimental and subject to substational change. We mean it. + * + * ROIs, essentially defined by the minimum and maximum index coordinates of an axis-aligned box, are + * represented by the nested ROI::Element class. These index coordinates are relative to a common + * TimeGeometry. + * + * All ROIs are required to have a unique ID by which they can be accessed. + * + * ROIs can optionally have properties (PropertyList), also called default properties. In case of + * time-resolved ROIs, each time step can optionally have properties, too. These properties have + * precedence over the default properties. In other words, the default properties may contain + * fallback properties which are queried when a property is not defined at a certain time step. + * This allows for an opt-in dynamic appearance of ROIs over time, for example by changing + * color or opacity. + * + * ROIs are rendered both in 3-d and 2-d views as cubes, resp. cutting slices of these cubes. + * They support the following ROI::Element properties: + * + * - \c color (ColorProperty): Color of the cube + * - \c opacity (FloatProperty): Opacity of the cube + * - \c lineWidth (FloatProperty): %Line width of the cube edges + * + * ROIs display a customizable caption at their bottom-left corner. It is defined by the base data + * property \c caption (StringProperty). By default it is set to "{name} ({ID})". Braces + * are used to define placeholders which are replaced by their corresponding ROI::Element properties. + * {ID} is a special placeholder which will be replaced by the ID of the ROI::Element instead. + * The caption is allowed to include line breaks. Rendering of captions can be customized through the + * following data node properties: + * + * - \c font.size (IntProperty) Font size in points + * - \c font.bold (BoolProperty) Bold font style + * - \c font.italic (BoolProperty) Italic font style + * + * See \ref MITKROIPage for details on the JSON-based MITK %ROI file format. + */ + class MITKROI_EXPORT ROI : public BaseData + { + public: + /** \brief Encapsulates a single (possibly time-resolved) %ROI. + * + * \sa ROI + */ + class MITKROI_EXPORT Element : public IPropertyOwner + { + public: + using PointsType = std::map; + using PropertyListsType = std::map; + + Element(); + explicit Element(unsigned int id); + ~Element() = default; + + /** \brief Get a const property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ + BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) const override; + BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = true) const; + + /** \brief Get all property keys. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ + std::vector GetPropertyKeys(const std::string& contextName = "", bool includeDefaultContext = false) const override; + std::vector GetPropertyKeys(TimeStepType t, bool includeDefaultContext = false) const; + + /** \brief Get all property context names (stringified time steps). + */ + std::vector GetPropertyContextNames() const override; + + /** \brief Get a property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ + BaseProperty* GetNonConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) override; + BaseProperty* GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = true); + + /** \brief Set a property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ + void SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + void SetProperty(const std::string& propertyKey, BaseProperty* property, TimeStepType t, bool fallBackOnDefaultContext = false); + + /** \brief Remove a property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ + void RemoveProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + void RemoveProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = false); + + unsigned int GetID() const; + void SetID(unsigned int id); + + /** \brief Check if the %ROI is defined for a certain time step. + */ + bool HasTimeStep(TimeStepType t) const; + + /** \brief Check if the %ROI can be considered time-resolved. + */ + bool HasTimeSteps() const; + + /** \brief Get all valid time steps that have a minimum point and a maximum point. + */ + std::vector GetTimeSteps() const; + + Point3D GetMin(TimeStepType t = 0) const; + void SetMin(const Point3D& min, TimeStepType t = 0); + + Point3D GetMax(TimeStepType t = 0) const; + void SetMax(const Point3D& max, TimeStepType t = 0); + + PropertyList* GetDefaultProperties() const; + void SetDefaultProperties(PropertyList* properties); + + /** \brief Get properties for a certain time step or \c nullptr if absent. + */ + PropertyList* GetProperties(TimeStepType t = 0) const; + + void SetProperties(PropertyList* properties, TimeStepType t = 0); + + private: + unsigned int m_ID; + PointsType m_Min; + PointsType m_Max; + PropertyList::Pointer m_DefaultProperties; + PropertyListsType m_Properties; + }; + + mitkClassMacro(ROI, BaseData) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + using ElementsType = std::map; + using Iterator = ElementsType::iterator; + using ConstIterator = ElementsType::const_iterator; + + size_t GetNumberOfElements() const; + + /** \brief Add a ROI::Element to the collection. + * + * \note The ID of the ROI::Element must be set to a unique number in advance. + */ + void AddElement(const Element& element); + + const Element& GetElement(unsigned int id) const; + Element& GetElement(unsigned int id); + + ConstIterator begin() const; + ConstIterator end() const; + + Iterator begin(); + Iterator end(); + + void SetRequestedRegionToLargestPossibleRegion() override; + bool RequestedRegionIsOutsideOfTheBufferedRegion() override; + bool VerifyRequestedRegion() override; + void SetRequestedRegion(const itk::DataObject* data) override; + + protected: + mitkCloneMacro(Self) + + ROI(); + ROI(const Self& other); + ~ROI() override; + + private: + ElementsType m_Elements; + }; + + MITKROI_EXPORT void to_json(nlohmann::json& j, const ROI::Element& roi); + MITKROI_EXPORT void from_json(const nlohmann::json& j, ROI::Element& roi); +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h new file mode 100644 index 0000000000..173915aaec --- /dev/null +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -0,0 +1,58 @@ +/*============================================================================ + +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 mitkROIMapper2D_h +#define mitkROIMapper2D_h + +#include +#include +#include +#include + +namespace mitk +{ + class MITKROI_EXPORT ROIMapper2D : public VtkMapper + { + class LocalStorage : public ROIMapperLocalStorage + { + public: + LocalStorage(); + ~LocalStorage() override; + + const PlaneGeometry* GetLastPlaneGeometry() const; + void SetLastPlaneGeometry(const PlaneGeometry* planeGeometry); + + protected: + PlaneGeometry::ConstPointer m_LastPlaneGeometry; + }; + + public: + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool override = false); + + mitkClassMacro(ROIMapper2D, VtkMapper) + itkFactorylessNewMacro(Self) + + vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; + + protected: + ROIMapper2D(); + ~ROIMapper2D() override; + + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; + + private: + LocalStorageHandler m_LocalStorageHandler; + }; +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapper3D.h b/Modules/ROI/include/mitkROIMapper3D.h new file mode 100644 index 0000000000..e57d3b148a --- /dev/null +++ b/Modules/ROI/include/mitkROIMapper3D.h @@ -0,0 +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 mitkROIMapper3D_h +#define mitkROIMapper3D_h + +#include +#include +#include +#include + +namespace mitk +{ + class MITKROI_EXPORT ROIMapper3D : public VtkMapper + { + class LocalStorage : public ROIMapperLocalStorage + { + public: + LocalStorage(); + ~LocalStorage() override; + }; + + public: + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool override = false); + + mitkClassMacro(ROIMapper3D, VtkMapper) + itkFactorylessNewMacro(Self) + + vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; + + protected: + ROIMapper3D(); + ~ROIMapper3D() override; + + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; + + private: + LocalStorageHandler m_LocalStorageHandler; + }; +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapperLocalStorage.h b/Modules/ROI/include/mitkROIMapperLocalStorage.h new file mode 100644 index 0000000000..567d1eca8e --- /dev/null +++ b/Modules/ROI/include/mitkROIMapperLocalStorage.h @@ -0,0 +1,45 @@ +/*============================================================================ + +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 mitkROIMapperLocalStorage_h +#define mitkROIMapperLocalStorage_h + +#include + +template +class vtkSmartPointer; + +class vtkPropAssembly; + +namespace mitk +{ + /** \brief Common base class for both 2-d and 3-d %ROI mapper local storages. + */ + class ROIMapperLocalStorage : public Mapper::BaseLocalStorage + { + public: + ROIMapperLocalStorage(); + ~ROIMapperLocalStorage() override; + + vtkPropAssembly* GetPropAssembly() const; + void SetPropAssembly(vtkPropAssembly* propAssembly); + + TimePointType GetLastTimePoint() const; + void SetLastTimePoint(TimePointType timePoint); + + protected: + vtkSmartPointer m_PropAssembly; + TimePointType m_LastTimePoint; + }; +} + +#endif diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp new file mode 100644 index 0000000000..6f5e614b1a --- /dev/null +++ b/Modules/ROI/src/mitkROI.cpp @@ -0,0 +1,399 @@ +/*============================================================================ + +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 + +void mitk::to_json(nlohmann::json& j, const ROI::Element& roi) +{ + j["ID"] = roi.GetID(); + + if (roi.HasTimeSteps()) + { + auto timeSteps = roi.GetTimeSteps(); + + for (const auto t : timeSteps) + { + nlohmann::json jTimeStep = { + { "t", t }, + { "Min", roi.GetMin(t) }, + { "Max", roi.GetMax(t) } + }; + + if (auto* properties = roi.GetProperties(t); properties != nullptr && !properties->IsEmpty()) + properties->ToJSON(jTimeStep["Properties"]); + + j["TimeSteps"].push_back(jTimeStep); + } + } + else + { + j["Min"] = roi.GetMin(); + j["Max"] = roi.GetMax(); + } + + if (auto* properties = roi.GetDefaultProperties(); properties != nullptr && !properties->IsEmpty()) + properties->ToJSON(j["Properties"]); +} + +void mitk::from_json(const nlohmann::json& j, ROI::Element& roi) +{ + auto id = j["ID"].get(); + roi.SetID(id); + + if (j.contains("TimeSteps")) + { + for (const auto& jTimeStep : j["TimeSteps"]) + { + auto t = jTimeStep["t"].get(); + + roi.SetMin(jTimeStep["Min"].get(), t); + roi.SetMax(jTimeStep["Max"].get(), t); + + if (jTimeStep.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(jTimeStep["Properties"]); + roi.SetProperties(properties, t); + } + } + } + else + { + roi.SetMin(j["Min"].get()); + roi.SetMax(j["Max"].get()); + } + + if (j.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(j["Properties"]); + roi.SetDefaultProperties(properties); + } +} + +mitk::ROI::Element::Element() + : Element(0) +{ +} + +mitk::ROI::Element::Element(unsigned int id) + : m_ID(id), + m_DefaultProperties(PropertyList::New()) +{ +} + +unsigned int mitk::ROI::Element::GetID() const +{ + return m_ID; +} + +void mitk::ROI::Element::SetID(unsigned int id) +{ + m_ID = id; +} + +bool mitk::ROI::Element::HasTimeStep(TimeStepType t) const +{ + return m_Min.count(t) != 0 && m_Max.count(t) != 0; +} + +bool mitk::ROI::Element::HasTimeSteps() const +{ + // Check for multiple time steps. + if (m_Min.size() > 1 && m_Max.size() > 1) + return true; + + // Check for single time step that is not 0. + if (m_Min.size() >= 1 && m_Max.size() >= 1) + return m_Min.count(0) == 0 && m_Max.count(0) == 0; + + // Single time step 0. + return false; +} + +std::vector mitk::ROI::Element::GetTimeSteps() const +{ + std::vector result; + result.reserve(m_Min.size()); + + for (const auto& [t, min] : m_Min) + { + if (m_Max.count(t) != 0) + result.push_back(t); + } + + return result; +} + +mitk::Point3D mitk::ROI::Element::GetMin(TimeStepType t) const +{ + return m_Min.at(t); +} + +void mitk::ROI::Element::SetMin(const Point3D& min, TimeStepType t) +{ + m_Min[t] = min; +} + +mitk::Point3D mitk::ROI::Element::GetMax(TimeStepType t) const +{ + return m_Max.at(t); +} + +void mitk::ROI::Element::SetMax(const Point3D& max, TimeStepType t) +{ + m_Max[t] = max; +} + +mitk::PropertyList* mitk::ROI::Element::GetDefaultProperties() const +{ + return m_DefaultProperties; +} + +void mitk::ROI::Element::SetDefaultProperties(PropertyList* properties) +{ + m_DefaultProperties = properties; +} + +mitk::PropertyList* mitk::ROI::Element::GetProperties(TimeStepType t) const +{ + if (m_Properties.count(t) != 0) + return m_Properties.at(t); + + return nullptr; +} + +void mitk::ROI::Element::SetProperties(PropertyList* properties, TimeStepType t) +{ + m_Properties[t] = properties; +} + +mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) const +{ + return !contextName.empty() + ? this->GetConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext) + : m_DefaultProperties->GetConstProperty(propertyKey); +} + +mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) const +{ + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + auto property = it->second->GetConstProperty(propertyKey); + + if (property.IsNotNull()) + return property; + } + + if (!fallBackOnDefaultContext) + return nullptr; + + return m_DefaultProperties->GetConstProperty(propertyKey); +} + +std::vector mitk::ROI::Element::GetPropertyKeys(const std::string& contextName, bool includeDefaultContext) const +{ + return !contextName.empty() + ? this->GetPropertyKeys(std::stoul(contextName), includeDefaultContext) + : m_DefaultProperties->GetPropertyKeys(); +} + +std::vector mitk::ROI::Element::GetPropertyKeys(TimeStepType t, bool includeDefaultContext) const +{ + auto it = m_Properties.find(t); + + std::vector result; + + if (it != m_Properties.end() && it->second.IsNotNull()) + result = it->second->GetPropertyKeys(); + + if (includeDefaultContext) + { + auto keys = m_DefaultProperties->GetPropertyKeys(); + auto end = result.cend(); + + std::remove_copy_if(keys.cbegin(), keys.cend(), std::back_inserter(result), [&, result, end](const std::string& key) { + return end != std::find(result.cbegin(), end, key); + }); + } + + return result; +} + +std::vector mitk::ROI::Element::GetPropertyContextNames() const +{ + std::vector result; + result.reserve(m_Properties.size()); + + std::transform(m_Properties.cbegin(), m_Properties.cend(), std::back_inserter(result), [](const PropertyListsType::value_type& property) { + return std::to_string(property.first); + }); + + return result; +} + +mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) +{ + return !contextName.empty() + ? this->GetNonConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext) + : m_DefaultProperties->GetNonConstProperty(propertyKey); +} + +mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) +{ + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + auto* property = it->second->GetNonConstProperty(propertyKey); + + if (property != nullptr) + return property; + } + + if (!fallBackOnDefaultContext) + return nullptr; + + return m_DefaultProperties->GetNonConstProperty(propertyKey); +} + +void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName, bool fallBackOnDefaultContext) +{ + if (!contextName.empty()) + { + this->SetProperty(propertyKey, property, std::stoul(contextName), fallBackOnDefaultContext); + return; + } + + m_DefaultProperties->SetProperty(propertyKey, property); +} + +void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, TimeStepType t, bool fallBackOnDefaultContext) +{ + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + it->second->SetProperty(propertyKey, property); + return; + } + + if (!fallBackOnDefaultContext) + mitkThrow() << "Time step " << t << " does not exist!"; + + m_DefaultProperties->SetProperty(propertyKey, property); +} + +void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) +{ + if (!contextName.empty()) + { + this->RemoveProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext); + return; + } + + m_DefaultProperties->RemoveProperty(propertyKey); +} + +void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) +{ + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + it->second->RemoveProperty(propertyKey); + return; + } + + if (!fallBackOnDefaultContext) + mitkThrow() << "Time step " << t << " does not exist!"; + + m_DefaultProperties->RemoveProperty(propertyKey); +} + +mitk::ROI::ROI() +{ +} + +mitk::ROI::ROI(const Self& other) + : BaseData(other) +{ +} + +mitk::ROI::~ROI() +{ +} + +size_t mitk::ROI::GetNumberOfElements() const +{ + return m_Elements.size(); +} + +void mitk::ROI::AddElement(const Element& element) +{ + const auto id = element.GetID(); + + if (m_Elements.count(id) != 0) + mitkThrow() << "ROI already contains an element with ID " << std::to_string(id) << '.'; + + m_Elements[id] = element; +} + +const mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id) const +{ + return m_Elements.at(id); +} + +mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id) +{ + return m_Elements.at(id); +} + +mitk::ROI::ConstIterator mitk::ROI::begin() const +{ + return m_Elements.begin(); +} + +mitk::ROI::ConstIterator mitk::ROI::end() const +{ + return m_Elements.end(); +} + +mitk::ROI::Iterator mitk::ROI::begin() +{ + return m_Elements.begin(); +} + +mitk::ROI::Iterator mitk::ROI::end() +{ + return m_Elements.end(); +} + +void mitk::ROI::SetRequestedRegionToLargestPossibleRegion() +{ +} + +bool mitk::ROI::RequestedRegionIsOutsideOfTheBufferedRegion() +{ + return false; +} + +bool mitk::ROI::VerifyRequestedRegion() +{ + return true; +} + +void mitk::ROI::SetRequestedRegion(const itk::DataObject*) +{ +} diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp new file mode 100644 index 0000000000..1435d4c6d7 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -0,0 +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. + +============================================================================*/ + +#include + +#include "mitkROIMapperHelper.h" + +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ + mitk::Point3D GetBottomLeftPoint(vtkPoints* points, mitk::BaseRenderer* renderer) + { + mitk::Point3D point = points->GetPoint(0); + mitk::Point2D bottomLeftDisplayPoint; + renderer->WorldToDisplay(point, bottomLeftDisplayPoint); + + auto numPoints = points->GetNumberOfPoints(); + mitk::Point2D displayPoint; + + for (decltype(numPoints) i = 1; i < numPoints; ++i) + { + point.FillPoint(points->GetPoint(i)); + renderer->WorldToDisplay(point, displayPoint); + + bottomLeftDisplayPoint[0] = std::min(bottomLeftDisplayPoint[0], displayPoint[0]); + bottomLeftDisplayPoint[1] = std::min(bottomLeftDisplayPoint[1], displayPoint[1]); + } + + renderer->DisplayToWorld(bottomLeftDisplayPoint, point); + + return point; + } +} + +mitk::ROIMapper2D::LocalStorage::LocalStorage() +{ +} + +mitk::ROIMapper2D::LocalStorage::~LocalStorage() +{ +} + +const mitk::PlaneGeometry* mitk::ROIMapper2D::LocalStorage::GetLastPlaneGeometry() const +{ + return m_LastPlaneGeometry; +} + +void mitk::ROIMapper2D::LocalStorage::SetLastPlaneGeometry(const PlaneGeometry* planeGeometry) +{ + m_LastPlaneGeometry = planeGeometry; +} + +void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + Superclass::SetDefaultProperties(node, renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); +} + +mitk::ROIMapper2D::ROIMapper2D() +{ +} + +mitk::ROIMapper2D::~ROIMapper2D() +{ +} + +void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep()); + const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + const auto* dataNode = this->GetDataNode(); + + if (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) && + localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() && + localStorage->GetLastTimePoint() == timePoint) + { + return; + } + + localStorage->SetLastPlaneGeometry(planeGeometry->Clone()); + localStorage->SetLastTimePoint(timePoint); + + auto data = static_cast(this->GetData()); + + if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint)) + return; + + const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint); + auto propAssembly = vtkSmartPointer::New(); + + if (dataNode->IsVisible(renderer)) + { + const auto* geometry = data->GetGeometry(t); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + + auto plane = vtkSmartPointer::New(); + plane->SetOrigin(planeGeometry->GetOrigin().data()); + plane->SetNormal(planeGeometry->GetNormal().data()); + + for (const auto& [id, roi] : *data) + { + if (!roi.HasTimeStep(t)) + continue; + + Point3D min; + geometry->IndexToWorld(roi.GetMin(t), min); + min -= halfSpacing; + + Point3D max; + geometry->IndexToWorld(roi.GetMax(t), max); + max += halfSpacing; + + auto cube = vtkSmartPointer::New(); + cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); + + auto cutter = vtkSmartPointer::New(); + cutter->SetInputConnection(cube->GetOutputPort()); + cutter->SetPlane(plane); + cutter->Update(); + + auto* slicePolyData = cutter->GetOutput(); + + if (slicePolyData->GetNumberOfLines() == 0) + continue; + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(cutter->GetOutputPort()); + + auto actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + this->ApplyColorAndOpacityProperties(renderer, actor); + ROIMapperHelper::ApplyIndividualProperties(roi, t, actor); + + propAssembly->AddPart(actor); + + if (std::string caption; dataNode->GetStringProperty("caption", caption, renderer)) + { + caption = ROIMapperHelper::ParseCaption(caption, roi, t); + + if (!caption.empty()) + { + auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); + auto captionActor = ROIMapperHelper::CreateCaptionActor(caption, bottomLeftPoint, actor->GetProperty(), dataNode, renderer); + + propAssembly->AddPart(captionActor); + } + } + } + } + + localStorage->SetPropAssembly(propAssembly); + localStorage->UpdateGenerateDataTime(); +} + +void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + float opacity = 1.0f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); +} + +vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer) +{ + return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); +} diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp new file mode 100644 index 0000000000..e8120de0e3 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -0,0 +1,118 @@ +/*============================================================================ + +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 "mitkROIMapperHelper.h" + +#include +#include +#include +#include + +mitk::ROIMapper3D::LocalStorage::LocalStorage() +{ +} + +mitk::ROIMapper3D::LocalStorage::~LocalStorage() +{ +} + +void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + Superclass::SetDefaultProperties(node, renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); +} + +mitk::ROIMapper3D::ROIMapper3D() +{ +} + +mitk::ROIMapper3D::~ROIMapper3D() +{ +} + +void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep()); + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + const auto* dataNode = this->GetDataNode(); + + if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() && + localStorage->GetLastTimePoint() == timePoint) + { + return; + } + + localStorage->SetLastTimePoint(timePoint); + + auto data = static_cast(this->GetData()); + + if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint)) + return; + + const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint); + auto propAssembly = vtkSmartPointer::New(); + + if (dataNode->IsVisible(renderer)) + { + const auto* geometry = data->GetGeometry(); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + + for (const auto& [id, roi] : *data) + { + if (!roi.HasTimeStep(t)) + continue; + + Point3D min; + geometry->IndexToWorld(roi.GetMin(t), min); + min -= halfSpacing; + + Point3D max; + geometry->IndexToWorld(roi.GetMax(t), max); + max += halfSpacing; + + auto cube = vtkSmartPointer::New(); + cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); + cube->Update(); + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(cube->GetOutputPort()); + + auto actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + this->ApplyColorAndOpacityProperties(renderer, actor); + ROIMapperHelper::ApplyIndividualProperties(roi, t, actor); + + propAssembly->AddPart(actor); + } + } + + localStorage->SetPropAssembly(propAssembly); + localStorage->UpdateGenerateDataTime(); +} + +void mitk::ROIMapper3D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + float opacity = 1.0f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); +} + +vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer) +{ + return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); +} diff --git a/Modules/ROI/src/mitkROIMapperHelper.cpp b/Modules/ROI/src/mitkROIMapperHelper.cpp new file mode 100644 index 0000000000..68dc54c545 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.cpp @@ -0,0 +1,130 @@ +/*============================================================================ + +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 "mitkROIMapperHelper.h" + +#include +#include + +#include + +#include + +void mitk::ROIMapperHelper::ApplyIndividualProperties(const ROI::Element& roi, TimeStepType t, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + property->SetRepresentationToWireframe(); + property->LightingOff(); + + if (auto colorProperty = GetConstProperty("color", roi, t); colorProperty != nullptr) + { + const auto color = colorProperty->GetColor(); + property->SetColor(color[0], color[1], color[2]); + } + + if (auto opacityProperty = GetConstProperty("opacity", roi, t); opacityProperty != nullptr) + { + const auto opacity = opacityProperty->GetValue(); + property->SetOpacity(property->GetOpacity() * opacity); + } + + if (auto lineWidthProperty = GetConstProperty("lineWidth", roi, t); lineWidthProperty != nullptr) + { + const auto lineWidth = lineWidthProperty->GetValue(); + property->SetLineWidth(lineWidth); + } +} + +vtkSmartPointer mitk::ROIMapperHelper::CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer) +{ + auto captionActor = vtkSmartPointer::New(); + captionActor->SetPosition(property->GetLineWidth() * 0.5, property->GetLineWidth() * 0.5); + captionActor->GetTextActor()->SetTextScaleModeToNone(); + captionActor->SetAttachmentPoint(attachmentPoint[0], attachmentPoint[1], attachmentPoint[2]); + captionActor->SetCaption(caption.c_str()); + captionActor->BorderOff(); + captionActor->LeaderOff(); + + auto* textProperty = captionActor->GetCaptionTextProperty(); + textProperty->SetColor(property->GetColor()); + textProperty->SetOpacity(property->GetOpacity()); + textProperty->ShadowOff(); + + int fontSize = 16; + dataNode->GetIntProperty("font.size", fontSize, renderer); + textProperty->SetFontSize(fontSize); + + bool bold = false; + dataNode->GetBoolProperty("font.bold", bold, renderer); + textProperty->SetBold(bold); + + bool italic = false; + dataNode->GetBoolProperty("font.italic", italic, renderer); + textProperty->SetItalic(italic); + + return captionActor; +} + +std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTemplate, const ROI::Element& roi, TimeStepType t) +{ + std::regex regex(R"(\{([^}]*)\})"); // Anything between curly braces (considered as placeholder). + + auto start = captionTemplate.cbegin(); + bool hasPlaceholders = false; + std::string caption; + std::smatch match; + + // Iterate through the caption template and substitute all + // placeholders with corresponding data or property values. + + while (std::regex_search(start, captionTemplate.cend(), match, regex)) + { + hasPlaceholders = true; + + caption.append(match.prefix().first, match.prefix().second); + + if (match[1] == "ID") + { + caption.append(std::to_string(roi.GetID())); + } + else + { + auto property = roi.GetConstProperty(match[1], t); + + if (property.IsNotNull()) + caption.append(property->GetValueAsString()); + } + + start = match.suffix().first; + } + + if (match.suffix().matched) + caption.append(match.suffix().first, match.suffix().second); + + if (hasPlaceholders) + { + boost::trim(caption); + return caption; + } + + return captionTemplate; +} + +void mitk::ROIMapperHelper::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + node->AddProperty("opacity", FloatProperty::New(1.0f), renderer, override); + node->AddProperty("font.bold", BoolProperty::New(false), renderer, override); + node->AddProperty("font.italic", BoolProperty::New(false), renderer, override); + node->AddProperty("font.size", IntProperty::New(16), renderer, override); + node->AddProperty("caption", StringProperty::New("{ID}\n{name}"), renderer, override); +} diff --git a/Modules/ROI/src/mitkROIMapperHelper.h b/Modules/ROI/src/mitkROIMapperHelper.h new file mode 100644 index 0000000000..edab2ad05d --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -0,0 +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 mitkROIMapperHelper_h +#define mitkROIMapperHelper_h + +#include +#include + +#include +#include +#include + +namespace mitk +{ + namespace ROIMapperHelper + { + /** \brief Apply %ROI properties at a certain time step to the given actor. + */ + void ApplyIndividualProperties(const ROI::Element& roi, TimeStepType t, vtkActor* actor); + + /** \brief Create an actor for the %ROI caption located at a certain attachment point considering several properties. + */ + vtkSmartPointer CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer); + + /** \brief Substitute all placeholders in a caption with corresponding property values. + * + * \sa ROI + */ + std::string ParseCaption(const std::string& captionTemplate, const ROI::Element& roi, TimeStepType t = 0); + + /** \brief Set common default properties for both 2-d and 3-d %ROI mappers. + */ + void SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override); + + /** \brief Syntactic sugar for getting %ROI properties. + */ + template + const T* GetConstProperty(const std::string& propertyKey, const ROI::Element& roi, TimeStepType t) + { + auto property = roi.GetConstProperty(propertyKey, t); + + if (property.IsNotNull()) + return dynamic_cast(property.GetPointer()); + + return nullptr; + } + } +} + +#endif diff --git a/Modules/ROI/src/mitkROIMapperLocalStorage.cpp b/Modules/ROI/src/mitkROIMapperLocalStorage.cpp new file mode 100644 index 0000000000..4c57abe8df --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperLocalStorage.cpp @@ -0,0 +1,46 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +#include +#include + +mitk::ROIMapperLocalStorage::ROIMapperLocalStorage() + : m_PropAssembly(vtkSmartPointer::New()), + m_LastTimePoint(0.0) +{ +} + +mitk::ROIMapperLocalStorage::~ROIMapperLocalStorage() +{ +} + +vtkPropAssembly* mitk::ROIMapperLocalStorage::GetPropAssembly() const +{ + return m_PropAssembly; +} + +void mitk::ROIMapperLocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) +{ + m_PropAssembly = propAssembly; +} + +mitk::TimePointType mitk::ROIMapperLocalStorage::GetLastTimePoint() const +{ + return m_LastTimePoint; +} + +void mitk::ROIMapperLocalStorage::SetLastTimePoint(TimePointType timePoint) +{ + m_LastTimePoint = timePoint; +} diff --git a/Modules/ROI/src/mitkROIObjectFactory.cpp b/Modules/ROI/src/mitkROIObjectFactory.cpp new file mode 100644 index 0000000000..cffe1fca6d --- /dev/null +++ b/Modules/ROI/src/mitkROIObjectFactory.cpp @@ -0,0 +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. + +============================================================================*/ + +#include "mitkROIObjectFactory.h" +#include +#include +#include +#include + +mitk::ROIObjectFactory::ROIObjectFactory() +{ +} + +mitk::ROIObjectFactory::~ROIObjectFactory() +{ +} + +mitk::Mapper::Pointer mitk::ROIObjectFactory::CreateMapper(DataNode* node, MapperSlotId slotId) +{ + Mapper::Pointer mapper; + + if (node != nullptr) + { + auto* roi = dynamic_cast(node->GetData()); + + if (roi != nullptr) + { + if (slotId == BaseRenderer::Standard2D) + { + mapper = ROIMapper2D::New(); + } + else if (slotId == BaseRenderer::Standard3D) + { + mapper = ROIMapper3D::New(); + } + + if (mapper.IsNotNull()) + mapper->SetDataNode(node); + } + } + + return mapper; +} + +void mitk::ROIObjectFactory::SetDefaultProperties(DataNode* node) +{ + if (node == nullptr) + return; + + auto* roi = dynamic_cast(node->GetData()); + + if (roi == nullptr) + return; + + ROIMapper2D::SetDefaultProperties(node); + ROIMapper3D::SetDefaultProperties(node); +} + +std::string mitk::ROIObjectFactory::GetFileExtensions() +{ + return ""; +} + +mitk::ROIObjectFactory::MultimapType mitk::ROIObjectFactory::GetFileExtensionsMap() +{ + return {}; +} + +std::string mitk::ROIObjectFactory::GetSaveFileExtensions() +{ + return ""; +} + +mitk::ROIObjectFactory::MultimapType mitk::ROIObjectFactory::GetSaveFileExtensionsMap() +{ + return {}; +} + +namespace mitk +{ + class RegisterROIObjectFactory + { + public: + RegisterROIObjectFactory() + : m_Factory(ROIObjectFactory::New()) + { + CoreObjectFactory::GetInstance()->RegisterExtraFactory(m_Factory); + } + + ~RegisterROIObjectFactory() + { + } + + private: + ROIObjectFactory::Pointer m_Factory; + }; +} + +static mitk::RegisterROIObjectFactory registerROIObjectFactory; diff --git a/Modules/ROI/src/mitkROIObjectFactory.h b/Modules/ROI/src/mitkROIObjectFactory.h new file mode 100644 index 0000000000..75ac43fc92 --- /dev/null +++ b/Modules/ROI/src/mitkROIObjectFactory.h @@ -0,0 +1,39 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkROIObjectFactory_h +#define mitkROIObjectFactory_h + +#include + +namespace mitk +{ + class ROIObjectFactory : public CoreObjectFactoryBase + { + public: + mitkClassMacro(ROIObjectFactory, CoreObjectFactoryBase) + itkFactorylessNewMacro(Self) + + Mapper::Pointer CreateMapper(DataNode* node, MapperSlotId slotId) override; + void SetDefaultProperties(DataNode *node) override; + std::string GetFileExtensions() override; + MultimapType GetFileExtensionsMap() override; + std::string GetSaveFileExtensions() override; + MultimapType GetSaveFileExtensionsMap() override; + + protected: + ROIObjectFactory(); + ~ROIObjectFactory() override; + }; +} + +#endif diff --git a/Modules/RT/include/mitkIsoDoseLevelSetProperty.h b/Modules/RT/include/mitkIsoDoseLevelSetProperty.h index 32aa056beb..84bce165a9 100644 --- a/Modules/RT/include/mitkIsoDoseLevelSetProperty.h +++ b/Modules/RT/include/mitkIsoDoseLevelSetProperty.h @@ -1,73 +1,76 @@ /*============================================================================ 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 mitkIsoDoseLevelSetProperty_h #define mitkIsoDoseLevelSetProperty_h #include "mitkBaseProperty.h" #include "mitkIsoDoseLevelCollections.h" #include "MitkRTExports.h" namespace mitk { /** \brief Property class for dose iso level sets. */ class MITKRT_EXPORT IsoDoseLevelSetProperty : public BaseProperty { protected: IsoDoseLevelSet::Pointer m_IsoLevelSet; IsoDoseLevelSetProperty(); explicit IsoDoseLevelSetProperty(const IsoDoseLevelSetProperty& other); explicit IsoDoseLevelSetProperty(IsoDoseLevelSet* levelSet); public: mitkClassMacro(IsoDoseLevelSetProperty, BaseProperty); itkNewMacro(IsoDoseLevelSetProperty); mitkNewMacro1Param(IsoDoseLevelSetProperty, IsoDoseLevelSet*); typedef IsoDoseLevelSet ValueType; ~IsoDoseLevelSetProperty() override; const IsoDoseLevelSet * GetIsoDoseLevelSet() const; const IsoDoseLevelSet * GetValue() const; IsoDoseLevelSet * GetIsoDoseLevelSet(); IsoDoseLevelSet * GetValue(); void SetIsoDoseLevelSet(IsoDoseLevelSet* levelSet); void SetValue(IsoDoseLevelSet* levelSet); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty& property) const override; bool Assign(const BaseProperty& property) override; }; } // namespace mitk #endif diff --git a/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h b/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h index eadc300ac4..5887234c37 100644 --- a/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h +++ b/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h @@ -1,74 +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 mitkIsoDoseLevelVectorProperty_h #define mitkIsoDoseLevelVectorProperty_h #include "mitkBaseProperty.h" #include "mitkIsoDoseLevelCollections.h" #include "MitkRTExports.h" namespace mitk { /** \brief Property class for dose iso level vector. */ class MITKRT_EXPORT IsoDoseLevelVectorProperty : public BaseProperty { protected: IsoDoseLevelVector::Pointer m_IsoLevelVector; IsoDoseLevelVectorProperty(); explicit IsoDoseLevelVectorProperty(const IsoDoseLevelVectorProperty& other); explicit IsoDoseLevelVectorProperty(IsoDoseLevelVector* levelVector); public: mitkClassMacro(IsoDoseLevelVectorProperty, BaseProperty); itkNewMacro(IsoDoseLevelVectorProperty); mitkNewMacro1Param(IsoDoseLevelVectorProperty, IsoDoseLevelVector*); typedef IsoDoseLevelVector ValueType; ~IsoDoseLevelVectorProperty() override; const IsoDoseLevelVector * GetIsoDoseLevelVector() const; const IsoDoseLevelVector * GetValue() const; IsoDoseLevelVector * GetIsoDoseLevelVector(); IsoDoseLevelVector * GetValue(); void SetIsoDoseLevelVector(IsoDoseLevelVector* levelVector); void SetValue(IsoDoseLevelVector* levelVector); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty& property) const override; bool Assign(const BaseProperty& property) override; }; } // namespace mitk #endif diff --git a/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp b/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp index aa15834838..35b321b3ab 100644 --- a/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp +++ b/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp @@ -1,103 +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 "mitkIsoDoseLevelSetProperty.h" mitk::IsoDoseLevelSetProperty::IsoDoseLevelSetProperty() { } mitk::IsoDoseLevelSetProperty::IsoDoseLevelSetProperty(const mitk::IsoDoseLevelSetProperty& other) : BaseProperty(other) , m_IsoLevelSet(other.m_IsoLevelSet) { } mitk::IsoDoseLevelSetProperty::IsoDoseLevelSetProperty(IsoDoseLevelSet* levelSet) : m_IsoLevelSet(levelSet) { } mitk::IsoDoseLevelSetProperty::~IsoDoseLevelSetProperty() { } bool mitk::IsoDoseLevelSetProperty::IsEqual(const BaseProperty& property) const { return this->m_IsoLevelSet == static_cast(property).m_IsoLevelSet; } bool mitk::IsoDoseLevelSetProperty::Assign(const BaseProperty& property) { this->m_IsoLevelSet = static_cast(property).m_IsoLevelSet; return true; } const mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetIsoDoseLevelSet() const { return m_IsoLevelSet; } const mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetValue() const { return GetIsoDoseLevelSet(); } mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetIsoDoseLevelSet() { return m_IsoLevelSet; } mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetValue() { return GetIsoDoseLevelSet(); } void mitk::IsoDoseLevelSetProperty::SetIsoDoseLevelSet(IsoDoseLevelSet* levelSet) { if(m_IsoLevelSet != levelSet) { m_IsoLevelSet = levelSet; Modified(); } } void mitk::IsoDoseLevelSetProperty::SetValue(IsoDoseLevelSet* levelSet) { SetIsoDoseLevelSet(levelSet); } std::string mitk::IsoDoseLevelSetProperty::GetValueAsString() const { std::stringstream myStr; myStr << "IsoDoseLevels: "; if (m_IsoLevelSet.IsNotNull()) { myStr << m_IsoLevelSet->Size() << std::endl; for (IsoDoseLevelSet::ConstIterator pos = m_IsoLevelSet->Begin(); pos != m_IsoLevelSet->End(); ++pos) { myStr << " " << 100*(pos->GetDoseValue()) << "% : ("<GetColor()<< "); iso line: " << pos->GetVisibleIsoLine() << "; color wash: "<GetVisibleColorWash() << std::endl; } } return myStr.str(); } +bool mitk::IsoDoseLevelSetProperty::ToJSON(nlohmann::json&) const +{ + return false; // Not implemented +} + +bool mitk::IsoDoseLevelSetProperty::FromJSON(const nlohmann::json&) +{ + return false; // Not implemented +} + itk::LightObject::Pointer mitk::IsoDoseLevelSetProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); return result; } diff --git a/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp b/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp index 0182f7c135..808acfa397 100644 --- a/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp +++ b/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp @@ -1,103 +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 "mitkIsoDoseLevelVectorProperty.h" mitk::IsoDoseLevelVectorProperty::IsoDoseLevelVectorProperty() { } mitk::IsoDoseLevelVectorProperty::IsoDoseLevelVectorProperty(const mitk::IsoDoseLevelVectorProperty& other) : BaseProperty(other) , m_IsoLevelVector(other.m_IsoLevelVector) { } mitk::IsoDoseLevelVectorProperty::IsoDoseLevelVectorProperty(IsoDoseLevelVector* levelVector) : m_IsoLevelVector(levelVector) { } mitk::IsoDoseLevelVectorProperty::~IsoDoseLevelVectorProperty() { } bool mitk::IsoDoseLevelVectorProperty::IsEqual(const BaseProperty& property) const { return this->m_IsoLevelVector == static_cast(property).m_IsoLevelVector; } bool mitk::IsoDoseLevelVectorProperty::Assign(const BaseProperty& property) { this->m_IsoLevelVector = static_cast(property).m_IsoLevelVector; return true; } const mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetIsoDoseLevelVector() const { return m_IsoLevelVector; } const mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetValue() const { return GetIsoDoseLevelVector(); } mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetIsoDoseLevelVector() { return m_IsoLevelVector; } mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetValue() { return GetIsoDoseLevelVector(); } void mitk::IsoDoseLevelVectorProperty::SetIsoDoseLevelVector(IsoDoseLevelVector* levelVector) { if(m_IsoLevelVector != levelVector) { m_IsoLevelVector = levelVector; Modified(); } } void mitk::IsoDoseLevelVectorProperty::SetValue(IsoDoseLevelVector* levelVector) { SetIsoDoseLevelVector(levelVector); } std::string mitk::IsoDoseLevelVectorProperty::GetValueAsString() const { std::stringstream myStr; myStr << "IsoDoseLevels: "; if (m_IsoLevelVector.IsNotNull()) { myStr << m_IsoLevelVector->Size() << std::endl; for (IsoDoseLevelVector::ConstIterator pos = m_IsoLevelVector->Begin(); pos != m_IsoLevelVector->End(); ++pos) { myStr << " " << 100*(pos->Value()->GetDoseValue()) << "% : ("<Value()->GetColor()<< "); iso line: " << pos->Value()->GetVisibleIsoLine() << std::endl; } } return myStr.str(); } +bool mitk::IsoDoseLevelVectorProperty::ToJSON(nlohmann::json&) const +{ + return false; // Not implemented +} + +bool mitk::IsoDoseLevelVectorProperty::FromJSON(const nlohmann::json&) +{ + return false; // Not implemented +} + itk::LightObject::Pointer mitk::IsoDoseLevelVectorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); return result; } diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp index c96aa37a3e..6072a7bc3f 100644 --- a/Modules/SceneSerialization/src/mitkSceneIO.cpp +++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp @@ -1,577 +1,578 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include "mitkBaseDataSerializer.h" #include "mitkPropertyListSerializer.h" #include "mitkSceneIO.h" #include "mitkSceneReader.h" #include "mitkBaseRenderer.h" #include "mitkProgressBar.h" #include "mitkRenderingManager.h" #include "mitkStandaloneDataStorage.h" #include #include +#include #include #include #include #include #include "itksys/SystemTools.hxx" #include mitk::SceneIO::SceneIO() : m_WorkingDirectory(""), m_UnzipErrors(0) { } mitk::SceneIO::~SceneIO() { } std::string mitk::SceneIO::CreateEmptyTempDirectory() { mitk::UIDGenerator uidGen; // std::string returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + // Poco::Path::separator() + "SceneIOTemp" + uidGen.GetUID(); std::string returnValue = Poco::Path::temp() + "SceneIOTemp" + uidGen.GetUID(); std::string uniquename = returnValue + Poco::Path::separator(); Poco::File tempdir(uniquename); try { bool existsNot = tempdir.createDirectory(); if (!existsNot) { MITK_ERROR << "Warning: Directory already exitsts: " << uniquename << " (choosing another)"; returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + Poco::Path::separator() + "SceneIOTempDirectory" + uidGen.GetUID(); uniquename = returnValue + Poco::Path::separator(); Poco::File tempdir2(uniquename); if (!tempdir2.createDirectory()) { MITK_ERROR << "Warning: Second directory also already exitsts: " << uniquename; } } } catch (std::exception &e) { MITK_ERROR << "Could not create temporary directory " << uniquename << ":" << e.what(); return ""; } return returnValue; } mitk::DataStorage::Pointer mitk::SceneIO::LoadScene(const std::string &filename, DataStorage *pStorage, bool clearStorageFirst) { mitk::LocaleSwitch localeSwitch("C"); // prepare data storage DataStorage::Pointer storage = pStorage; if (storage.IsNull()) { storage = StandaloneDataStorage::New().GetPointer(); } // test input filename if (filename.empty()) { MITK_ERROR << "No filename given. Not possible to load scene."; return storage; } // test if filename can be read std::ifstream file(filename.c_str(), std::ios::binary); if (!file.good()) { MITK_ERROR << "Cannot open '" << filename << "' for reading"; return storage; } // get new temporary directory m_WorkingDirectory = CreateEmptyTempDirectory(); if (m_WorkingDirectory.empty()) { MITK_ERROR << "Could not create temporary directory. Cannot open scene files."; return storage; } // unzip all filenames contents to temp dir m_UnzipErrors = 0; Poco::Zip::Decompress unzipper(file, Poco::Path(m_WorkingDirectory)); unzipper.EError += Poco::Delegate>( this, &SceneIO::OnUnzipError); unzipper.EOk += Poco::Delegate>( this, &SceneIO::OnUnzipOk); unzipper.decompressAllFiles(); unzipper.EError -= Poco::Delegate>( this, &SceneIO::OnUnzipError); unzipper.EOk -= Poco::Delegate>( this, &SceneIO::OnUnzipOk); if (m_UnzipErrors) { MITK_ERROR << "There were " << m_UnzipErrors << " errors unzipping '" << filename << "'. Will attempt to read whatever could be unzipped."; } // transcode locale-dependent string m_WorkingDirectory = Poco::Path::transcode (m_WorkingDirectory); auto indexFile = m_WorkingDirectory + mitk::IOUtil::GetDirectorySeparator() + "index.xml"; storage = LoadSceneUnzipped(indexFile, storage, clearStorageFirst); // delete temp directory try { Poco::File deleteDir(m_WorkingDirectory); deleteDir.remove(true); // recursive } catch (...) { MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory; } // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method) return storage; } mitk::DataStorage::Pointer mitk::SceneIO::LoadSceneUnzipped(const std::string &indexfilename, DataStorage *pStorage, bool clearStorageFirst) { mitk::LocaleSwitch localeSwitch("C"); // prepare data storage DataStorage::Pointer storage = pStorage; if (storage.IsNull()) { storage = StandaloneDataStorage::New().GetPointer(); } if (clearStorageFirst) { try { storage->Remove(storage->GetAll()); } catch (...) { MITK_ERROR << "DataStorage cannot be cleared properly."; } } // test input filename if (indexfilename.empty()) { MITK_ERROR << "No filename given. Not possible to load scene."; return storage; } // transcode locale-dependent string std::string tempfilename; std::string workingDir; itksys::SystemTools::SplitProgramPath(indexfilename, workingDir, tempfilename); // test if index.xml exists // parse index.xml with TinyXML tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS != document.LoadFile(indexfilename.c_str())) { MITK_ERROR << "Could not open/read/parse " << workingDir << mitk::IOUtil::GetDirectorySeparator() << "index.xml\nTinyXML reports: " << document.ErrorStr() << std::endl; return storage; } SceneReader::Pointer reader = SceneReader::New(); if (!reader->LoadScene(document, workingDir, storage)) { MITK_ERROR << "There were errors while loading scene file " << indexfilename << ". Your data may be corrupted"; } // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method) return storage; } bool mitk::SceneIO::SaveScene(DataStorage::SetOfObjects::ConstPointer sceneNodes, const DataStorage *storage, const std::string &filename) { if (!sceneNodes) { MITK_ERROR << "No set of nodes given. Not possible to save scene."; return false; } if (!storage) { MITK_ERROR << "No data storage given. Not possible to save scene."; // \TODO: Technically, it would be possible to // save the nodes without their relation return false; } if (filename.empty()) { MITK_ERROR << "No filename given. Not possible to save scene."; return false; } mitk::LocaleSwitch localeSwitch("C"); try { m_FailedNodes = DataStorage::SetOfObjects::New(); m_FailedProperties = PropertyList::New(); // start XML DOM tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); auto *version = document.NewElement("Version"); version->SetAttribute("Writer", __FILE__); version->SetAttribute("Revision", "$Revision: 17055 $"); version->SetAttribute("FileVersion", 1); document.InsertEndChild(version); // DataStorage::SetOfObjects::ConstPointer sceneNodes = storage->GetSubset( predicate ); if (sceneNodes.IsNull()) { MITK_WARN << "Saving empty scene to " << filename; } else { if (sceneNodes->size() == 0) { MITK_WARN << "Saving empty scene to " << filename; } MITK_INFO << "Storing scene with " << sceneNodes->size() << " objects to " << filename; m_WorkingDirectory = CreateEmptyTempDirectory(); if (m_WorkingDirectory.empty()) { MITK_ERROR << "Could not create temporary directory. Cannot create scene files."; return false; } ProgressBar::GetInstance()->AddStepsToDo(sceneNodes->size()); // find out about dependencies typedef std::map UIDMapType; typedef std::map> SourcesMapType; UIDMapType nodeUIDs; // for dependencies: ID of each node SourcesMapType sourceUIDs; // for dependencies: IDs of a node's parent nodes UIDGenerator nodeUIDGen("OBJECT_"); for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter) { DataNode *node = iter->GetPointer(); if (!node) continue; // unlikely event that we get a nullptr pointer as an object for saving. just ignore // generate UIDs for all source objects DataStorage::SetOfObjects::ConstPointer sourceObjects = storage->GetSources(node); for (auto sourceIter = sourceObjects->begin(); sourceIter != sourceObjects->end(); ++sourceIter) { if (std::find(sceneNodes->begin(), sceneNodes->end(), *sourceIter) == sceneNodes->end()) continue; // source is not saved, so don't generate a UID for this source // create a uid for the parent object if (nodeUIDs[*sourceIter].empty()) { nodeUIDs[*sourceIter] = nodeUIDGen.GetUID(); } // store this dependency for writing sourceUIDs[node].push_back(nodeUIDs[*sourceIter]); } if (nodeUIDs[node].empty()) { nodeUIDs[node] = nodeUIDGen.GetUID(); } } // write out objects, dependencies and properties for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter) { DataNode *node = iter->GetPointer(); if (node) { auto *nodeElement = document.NewElement("node"); std::string filenameHint(node->GetName()); filenameHint = itksys::SystemTools::MakeCindentifier( filenameHint.c_str()); // escape filename <-- only allow [A-Za-z0-9_], replace everything else with _ // store dependencies auto searchUIDIter = nodeUIDs.find(node); if (searchUIDIter != nodeUIDs.end()) { // store this node's ID nodeElement->SetAttribute("UID", searchUIDIter->second.c_str()); } auto searchSourcesIter = sourceUIDs.find(node); if (searchSourcesIter != sourceUIDs.end()) { // store all source IDs for (auto sourceUIDIter = searchSourcesIter->second.begin(); sourceUIDIter != searchSourcesIter->second.end(); ++sourceUIDIter) { auto *uidElement = document.NewElement("source"); uidElement->SetAttribute("UID", sourceUIDIter->c_str()); nodeElement->InsertEndChild(uidElement); } } // store basedata if (BaseData *data = node->GetData()) { // std::string filenameHint( node->GetName() ); bool error(false); auto *dataElement = SaveBaseData(document, data, filenameHint, error); // returns a reference to a file if (error) { m_FailedNodes->push_back(node); } // store basedata properties PropertyList *propertyList = data->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { auto *baseDataPropertiesElement = SavePropertyList(document, propertyList, filenameHint + "-data"); // returns a reference to a file dataElement->InsertEndChild(baseDataPropertiesElement); } nodeElement->InsertEndChild(dataElement); } // store all renderwindow specific propertylists mitk::DataNode::PropertyListKeyNames propertyListKeys = node->GetPropertyListNames(); for (const auto &renderWindowName : propertyListKeys) { PropertyList *propertyList = node->GetPropertyList(renderWindowName); if (propertyList && !propertyList->IsEmpty()) { auto *renderWindowPropertiesElement = SavePropertyList(document, propertyList, filenameHint + "-" + renderWindowName); // returns a reference to a file renderWindowPropertiesElement->SetAttribute("renderwindow", renderWindowName.c_str()); nodeElement->InsertEndChild(renderWindowPropertiesElement); } } // don't forget the renderwindow independent list PropertyList *propertyList = node->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { auto *propertiesElement = SavePropertyList(document, propertyList, filenameHint + "-node"); // returns a reference to a file nodeElement->InsertEndChild(propertiesElement); } document.InsertEndChild(nodeElement); } else { MITK_WARN << "Ignoring nullptr node during scene serialization."; } ProgressBar::GetInstance()->Progress(); } // end for all nodes } // end if sceneNodes std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); auto xmlFilename = defaultLocale_WorkingDirectory + Poco::Path::separator() + "index.xml"; if (tinyxml2::XML_SUCCESS != document.SaveFile(xmlFilename.c_str())) { MITK_ERROR << "Could not write scene to " << defaultLocale_WorkingDirectory << Poco::Path::separator() << "index.xml" << "\nTinyXML reports '" << document.ErrorStr() << "'"; return false; } else { try { Poco::File deleteFile(filename.c_str()); if (deleteFile.exists()) { deleteFile.remove(); } // create zip at filename std::ofstream file(filename.c_str(), std::ios::binary | std::ios::out); if (!file.good()) { MITK_ERROR << "Could not open a zip file for writing: '" << filename << "'"; return false; } else { Poco::Zip::Compress zipper(file, true); Poco::Path tmpdir(m_WorkingDirectory); zipper.addRecursive(tmpdir); zipper.close(); } try { Poco::File deleteDir(m_WorkingDirectory); deleteDir.remove(true); // recursive } catch (...) { MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory; return false; // ok? } } catch (std::exception &e) { MITK_ERROR << "Could not create ZIP file from " << m_WorkingDirectory << "\nReason: " << e.what(); return false; } return true; } } catch (std::exception &e) { MITK_ERROR << "Caught exception during saving temporary files to disk. Error description: '" << e.what() << "'"; return false; } } tinyxml2::XMLElement *mitk::SceneIO::SaveBaseData(tinyxml2::XMLDocument &doc, BaseData *data, const std::string &filenamehint, bool &error) { assert(data); error = true; // find correct serializer // the serializer must // - create a file containing all information to recreate the BaseData object --> needs to know where to put this // file (and a filename?) // - TODO what to do about writers that creates one file per timestep? auto *element = doc.NewElement("data"); element->SetAttribute("type", data->GetNameOfClass()); // construct name of serializer class std::string serializername(data->GetNameOfClass()); serializername += "Serializer"; std::list thingsThatCanSerializeThis = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (thingsThatCanSerializeThis.size() < 1) { MITK_ERROR << "No serializer found for " << data->GetNameOfClass() << ". Skipping object"; } for (auto iter = thingsThatCanSerializeThis.begin(); iter != thingsThatCanSerializeThis.end(); ++iter) { if (auto *serializer = dynamic_cast(iter->GetPointer())) { serializer->SetData(data); serializer->SetFilenameHint(filenamehint); std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory); try { std::string writtenfilename = serializer->Serialize(); element->SetAttribute("file", writtenfilename.c_str()); if (!writtenfilename.empty()) error = false; } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } element->SetAttribute("UID", data->GetUID().c_str()); return element; } tinyxml2::XMLElement *mitk::SceneIO::SavePropertyList(tinyxml2::XMLDocument &doc, PropertyList *propertyList, const std::string &filenamehint) { assert(propertyList); // - TODO what to do about shared properties (same object in two lists or behind several keys)? auto *element = doc.NewElement("properties"); // construct name of serializer class PropertyListSerializer::Pointer serializer = PropertyListSerializer::New(); serializer->SetPropertyList(propertyList); serializer->SetFilenameHint(filenamehint); std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory); try { std::string writtenfilename = serializer->Serialize(); element->SetAttribute("file", writtenfilename.c_str()); PropertyList::Pointer failedProperties = serializer->GetFailedProperties(); if (failedProperties.IsNotNull()) { // move failed properties to global list m_FailedProperties->ConcatenatePropertyList(failedProperties, true); } } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } return element; } const mitk::SceneIO::FailedBaseDataListType *mitk::SceneIO::GetFailedNodes() { return m_FailedNodes.GetPointer(); } const mitk::PropertyList *mitk::SceneIO::GetFailedProperties() { return m_FailedProperties; } void mitk::SceneIO::OnUnzipError(const void * /*pSender*/, std::pair &info) { ++m_UnzipErrors; MITK_ERROR << "Error while unzipping: " << info.second; } void mitk::SceneIO::OnUnzipOk(const void * /*pSender*/, std::pair & /*info*/) { // MITK_INFO << "Unzipped ok: " << info.second.toString(); } diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.h b/Modules/SceneSerialization/src/mitkSceneReaderV1.h index ba69c6081e..2c370b1ad8 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.h +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.h @@ -1,76 +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 mitkSceneReaderV1_h #define mitkSceneReaderV1_h -#include "mitkSceneReader.h" +#include +#include namespace tinyxml2 { class XMLElement; } namespace mitk { class SceneReaderV1 : public SceneReader { public: mitkClassMacro(SceneReaderV1, SceneReader); itkFactorylessNewMacro(Self); itkCloneMacro(Self); bool LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage) override; protected: /** \brief tries to create one DataNode from a given XML \ element */ DataNode::Pointer LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement, const PropertyList *properties, const std::string &workingDirectory, bool &error); /** \brief reads all the properties from the XML document and recreates them in node */ bool DecorateNodeWithProperties(DataNode *node, const tinyxml2::XMLElement *nodeElement, const std::string &workingDirectory); /** \brief Clear a default property list and handle some exceptions. Called after assigning a BaseData object to a fresh DataNode via SetData(). This call to SetData() would create default properties that have not been there when saving the scene. Since they can produce problems, we clear the list and use only those properties that we read from the scene file. This method also handles some exceptions for backwards compatibility. Those exceptions are documented directly in the code of the method. */ void ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList); typedef std::pair> NodesAndParentsPair; typedef std::list OrderedNodesList; typedef std::map IDToNodeMappingType; typedef std::map NodeToIDMappingType; OrderedNodesList m_OrderedNodePairs; IDToNodeMappingType m_NodeForID; NodeToIDMappingType m_IDForNode; UIDGenerator m_UIDGen; }; } #endif diff --git a/Modules/TubeGraph/include/mitkTubeGraphProperty.h b/Modules/TubeGraph/include/mitkTubeGraphProperty.h index 87afa04b89..95fc651efe 100644 --- a/Modules/TubeGraph/include/mitkTubeGraphProperty.h +++ b/Modules/TubeGraph/include/mitkTubeGraphProperty.h @@ -1,146 +1,149 @@ /*============================================================================ 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 mitkTubeGraphProperty_h #define mitkTubeGraphProperty_h #include #include "mitkTubeGraph.h" #include #include #include #include #include namespace mitk { /** * \brief Property for tube graphs */ class MITKTUBEGRAPH_EXPORT TubeGraphProperty : public BaseProperty { public: mitkClassMacro(TubeGraphProperty, BaseProperty); itkNewMacro(TubeGraphProperty); struct LabelGroup { struct Label { std::string labelName; bool isVisible; Color labelColor; }; std::string labelGroupName; std::vector